diff --git a/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.js b/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.js new file mode 100644 index 00000000000000..821f8b8c126287 --- /dev/null +++ b/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.js @@ -0,0 +1,90 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Checkbox from '@material-ui/core/Checkbox'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const useStyles = makeStyles({ + root: { + width: '100%', + }, +}); + +export default function ActionsInExpansionPanelSummary() { + const classes = useStyles(); + + return ( +
+ + } + aria-label="Expand" + aria-controls="additional-actions1-content" + id="additional-actions1-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the click event propagation" + /> + + + + The click event of the nested action will propagate up and expand the panel unless you + explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions2-content" + id="additional-actions2-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the focus event propagation" + /> + + + + The focus event of the nested action will propagate up and also focus the expansion + panel unless you explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions3-content" + id="additional-actions3-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should provide an aria-label on each action that I add" + /> + + + + If you forget to put an aria-label on the nested action, the label of the action will + also be included in the label of the parent button that controls the panel expansion. + + + +
+ ); +} diff --git a/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.tsx b/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.tsx new file mode 100644 index 00000000000000..821f8b8c126287 --- /dev/null +++ b/docs/src/pages/components/expansion-panels/ActionsInExpansionPanelSummary.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import ExpansionPanel from '@material-ui/core/ExpansionPanel'; +import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'; +import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'; +import Checkbox from '@material-ui/core/Checkbox'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; + +const useStyles = makeStyles({ + root: { + width: '100%', + }, +}); + +export default function ActionsInExpansionPanelSummary() { + const classes = useStyles(); + + return ( +
+ + } + aria-label="Expand" + aria-controls="additional-actions1-content" + id="additional-actions1-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the click event propagation" + /> + + + + The click event of the nested action will propagate up and expand the panel unless you + explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions2-content" + id="additional-actions2-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should stop the focus event propagation" + /> + + + + The focus event of the nested action will propagate up and also focus the expansion + panel unless you explicitly stop it. + + + + + } + aria-label="Expand" + aria-controls="additional-actions3-content" + id="additional-actions3-header" + > + event.stopPropagation()} + onFocus={event => event.stopPropagation()} + control={} + label="I acknowledge that I should provide an aria-label on each action that I add" + /> + + + + If you forget to put an aria-label on the nested action, the label of the action will + also be included in the label of the parent button that controls the panel expansion. + + + +
+ ); +} diff --git a/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.js b/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.js index 4fe1071686225c..21e1709fa6f1c6 100644 --- a/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.js +++ b/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.js @@ -73,7 +73,7 @@ export default function DetailedExpansionPanel() { Select your destination of choice
- + Learn more
diff --git a/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.tsx b/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.tsx index 8f6b7ff870c67b..12d73dbdafd5f5 100644 --- a/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.tsx +++ b/docs/src/pages/components/expansion-panels/DetailedExpansionPanel.tsx @@ -75,7 +75,7 @@ export default function DetailedExpansionPanel() { Select your destination of choice
- + Learn more
diff --git a/docs/src/pages/components/expansion-panels/expansion-panels.md b/docs/src/pages/components/expansion-panels/expansion-panels.md index f4446e19975a16..ca84c269dcbf4c 100644 --- a/docs/src/pages/components/expansion-panels/expansion-panels.md +++ b/docs/src/pages/components/expansion-panels/expansion-panels.md @@ -28,6 +28,15 @@ Here is an example of customizing the component. You can learn more about this i {{"demo": "pages/components/expansion-panels/CustomizedExpansionPanels.js"}} +## Additional actions + +In order to put an action such as a `Checkbox` or a button inside of the `ExpansionPanelSummary`, you need to stop the propagation of the focus and click events to prevent the panel from +expanding/collapsing when using the action. +You should also provide an `aria-label` for the action, otherwise the label of the nested action will be included in +the label of the parent button that controls the panel expansion. + +{{"demo": "pages/components/expansion-panels/ActionsInExpansionPanelSummary.js"}} + ## Performance The content of ExpansionPanels is mounted by default even if the panel is not expanded. diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js index 81d71a8ff81b4c..5d3227e2db020a 100644 --- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js +++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js @@ -1,3 +1,4 @@ +/* eslint-disable jsx-a11y/aria-role */ import React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; @@ -128,13 +129,13 @@ const ExpansionPanelSummary = React.forwardRef(function ExpansionPanelSummary(pr
{children}
{expandIcon && ( diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js index 73c129ef216b3a..ce91479de99bad 100644 --- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js +++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js @@ -38,25 +38,21 @@ describe('', () => { expect(getByRole('button')).to.have.class(classes.disabled); }); - it('when expanded adds the expanded class to any button regardless of a11y', () => { - const { getAllByRole } = render(); - - const buttons = getAllByRole('button', { hidden: true }); - expect(buttons).to.have.length(2); - expect(buttons[0]).to.have.class(classes.expanded); - expect(buttons[0]).to.have.attribute('aria-expanded', 'true'); - expect(buttons[0]).not.to.be.inaccessible; - expect(buttons[1]).to.have.class(classes.expanded); - expect(buttons[1]).to.be.inaccessible; + it('when expanded adds the expanded class to the button and expandIcon', () => { + const { container, getByRole } = render(); + + const button = getByRole('button'); + expect(button).to.have.class(classes.expanded); + expect(button).to.have.attribute('aria-expanded', 'true'); + expect(container.querySelector(`.${classes.expandIcon}`)).to.have.class(classes.expanded); }); - it('should render with the expand icon and have the expandIcon class', () => { - const { getAllByRole } = render(Icon} />); + it('should render with an inaccessible expand icon and have the expandIcon class', () => { + const { container } = render(Icon} />); - const expandButton = getAllByRole('button', { hidden: true })[1]; - expect(expandButton).to.have.class(classes.expandIcon); - expect(expandButton).to.have.text('Icon'); - expect(expandButton).to.be.inaccessible; + const expandIcon = container.querySelector(`.${classes.expandIcon}`); + expect(expandIcon).to.have.text('Icon'); + expect(expandIcon).to.be.inaccessible; }); it('focusing adds the `focused` class if focused visible', () => {