Skip to content

Commit 5282298

Browse files
authored
feat(theming): enhance getColor to accept rgba and literal color variables (#1806)
1 parent 717a8d5 commit 5282298

File tree

4 files changed

+145
-15
lines changed

4 files changed

+145
-15
lines changed

packages/theming/src/elements/theme/__snapshots__/index.spec.ts.snap

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
3737
"danger": "dangerHue.1000",
3838
"dangerEmphasis": "dangerHue.600",
3939
"default": "neutralHue.1100",
40+
"disabled": "rgba(neutralHue.500, 100)",
4041
"emphasis": "neutralHue.600",
4142
"primary": "primaryHue.900",
4243
"primaryEmphasis": "primaryHue.600",
@@ -52,6 +53,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
5253
"danger": "dangerHue.800",
5354
"dangerEmphasis": "dangerHue.600",
5455
"default": "neutralHue.800",
56+
"disabled": "neutralHue.800",
5557
"emphasis": "neutralHue.600",
5658
"primaryEmphasis": "primaryHue.600",
5759
"subtle": "neutralHue.900",
@@ -64,6 +66,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
6466
"danger": "dangerHue.400",
6567
"dangerEmphasis": "dangerHue.300",
6668
"default": "neutralHue.300",
69+
"disabled": "neutralHue.700",
6770
"onEmphasis": "neutralHue.1100",
6871
"primary": "primaryHue.600",
6972
"subtle": "neutralHue.500",
@@ -78,6 +81,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
7881
"danger": "dangerHue.100",
7982
"dangerEmphasis": "dangerHue.700",
8083
"default": "palette.white",
84+
"disabled": "rgba(neutralHue.700, 100)",
8185
"emphasis": "neutralHue.700",
8286
"primary": "primaryHue.100",
8387
"primaryEmphasis": "primaryHue.700",
@@ -93,6 +97,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
9397
"danger": "dangerHue.300",
9498
"dangerEmphasis": "dangerHue.700",
9599
"default": "neutralHue.300",
100+
"disabled": "neutralHue.300",
96101
"emphasis": "neutralHue.600",
97102
"primaryEmphasis": "primaryHue.700",
98103
"subtle": "neutralHue.200",
@@ -105,6 +110,7 @@ exports[`DEFAULT_THEME matches snapshot 1`] = `
105110
"danger": "dangerHue.700",
106111
"dangerEmphasis": "dangerHue.900",
107112
"default": "neutralHue.900",
113+
"disabled": "neutralHue.600",
108114
"onEmphasis": "palette.white",
109115
"primary": "primaryHue.700",
110116
"subtle": "neutralHue.700",

packages/theming/src/elements/theme/index.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ const colors = {
5959
primaryEmphasis: 'primaryHue.600',
6060
successEmphasis: 'successHue.600',
6161
warningEmphasis: 'warningHue.600',
62-
dangerEmphasis: 'dangerHue.600'
62+
dangerEmphasis: 'dangerHue.600',
63+
disabled: 'rgba(neutralHue.500, 100)'
6364
},
6465
border: {
6566
default: 'neutralHue.800',
@@ -71,7 +72,8 @@ const colors = {
7172
primaryEmphasis: 'primaryHue.600',
7273
successEmphasis: 'successHue.600',
7374
warningEmphasis: 'warningHue.600',
74-
dangerEmphasis: 'dangerHue.600'
75+
dangerEmphasis: 'dangerHue.600',
76+
disabled: 'neutralHue.800'
7577
},
7678
foreground: {
7779
default: 'neutralHue.300',
@@ -83,7 +85,8 @@ const colors = {
8385
danger: 'dangerHue.400',
8486
successEmphasis: 'successHue.300',
8587
warningEmphasis: 'warningHue.300',
86-
dangerEmphasis: 'dangerHue.300'
88+
dangerEmphasis: 'dangerHue.300',
89+
disabled: 'neutralHue.700'
8790
}
8891
},
8992
light: {
@@ -100,7 +103,8 @@ const colors = {
100103
primaryEmphasis: 'primaryHue.700',
101104
successEmphasis: 'successHue.700',
102105
warningEmphasis: 'warningHue.700',
103-
dangerEmphasis: 'dangerHue.700'
106+
dangerEmphasis: 'dangerHue.700',
107+
disabled: 'rgba(neutralHue.700, 100)'
104108
},
105109
border: {
106110
default: 'neutralHue.300',
@@ -112,7 +116,8 @@ const colors = {
112116
primaryEmphasis: 'primaryHue.700',
113117
successEmphasis: 'successHue.700',
114118
warningEmphasis: 'warningHue.700',
115-
dangerEmphasis: 'dangerHue.700'
119+
dangerEmphasis: 'dangerHue.700',
120+
disabled: 'neutralHue.300'
116121
},
117122
foreground: {
118123
default: 'neutralHue.900',
@@ -124,7 +129,8 @@ const colors = {
124129
danger: 'dangerHue.700',
125130
successEmphasis: 'successHue.900',
126131
warningEmphasis: 'warningHue.900',
127-
dangerEmphasis: 'dangerHue.900'
132+
dangerEmphasis: 'dangerHue.900',
133+
disabled: 'neutralHue.600'
128134
}
129135
}
130136
}

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

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ describe('getColor', () => {
2929
expect(color).toBe(expected);
3030
});
3131

32+
it('accepts literal color values', () => {
33+
const expected = '#fd5a1e';
34+
const theme: IGardenTheme = {
35+
...DEFAULT_THEME,
36+
colors: {
37+
...DEFAULT_THEME.colors,
38+
variables: {
39+
...DEFAULT_THEME.colors.variables,
40+
light: {
41+
...DEFAULT_THEME.colors.variables.light,
42+
background: { test: expected }
43+
}
44+
}
45+
}
46+
};
47+
const color = getColor({ theme, variable: 'background.test' });
48+
49+
expect(color).toBe(expected);
50+
});
51+
3252
it('uses `DEFAULT_THEME` fallback for malformed variables', () => {
3353
const theme: IGardenTheme = {
3454
...DEFAULT_THEME,
@@ -38,6 +58,38 @@ describe('getColor', () => {
3858

3959
expect(color).toBe(PALETTE.white);
4060
});
61+
62+
describe('with rbga', () => {
63+
it.each([['light'], ['dark']])('gets the expected %s mode color with opacity', mode => {
64+
const color = getColor({
65+
theme: mode === 'dark' ? DARK_THEME : DEFAULT_THEME,
66+
variable: 'background.disabled'
67+
});
68+
const transparency = 0.08;
69+
const expected = rgba(PALETTE.grey[mode === 'dark' ? 500 : 700], transparency);
70+
71+
expect(color).toBe(expected);
72+
});
73+
74+
it('accepts a literal color parameter', () => {
75+
const theme: IGardenTheme = {
76+
...DEFAULT_THEME,
77+
colors: {
78+
...DEFAULT_THEME.colors,
79+
variables: {
80+
...DEFAULT_THEME.colors.variables,
81+
light: {
82+
...DEFAULT_THEME.colors.variables.light,
83+
background: { test: 'rgba(#fd5a1e, 0.5)' }
84+
}
85+
}
86+
}
87+
};
88+
const color = getColor({ theme, variable: 'background.test' });
89+
90+
expect(color).toBe(rgba('#fd5a1e', 0.5));
91+
});
92+
});
4193
});
4294

4395
describe('by hue', () => {
@@ -362,5 +414,22 @@ describe('getColor', () => {
362414
Error
363415
);
364416
});
417+
418+
it('throws an error if rgba is invalid', () => {
419+
const theme: IGardenTheme = {
420+
...DEFAULT_THEME,
421+
colors: {
422+
...DEFAULT_THEME.colors,
423+
variables: {
424+
...DEFAULT_THEME.colors.variables,
425+
light: {
426+
...DEFAULT_THEME.colors.variables.light,
427+
background: { test: 'rgba(invalid)' }
428+
}
429+
}
430+
}
431+
};
432+
expect(() => getColor({ theme, variable: 'background.test' })).toThrow(Error);
433+
});
365434
});
366435
});

packages/theming/src/utils/getColor.ts

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,56 @@ const toProperty = (object: object, path: string) => {
135135
}
136136
};
137137

138+
/* derive the property + transparency from the given rgba variable value */
139+
const fromRgba = (value: string) => {
140+
let retVal;
141+
const regex =
142+
/rgba\s*\(\s*(?<property>[#\w.]+)\s*,\s*(?<alpha>[\w.]+)\s*\)/gu; /* ex. 'rgba(primaryHue.700, 600)' */
143+
const _rgba = regex.exec(value);
144+
145+
if (_rgba && _rgba.groups) {
146+
const property = _rgba.groups.property;
147+
const transparency = parseFloat(_rgba.groups.alpha);
148+
149+
retVal = { property, transparency };
150+
} else {
151+
throw new Error(`Error: invalid \`rgba\` value "${value}"`);
152+
}
153+
154+
return retVal;
155+
};
156+
157+
/* derive the hue + shade + transparency from the given variable */
158+
const fromVariable = (
159+
variable: string,
160+
variables: IGardenTheme['colors']['variables']['dark' | 'light'],
161+
palette: IGardenTheme['palette']
162+
) => {
163+
const retVal: { hue?: string; shade?: number; transparency?: number } = {};
164+
let property = toProperty(variables, variable);
165+
166+
if (property.startsWith('rgba')) {
167+
const value = fromRgba(property);
168+
169+
property = value.property;
170+
retVal.transparency = value.transparency;
171+
}
172+
173+
const [key, value] = property.split(/\.(?<value>.*)/u);
174+
175+
if (key === 'palette') {
176+
retVal.hue = toProperty(palette, value); /* ex. `variable` = 'palette.white' */
177+
} else {
178+
retVal.hue = key; /* ex. `variable` = '#fd5a1e' */
179+
180+
if (value !== undefined) {
181+
retVal.shade = parseInt(value, 10); /* ex. `variable` = 'primaryHue.700' */
182+
}
183+
}
184+
185+
return retVal;
186+
};
187+
138188
/**
139189
* Get a color value from the theme. Variable lookup takes precedence, followed
140190
* by `dark` and `light` object values. If none of these are provided, `hue`,
@@ -172,21 +222,20 @@ export const getColor = memoize(
172222
let _hue = mode?.hue || hue;
173223
let _shade = mode?.shade === undefined ? shade : mode.shade;
174224
const _offset = mode?.offset === undefined ? offset : mode.offset;
175-
const _transparency = mode?.transparency === undefined ? transparency : mode.transparency;
225+
let _transparency = mode?.transparency === undefined ? transparency : mode.transparency;
176226

177227
if (variable) {
178228
// variable lookup takes precedence
179229
const _variables = variables?.[scheme]
180230
? variables[scheme]
181231
: DEFAULT_THEME.colors.variables[scheme];
182-
const property = toProperty(_variables, variable);
183-
const [key, value] = property.split(/\.(?<value>.*)/u);
184-
185-
if (key === 'palette') {
186-
_hue = toProperty(palette, value); /* ex. `variable` = 'palette.white' */
187-
} else {
188-
_hue = key;
189-
_shade = parseInt(value, 10);
232+
const value = fromVariable(variable, _variables, palette);
233+
234+
_hue = value.hue;
235+
_shade = value.shade;
236+
237+
if (value.transparency !== undefined) {
238+
_transparency = value.transparency;
190239
}
191240
}
192241

0 commit comments

Comments
 (0)