Skip to content

Commit 3dc00a8

Browse files
authored
fix(theming): harden getColor memoization key generation (#1966)
1 parent 1c6ac75 commit 3dc00a8

File tree

5 files changed

+56
-22
lines changed

5 files changed

+56
-22
lines changed

docs/migration.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,16 @@ consider additional positioning prop support on a case-by-case basis.
118118
- Use this package if you were using `@zendeskgarden/react-dropdowns.next` in `v8`
119119
- The `v8` version of `@zendeskgarden/react-dropdowns` is no longer maintained and is
120120
renamed to `@zendeskgarden/react-dropdowns.legacy` in `v9`
121+
- `Combobox`
122+
- `Option` no longer accepts an object type for the `value` prop. Use string
123+
values for stable comparison.
124+
- Removed `label` prop from `OptGroup`. Use `legend` instead.
121125
- `Menu`
122126
- value `auto` is no longer valid for the `fallbackPlacements` prop.
123127
- new `restoreFocus` prop (default: `true`) returns focus to trigger
124128
after menu interaction. When menu expansion is controlled to allow
125129
multiple item selection, set `restoreFocus={false}` and manage trigger
126130
focus manually on close.
127-
- Removed `label` prop from `OptGroup`. Use `legend` instead.
128131

129132
#### @zendeskgarden/react-forms
130133

packages/theming/src/utils/getColor.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,12 @@ describe('getColor', () => {
391391
console.error = consoleError;
392392
});
393393

394+
it('does not throw a memoization key error when the theme is invalid', () => {
395+
const test = () => getColor({ theme: {} as any, variable: 'background.default' });
396+
397+
expect(test).not.toThrow('Invalid value used as weak map key');
398+
});
399+
394400
it('throws an error if color arguments are missing', () => {
395401
expect(() => getColor({ theme: DEFAULT_THEME })).toThrow(Error);
396402
});

packages/theming/src/utils/getColor.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ CACHE.set(DEFAULT_THEME.colors, KEYS.colors);
281281
CACHE.set(DEFAULT_THEME.palette, KEYS.palette);
282282
CACHE.set(DEFAULT_THEME.opacity, KEYS.opacity);
283283

284+
/* convert `getColor` parameters to a memoization key */
284285
const toKey = ({
285286
dark,
286287
hue,
@@ -291,25 +292,37 @@ const toKey = ({
291292
transparency,
292293
variable
293294
}: ColorParameters) => {
294-
let themeColorsKey = CACHE.get(theme.colors);
295+
let themeColorsKey;
295296

296-
if (themeColorsKey === undefined) {
297-
themeColorsKey = ++KEYS.colors;
298-
CACHE.set(theme.colors, themeColorsKey);
297+
if (theme.colors) {
298+
themeColorsKey = CACHE.get(theme.colors);
299+
300+
if (themeColorsKey === undefined) {
301+
themeColorsKey = ++KEYS.colors;
302+
CACHE.set(theme.colors, themeColorsKey);
303+
}
299304
}
300305

301-
let themeOpacityKey = CACHE.get(theme.opacity);
306+
let themeOpacityKey;
307+
308+
if (theme.opacity) {
309+
themeOpacityKey = CACHE.get(theme.opacity);
302310

303-
if (themeOpacityKey === undefined) {
304-
themeOpacityKey = ++KEYS.opacity;
305-
CACHE.set(theme.opacity, themeOpacityKey);
311+
if (themeOpacityKey === undefined) {
312+
themeOpacityKey = ++KEYS.opacity;
313+
CACHE.set(theme.opacity, themeOpacityKey);
314+
}
306315
}
307316

308-
let themePaletteKey = CACHE.get(theme.palette);
317+
let themePaletteKey;
318+
319+
if (theme.palette) {
320+
themePaletteKey = CACHE.get(theme.palette);
309321

310-
if (themePaletteKey === undefined) {
311-
themePaletteKey = ++KEYS.palette;
312-
CACHE.set(theme.palette, themePaletteKey);
322+
if (themePaletteKey === undefined) {
323+
themePaletteKey = ++KEYS.palette;
324+
CACHE.set(theme.palette, themePaletteKey);
325+
}
313326
}
314327

315328
let retVal = `{${themeColorsKey},${themePaletteKey},${themeOpacityKey}}`;

packages/theming/src/utils/getColorV8.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@ describe('getColorV8', () => {
167167

168168
expect(color).toBe(expected);
169169
});
170+
171+
it('handles an invalid theme', () => {
172+
expect(() => getColorV8('test', 600, {} as any)).not.toThrow();
173+
});
170174
});
171175

172176
describe('by transparency', () => {

packages/theming/src/utils/getColorV8.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,25 +42,33 @@ const toKey = ({
4242
theme?: DefaultTheme;
4343
transparency?: number;
4444
}) => {
45-
let retVal = `${hue}`;
45+
let retVal = `${typeof hue === 'object' ? JSON.stringify(hue) : hue}`;
4646

4747
if (shade !== undefined) {
4848
retVal += `,${shade}`;
4949
}
5050

5151
if (theme !== undefined) {
52-
let themeColorsKey = CACHE.get(theme.colors);
52+
let themeColorsKey;
5353

54-
if (themeColorsKey === undefined) {
55-
themeColorsKey = ++KEYS.colors;
56-
CACHE.set(theme.colors, themeColorsKey);
54+
if (theme.colors) {
55+
themeColorsKey = CACHE.get(theme.colors);
56+
57+
if (themeColorsKey === undefined) {
58+
themeColorsKey = ++KEYS.colors;
59+
CACHE.set(theme.colors, themeColorsKey);
60+
}
5761
}
5862

59-
let themePaletteKey = CACHE.get(theme.palette);
63+
let themePaletteKey;
6064

61-
if (themePaletteKey === undefined) {
62-
themePaletteKey = ++KEYS.palette;
63-
CACHE.set(theme.palette, themePaletteKey);
65+
if (theme.palette) {
66+
themePaletteKey = CACHE.get(theme.palette);
67+
68+
if (themePaletteKey === undefined) {
69+
themePaletteKey = ++KEYS.palette;
70+
CACHE.set(theme.palette, themePaletteKey);
71+
}
6472
}
6573

6674
retVal += `,{${themeColorsKey},${themePaletteKey}}`;

0 commit comments

Comments
 (0)