Skip to content

Commit c2184f8

Browse files
committed
add warnings, refactor, and test
1 parent 75da390 commit c2184f8

File tree

2 files changed

+87
-20
lines changed

2 files changed

+87
-20
lines changed

src/components/Icon/Icon.tsx

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,49 @@ const COLORS_WITH_BACKDROPS = [
1616
'inkLighter',
1717
];
1818

19+
const NEW_DESIGN_LANGUAGE_COLORS = [
20+
'base',
21+
'disabled',
22+
'hovered',
23+
'pressed',
24+
'subdued',
25+
'critical',
26+
'warning',
27+
'highlight',
28+
'interactive',
29+
'success',
30+
'primary',
31+
'primaryDisabled',
32+
'primaryHovered',
33+
'primaryPressed',
34+
];
35+
1936
// This is needed for the polaris
2037
// styleguide to generate the props explorer
2138
interface Props extends IconProps {}
2239

23-
export function Icon({source, color, backdrop, accessibilityLabel}: Props) {
40+
export function Icon({
41+
source,
42+
color: colorFromProps,
43+
backdrop,
44+
accessibilityLabel,
45+
}: Props) {
2446
const i18n = useI18n();
47+
const {newDesignLanguage} = useFeatures();
48+
49+
const color =
50+
colorFromProps == null && newDesignLanguage === true
51+
? 'base'
52+
: colorFromProps;
53+
54+
let sourceType: 'function' | 'placeholder' | 'external';
55+
if (typeof source === 'function') {
56+
sourceType = 'function';
57+
} else if (source === 'placeholder') {
58+
sourceType = 'placeholder';
59+
} else {
60+
sourceType = 'external';
61+
}
2562

2663
if (color && backdrop && !COLORS_WITH_BACKDROPS.includes(color)) {
2764
// eslint-disable-next-line no-console
@@ -33,45 +70,49 @@ export function Icon({source, color, backdrop, accessibilityLabel}: Props) {
3370
);
3471
}
3572

36-
const {newDesignLanguage} = useFeatures();
73+
if (
74+
color &&
75+
sourceType === 'external' &&
76+
newDesignLanguage === true &&
77+
NEW_DESIGN_LANGUAGE_COLORS.includes(color)
78+
) {
79+
// eslint-disable-next-line no-console
80+
console.warn(
81+
'Recoloring external SVGs is not supported with colors in the new design langauge. Set the intended color on your SVG instead.',
82+
);
83+
}
3784

3885
const className = classNames(
3986
styles.Icon,
4087
color && styles[variationName('color', color)],
41-
color == null &&
42-
newDesignLanguage &&
43-
styles[variationName('color', 'base')],
4488
color && color !== 'white' && styles.isColored,
4589
backdrop && styles.hasBackdrop,
4690
newDesignLanguage && styles.newDesignLanguage,
4791
);
4892

49-
let contentMarkup: React.ReactNode;
50-
if (typeof source === 'function') {
51-
const SourceComponent = source;
52-
contentMarkup = (
93+
const SourceComponent = source;
94+
const contentMarkup = {
95+
function: (
5396
<SourceComponent
5497
className={styles.Svg}
5598
focusable="false"
5699
aria-hidden="true"
57100
/>
58-
);
59-
} else if (source === 'placeholder') {
60-
contentMarkup = <div className={styles.Placeholder} />;
61-
} else {
62-
contentMarkup = (
101+
),
102+
placeholder: <div className={styles.Placeholder} />,
103+
external: (
63104
<img
64105
className={styles.Img}
65106
src={`data:image/svg+xml;utf8,${source}`}
66107
alt=""
67108
aria-hidden="true"
68109
/>
69-
);
70-
}
110+
),
111+
};
71112

72113
return (
73114
<span className={className} aria-label={accessibilityLabel}>
74-
{contentMarkup}
115+
{contentMarkup[sourceType]}
75116
</span>
76117
);
77118
}

src/components/Icon/tests/Icon.test.tsx

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,42 @@ describe('<Icon />', () => {
3535
});
3636

3737
describe('newDesignLanguage', () => {
38-
it('adds a newDesignLanguage class when newDesignLanguage is enabled', () => {
38+
it('adds a newDesignLanguage class when newDesignLanguage is enabled, and sets a default color', () => {
3939
const icon = mountWithApp(<Icon source={PlusMinor} />, {
4040
features: {newDesignLanguage: true},
4141
});
4242
expect(icon).toContainReactComponent('span', {
43-
className: 'Icon newDesignLanguage',
43+
className: 'Icon colorBase isColored newDesignLanguage',
4444
});
4545
});
4646

47-
it('does not add a newDesignLanguage class when newDesignLanguage is disabled', () => {
47+
it('warns when an untrusted SVG is used with a color option from the new design langauge', () => {
48+
const warningSpy = jest
49+
.spyOn(console, 'warn')
50+
.mockImplementation(() => {});
51+
const svg =
52+
"<svg><path d='M17 9h-6V3a1 1 0 1 0-2 0v6H3a1 1 0 1 0 0 2h6v6a1 1 0 1 0 2 0v-6h6a1 1 0 1 0 0-2' fill-rule='evenodd'/></svg>";
53+
54+
mountWithApp(<Icon source={svg} color="subdued" />, {
55+
features: {newDesignLanguage: true},
56+
});
57+
58+
expect(warningSpy).toHaveBeenCalledWith(
59+
'Recoloring external SVGs is not supported with colors in the new design langauge. Set the intended color on your SVG instead.',
60+
);
61+
warningSpy.mockRestore();
62+
});
63+
64+
it('uses a specified color when newDesignLanguage is enabled', () => {
65+
const icon = mountWithApp(<Icon source={PlusMinor} color="subdued" />, {
66+
features: {newDesignLanguage: true},
67+
});
68+
expect(icon).toContainReactComponent('span', {
69+
className: 'Icon colorSubdued isColored newDesignLanguage',
70+
});
71+
});
72+
73+
it('does not add a newDesignLanguage class when newDesignLanguage is disabled, and does not set a default color', () => {
4874
const icon = mountWithApp(<Icon source={PlusMinor} />, {
4975
features: {newDesignLanguage: false},
5076
});

0 commit comments

Comments
 (0)