Skip to content

Commit 4dd4c7a

Browse files
authored
feat(theming): enhance focus utilities with additional parameter overrides (#1549)
- feat: add `boxShadow` retention to `getFocusBoxShadow` - fix: prevent `focusStyles` from overriding existing `styles: { boxShadow: ... }` - feat: add `spacerHue` and `spacerShade` color overrides to `getFocusBoxShadow` and `focusStyles`
1 parent 6cdbddc commit 4dd4c7a

File tree

5 files changed

+74
-15
lines changed

5 files changed

+74
-15
lines changed

packages/theming/.size-snapshot.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"index.cjs.js": {
3-
"bundled": 20997,
4-
"minified": 13418,
5-
"gzipped": 5145
3+
"bundled": 21255,
4+
"minified": 13544,
5+
"gzipped": 5198
66
},
77
"index.esm.js": {
8-
"bundled": 20004,
9-
"minified": 12515,
10-
"gzipped": 5036,
8+
"bundled": 20262,
9+
"minified": 12641,
10+
"gzipped": 5090,
1111
"treeshaked": {
1212
"rollup": {
1313
"code": 3918,

packages/theming/src/utils/focusStyles.spec.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface IStyledDivProps extends ThemeProps<DefaultTheme> {
2020
selector?: string;
2121
shade?: number;
2222
shadowWidth?: 'sm' | 'md';
23+
spacerHue?: Hue;
24+
spacerShade?: number;
2325
spacerWidth?: null | 'xs' | 'sm';
2426
styles?: CSSObject;
2527
}
@@ -76,6 +78,18 @@ describe('focusStyles', () => {
7678
);
7779
});
7880

81+
it('renders spacer color as expected', () => {
82+
const { container } = render(<StyledDiv spacerHue="red" spacerShade={400} />);
83+
84+
expect(container.firstChild).toHaveStyleRule(
85+
'box-shadow',
86+
expect.stringContaining(`${DEFAULT_THEME.shadowWidths.xs} ${PALETTE.red[400]}`),
87+
{
88+
modifier: '&:focus-visible'
89+
}
90+
);
91+
});
92+
7993
it('renders selector as expected', () => {
8094
const { container } = render(<StyledDiv selector="&:focus-within" />);
8195

@@ -124,8 +138,9 @@ describe('focusStyles', () => {
124138
});
125139

126140
it('renders user provided styles', () => {
141+
const dropShadow = DEFAULT_THEME.shadows.lg('4px', '8px', PALETTE.black);
127142
const { container } = render(
128-
<StyledDiv styles={{ backgroundColor: 'black', color: 'white' }} />
143+
<StyledDiv styles={{ backgroundColor: 'black', boxShadow: dropShadow, color: 'white' }} />
129144
);
130145

131146
expect(container.firstChild).toHaveStyleRule('background-color', 'black', {
@@ -134,5 +149,12 @@ describe('focusStyles', () => {
134149
expect(container.firstChild).toHaveStyleRule('color', 'white', {
135150
modifier: '&:focus-visible'
136151
});
152+
expect(container.firstChild).toHaveStyleRule(
153+
'box-shadow',
154+
expect.stringContaining(dropShadow),
155+
{
156+
modifier: '&:focus-visible'
157+
}
158+
);
137159
});
138160
});

packages/theming/src/utils/focusStyles.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ export const focusStyles = ({
5151
selector = SELECTOR_FOCUS_VISIBLE,
5252
shadowWidth = 'md',
5353
spacerWidth = 'xs',
54-
styles,
54+
styles: { boxShadow, ...styles } = {},
5555
theme,
5656
...options
5757
}: FocusStylesParameters) => {
58-
const boxShadow = condition
59-
? getFocusBoxShadow({ shadowWidth, spacerWidth, theme, ...options })
60-
: undefined;
58+
const _boxShadow = condition
59+
? getFocusBoxShadow({ boxShadow, shadowWidth, spacerWidth, theme, ...options })
60+
: boxShadow;
6161
let outline;
6262
let outlineOffset;
6363

@@ -82,7 +82,7 @@ export const focusStyles = ({
8282
${selector} {
8383
outline: ${outline}; /* [2] */
8484
outline-offset: ${outlineOffset};
85-
box-shadow: ${boxShadow};
85+
box-shadow: ${_boxShadow};
8686
${styles}
8787
}
8888
`;

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,33 @@ describe('getFocusBoxShadow', () => {
4545
);
4646
});
4747

48+
it('overrides spacer color as expected', () => {
49+
const spacerHue = 'successHue';
50+
const spacerShade = 400;
51+
const boxShadow = getFocusBoxShadow({
52+
theme: DEFAULT_THEME,
53+
spacerHue,
54+
spacerShade
55+
});
56+
57+
expect(boxShadow).toContain(
58+
`${DEFAULT_THEME.shadowWidths.xs} ${getColor(spacerHue, spacerShade, DEFAULT_THEME)}`
59+
);
60+
});
61+
4862
it('knocks out spacer as expected', () => {
4963
const boxShadow = getFocusBoxShadow({ theme: DEFAULT_THEME, spacerWidth: null });
5064

5165
expect(boxShadow).not.toContain(`${DEFAULT_THEME.shadowWidths.xs} ${PALETTE.white}`);
5266
});
67+
68+
it('combines with existing box-shadow as expected', () => {
69+
const dropShadow = DEFAULT_THEME.shadows.lg('4px', '8px', PALETTE.black);
70+
const boxShadow = getFocusBoxShadow({
71+
theme: DEFAULT_THEME,
72+
boxShadow: dropShadow
73+
});
74+
75+
expect(boxShadow).toContain(dropShadow);
76+
});
5377
});

packages/theming/src/utils/getFocusBoxShadow.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ import { IGardenTheme } from '../types';
1010
import { DEFAULT_SHADE, Hue, getColor } from './getColor';
1111

1212
export type FocusBoxShadowParameters = {
13+
boxShadow?: string;
1314
inset?: boolean;
1415
hue?: Hue;
1516
shade?: number;
1617
shadowWidth?: 'sm' | 'md';
18+
spacerHue?: Hue;
19+
spacerShade?: number;
1720
spacerWidth?: null | 'xs' | 'sm';
1821
theme: IGardenTheme;
1922
};
@@ -22,21 +25,27 @@ export type FocusBoxShadowParameters = {
2225
* Get a CSS `box-shadow` property value for focus state styling. The `hue` and
2326
* `shade` are used to determine the color of the focus ring.
2427
*
28+
* @param {string} [options.boxShadow] Provides an existing `box-shadow` (a drop shadow, for example) to be retained along with the focus ring
2529
* @param {boolean} [options.inset=false] Determines whether the `box-shadow` is inset
2630
* @param {string|Object} [options.hue='primaryHue'] Provides a theme object `palette` hue or `color` key, or any valid CSS color notation
27-
* @param {number} [options.shade=600] Selects a shade for the given hue
31+
* @param {number} [options.shade=600] Selects a shade for the given `hue`
2832
* @param {string} [options.shadowWidth='md'] Provides a theme object `shadowWidth` key for the cumulative width of the `box-shadow`
33+
* @param {string|Object} [options.spacerHue='background'] Provides a theme object `palette` hue or `color` key, or any valid CSS color notation
34+
* @param {number} [options.spacerShade=600] Selects a shade for the given `spacerHue`
2935
* @param {string} [options.spacerWidth='xs'] Provides a theme object `shadowWidth` for the white spacer, or `null` to remove
3036
* @param {Object} options.theme Provides values used to resolve the desired color
3137
*
3238
* @returns A `box-shadow` property value for the given options. Default is a
3339
* 3px `blue[600]` ring with a 1px white spacer overlay.
3440
*/
3541
export const getFocusBoxShadow = ({
42+
boxShadow,
3643
inset = false,
3744
hue = 'primaryHue',
3845
shade = DEFAULT_SHADE,
3946
shadowWidth = 'md',
47+
spacerHue = 'background',
48+
spacerShade = DEFAULT_SHADE,
4049
spacerWidth = 'xs',
4150
theme = DEFAULT_THEME
4251
}: FocusBoxShadowParameters) => {
@@ -47,7 +56,11 @@ export const getFocusBoxShadow = ({
4756
return `${inset ? 'inset' : ''} ${shadow}`;
4857
}
4958

50-
return `
51-
${inset ? 'inset' : ''} ${theme.shadows[spacerWidth](theme.colors.background)},
59+
const spacerColor = getColor(spacerHue, spacerShade, theme);
60+
61+
const retVal = `
62+
${inset ? 'inset' : ''} ${theme.shadows[spacerWidth](spacerColor!)},
5263
${inset ? 'inset' : ''} ${shadow}`;
64+
65+
return boxShadow ? `${retVal}, ${boxShadow}` : retVal;
5366
};

0 commit comments

Comments
 (0)