Skip to content

Commit 4b76942

Browse files
[Accordion] Normalize focusVisible logic (#22567)
1 parent 3994bcc commit 4b76942

File tree

5 files changed

+29
-70
lines changed

5 files changed

+29
-70
lines changed

docs/pages/api-docs/accordion-summary.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ The `MuiAccordionSummary` name can be used for providing [default props](/custom
3131
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the accordion summary. |
3232
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
3333
| <span class="prop-name">expandIcon</span> | <span class="prop-type">node</span> | | The icon to display as the expand indicator. |
34+
| <span class="prop-name">focusVisibleClassName</span> | <span class="prop-type">string</span> | | This prop can help a person know which element has the keyboard focus. The class name will be applied when the element gain the focus through a keyboard interaction. It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo). The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/master/explainer.md). A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components if needed. |
3435
| <span class="prop-name">IconButtonProps</span> | <span class="prop-type">object</span> | <span class="prop-default">{}</span> | Props applied to the `IconButton` element wrapping the expand icon. |
3536

3637
The `ref` is forwarded to the root element.
@@ -43,7 +44,7 @@ Any other props supplied will be provided to the root element ([ButtonBase](/api
4344
|:-----|:-------------|:------------|
4445
| <span class="prop-name">root</span> | <span class="prop-name">.MuiAccordionSummary-root</span> | Styles applied to the root element.
4546
| <span class="prop-name">expanded</span> | <span class="prop-name">.Mui-expanded</span> | Pseudo-class applied to the root element, children wrapper element and `IconButton` component if `expanded={true}`.
46-
| <span class="prop-name">focused</span> | <span class="prop-name">.Mui-focused</span> | Pseudo-class applied to the root element if `focused={true}`.
47+
| <span class="prop-name">focusVisible</span> | <span class="prop-name">.Mui-focusVisible</span> | Pseudo-class applied to the ButtonBase root element if the button is keyboard focused.
4748
| <span class="prop-name">disabled</span> | <span class="prop-name">.Mui-disabled</span> | Pseudo-class applied to the root element if `disabled={true}`.
4849
| <span class="prop-name">content</span> | <span class="prop-name">.MuiAccordionSummary-content</span> | Styles applied to the children wrapper element.
4950
| <span class="prop-name">expandIcon</span> | <span class="prop-name">.MuiAccordionSummary-expandIcon</span> | Styles applied to the `IconButton` component when `expandIcon` is supplied.

docs/src/pages/guides/migration-v4/migration-v4.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,17 @@ const theme = createMuitheme({
314314
+<Accordion onChange={(event: React.SyntheticEvent, expanded: boolean) => {}} />
315315
```
316316

317+
- Rename `focused` to `focusVisible` for consistency:
318+
319+
```diff
320+
<Accordion
321+
classes={{
322+
- focused: 'custom-focus-visible-classname',
323+
+ focusVisible: 'custom-focus-visible-classname',
324+
}}
325+
/>
326+
```
327+
317328
### Fab
318329

319330
- Rename `round` to `circular` for consistency. The possible values should be adjectives, not nouns:

packages/material-ui/src/AccordionSummary/AccordionSummary.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export type AccordionSummaryTypeMap<
2020
root?: string;
2121
/** Pseudo-class applied to the root element, children wrapper element and `IconButton` component if `expanded={true}`. */
2222
expanded?: string;
23-
/** Pseudo-class applied to the root element if `focused={true}`. */
24-
focused?: string;
23+
/** Pseudo-class applied to the ButtonBase root element if the button is keyboard focused. */
24+
focusVisible?: string;
2525
/** Pseudo-class applied to the root element if `disabled={true}`. */
2626
disabled?: string;
2727
/** Styles applied to the children wrapper element. */

packages/material-ui/src/AccordionSummary/AccordionSummary.js

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const styles = (theme) => {
2525
'&$expanded': {
2626
minHeight: 64,
2727
},
28-
'&$focused': {
28+
'&$focusVisible': {
2929
backgroundColor: theme.palette.action.focus,
3030
},
3131
'&$disabled': {
@@ -34,8 +34,8 @@ export const styles = (theme) => {
3434
},
3535
/* Pseudo-class applied to the root element, children wrapper element and `IconButton` component if `expanded={true}`. */
3636
expanded: {},
37-
/* Pseudo-class applied to the root element if `focused={true}`. */
38-
focused: {},
37+
/* Pseudo-class applied to the ButtonBase root element if the button is keyboard focused. */
38+
focusVisible: {},
3939
/* Pseudo-class applied to the root element if `disabled={true}`. */
4040
disabled: {},
4141
/* Styles applied to the children wrapper element. */
@@ -71,29 +71,12 @@ const AccordionSummary = React.forwardRef(function AccordionSummary(props, ref)
7171
classes,
7272
className,
7373
expandIcon,
74+
focusVisibleClassName,
7475
IconButtonProps = {},
75-
onBlur,
7676
onClick,
77-
onFocusVisible,
7877
...other
7978
} = props;
8079

81-
const [focusedState, setFocusedState] = React.useState(false);
82-
const handleFocusVisible = (event) => {
83-
setFocusedState(true);
84-
85-
if (onFocusVisible) {
86-
onFocusVisible(event);
87-
}
88-
};
89-
const handleBlur = (event) => {
90-
setFocusedState(false);
91-
92-
if (onBlur) {
93-
onBlur(event);
94-
}
95-
};
96-
9780
const { disabled = false, expanded, toggle } = React.useContext(AccordionContext);
9881
const handleChange = (event) => {
9982
if (toggle) {
@@ -116,12 +99,10 @@ const AccordionSummary = React.forwardRef(function AccordionSummary(props, ref)
11699
{
117100
[classes.disabled]: disabled,
118101
[classes.expanded]: expanded,
119-
[classes.focused]: focusedState,
120102
},
121103
className,
122104
)}
123-
onFocusVisible={handleFocusVisible}
124-
onBlur={handleBlur}
105+
focusVisibleClassName={clsx(classes.focusVisible, focusVisibleClassName)}
125106
onClick={handleChange}
126107
ref={ref}
127108
{...other}
@@ -171,23 +152,24 @@ AccordionSummary.propTypes = {
171152
* The icon to display as the expand indicator.
172153
*/
173154
expandIcon: PropTypes.node,
155+
/**
156+
* This prop can help a person know which element has the keyboard focus.
157+
* The class name will be applied when the element gain the focus through a keyboard interaction.
158+
* It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo).
159+
* The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/master/explainer.md).
160+
* A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components
161+
* if needed.
162+
*/
163+
focusVisibleClassName: PropTypes.string,
174164
/**
175165
* Props applied to the `IconButton` element wrapping the expand icon.
176166
* @default {}
177167
*/
178168
IconButtonProps: PropTypes.object,
179-
/**
180-
* @ignore
181-
*/
182-
onBlur: PropTypes.func,
183169
/**
184170
* @ignore
185171
*/
186172
onClick: PropTypes.func,
187-
/**
188-
* @ignore
189-
*/
190-
onFocusVisible: PropTypes.func,
191173
};
192174

193175
export default withStyles(styles, { name: 'MuiAccordionSummary' })(AccordionSummary);

packages/material-ui/src/AccordionSummary/AccordionSummary.test.js

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,41 +80,6 @@ describe('<AccordionSummary />', () => {
8080
expect(expandIcon).toBeInaccessible();
8181
});
8282

83-
it('focusing adds the `focused` class if focused visible', () => {
84-
// TODO v5: Rename `focused` -> `focus-visible`
85-
// `focused` is a global state which is applied on focus
86-
// only here do we constrain it to focus-visible. THe name is also not consistent
87-
// with :focus
88-
const { getByRole } = render(<AccordionSummary />);
89-
fireEvent.mouseDown(document.body); // pointer device
90-
const button = getByRole('button');
91-
92-
act(() => {
93-
fireEvent.keyDown(document.body, { key: 'Tab' }); // not actually focusing (yet)
94-
button.focus();
95-
});
96-
97-
expect(button).toHaveFocus();
98-
expect(button).to.have.class(classes.focused);
99-
});
100-
101-
it('blur should unset focused state', () => {
102-
const { getByRole } = render(<AccordionSummary />);
103-
fireEvent.mouseDown(document.body); // pointer device
104-
fireEvent.keyDown(document.body, { key: 'Tab' }); // not actually focusing (yet)
105-
const button = getByRole('button');
106-
107-
act(() => {
108-
button.focus();
109-
});
110-
act(() => {
111-
button.blur();
112-
});
113-
114-
expect(button).not.toHaveFocus();
115-
expect(button).not.to.have.class(classes.focused);
116-
});
117-
11883
it('should fire onBlur when the button blurs', () => {
11984
const handleBlur = spy();
12085
const { getByRole } = render(<AccordionSummary onBlur={handleBlur} />);

0 commit comments

Comments
 (0)