> = {
+ size: '20',
+ hasBorder: true,
+};
+
+const CustomIcon = (props: TCustomIconProps) => (
+
+ {typeof props.icon === 'string' ? (
+
+ ) : (
+ cloneElement(props.icon)
+ )}
+
+);
+
+CustomIcon.displayName = 'CustomIcon';
+CustomIcon.defaultProps = defaultProps;
+
+export default CustomIcon;
diff --git a/packages/components/icons/src/custom-icon/export-types.ts b/packages/components/icons/src/custom-icon/export-types.ts
new file mode 100644
index 0000000000..d2456cb219
--- /dev/null
+++ b/packages/components/icons/src/custom-icon/export-types.ts
@@ -0,0 +1 @@
+export type { TCustomIconProps } from './custom-icon';
diff --git a/packages/components/icons/src/custom-icon/index.ts b/packages/components/icons/src/custom-icon/index.ts
new file mode 100644
index 0000000000..ec7997b088
--- /dev/null
+++ b/packages/components/icons/src/custom-icon/index.ts
@@ -0,0 +1,3 @@
+export { default } from './custom-icon';
+
+export * from './export-types';
diff --git a/packages/components/icons/src/fixtures/CustomIconReact.tsx b/packages/components/icons/src/fixtures/CustomIconReact.tsx
new file mode 100644
index 0000000000..39bd7661b5
--- /dev/null
+++ b/packages/components/icons/src/fixtures/CustomIconReact.tsx
@@ -0,0 +1,30 @@
+const SvgCustomIcon = () => (
+
+);
+SvgCustomIcon.displayName = 'SvgCustomIcon';
+
+export default SvgCustomIcon;
diff --git a/packages/components/icons/src/icon.story.js b/packages/components/icons/src/icon.story.js
index 1db60e323a..63e575d1fe 100644
--- a/packages/components/icons/src/icon.story.js
+++ b/packages/components/icons/src/icon.story.js
@@ -15,6 +15,8 @@ import {
import Section from '../../../../docs/.storybook/decorators/section';
import Text from '../../text';
import Readme from '../README.md';
+import CustomIcon from './custom-icon';
+import CustomReactSvg from './fixtures/CustomIconReact';
import xssFixtures from './fixtures/xss';
import InlineSvg from './inline-svg';
import LeadingIcon from './leading-icon';
@@ -294,4 +296,17 @@ storiesOf('Components|Icons', module)
/>
);
+ })
+ .add('CustomIcon', () => {
+ // storybook knobs escape input data to html, so we cannot use them to send unescaped svg, so setting it here using a boolean
+ const useString = boolean('use stringified svg for icon', false);
+ return (
+
+ );
});
diff --git a/packages/components/icons/src/icons.visualroute.jsx b/packages/components/icons/src/icons.visualroute.jsx
index 2d9bcac47a..46977002ae 100644
--- a/packages/components/icons/src/icons.visualroute.jsx
+++ b/packages/components/icons/src/icons.visualroute.jsx
@@ -2,10 +2,12 @@ import styled from '@emotion/styled';
import { Switch, Route } from 'react-router-dom';
import { designTokens } from '@commercetools-uikit/design-system';
import * as icons from '@commercetools-uikit/icons';
+import CustomIcon from '@commercetools-uikit/icons/custom-icon';
import InlineSvg from '@commercetools-uikit/icons/inline-svg';
import LeadingIcon from '@commercetools-uikit/icons/leading-icon';
import Text from '@commercetools-uikit/text';
import Spacings from '@commercetools-uikit/spacings';
+import CustomReactSvg from './fixtures/CustomIconReact';
import rawSvg from './fixtures/raw-svg';
import { Suite, Spec } from '../../../../test/percy';
@@ -100,6 +102,9 @@ export const component = () => (
href={`${routePath}/leading-icon`}
>{`${routePath}/leading-icon`}
+
+ {`${routePath}/custom-icon`}
+
{colors.map((color) => (
@@ -198,5 +203,49 @@ export const component = () => (
+
+
+
+
+ {leadingIconSizes.map((size) => (
+
+
+ } />
+ {`size ${size}`}
+
+
+ ))}
+
+
+
+
+ {leadingIconSizes.map((size) => (
+
+
+
+ {` size ${size}`}
+
+
+ ))}
+
+
+
+
+ {leadingIconSizes.map((size) => (
+
+
+ }
+ hasBorder={false}
+ />
+ {`size ${size}`}
+
+
+ ))}
+
+
+
+
);
diff --git a/packages/components/icons/src/icons.visualspec.js b/packages/components/icons/src/icons.visualspec.js
index 3530f91de9..2d23c1b5b5 100644
--- a/packages/components/icons/src/icons.visualspec.js
+++ b/packages/components/icons/src/icons.visualspec.js
@@ -36,4 +36,9 @@ describe('Icons', () => {
await page.waitForSelector('text/Leading Icon');
await percySnapshot(page, `Icons - Leading Icon`);
});
+ it('Custom Icon', async () => {
+ await page.goto(`${globalThis.HOST}/icons/custom-icon`);
+ await page.waitForSelector('text/Custom Icon');
+ await percySnapshot(page, 'Icons - Custom Icon');
+ });
});