diff --git a/docs/data/material/components/accordion/accordion.md b/docs/data/material/components/accordion/accordion.md index 43a50d50aec4e0..c9fe4170bc5896 100644 --- a/docs/data/material/components/accordion/accordion.md +++ b/docs/data/material/components/accordion/accordion.md @@ -84,6 +84,26 @@ The demo below also shows a bit of visual customization. {{"demo": "CustomizedAccordions.js", "bg": true}} +### Changing heading level + +By default, the Accordion uses an `h3` element for the heading. You can change the heading element using the `slotProps.heading.component` prop to ensure the correct heading hierarchy in your document. + +```jsx + + } + aria-controls="panel1-content" + id="panel1-header" + > + Accordion + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada + lacus ex, sit amet blandit leo lobortis eget. + + +``` + ## Performance The Accordion content is mounted by default even if it's not expanded. diff --git a/docs/data/material/migration/migrating-to-v6/migrating-to-v6.md b/docs/data/material/migration/migrating-to-v6/migrating-to-v6.md index 1b23dc5e877ce7..36a5ec48e4c804 100644 --- a/docs/data/material/migration/migrating-to-v6/migrating-to-v6.md +++ b/docs/data/material/migration/migrating-to-v6/migrating-to-v6.md @@ -132,6 +132,30 @@ These three were previously treated as `"reset"`, so if you are relying on that, These are added on top of the existing ones: `"input"`, `"reset"`, and `"clear"`. +### Accordion + +#### Heading element wrapping Accordion Summary + +To meet the [W3C Accordion Pattern standard](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/), the Accordion Summary is now wrapped with a default `h3` heading element. This change may affect customizations relying on the previous DOM structure and CSS specificity. Additionally, the default heading element might conflict with existing heading structures on your page. + +If your styles or DOM manipulations depend on the old structure, you will need to update them to accommodate the new heading element. If the default heading element conflicts with your existing structure, you can change the heading element using the `slotProps.heading.component` prop. + +```jsx + + } + aria-controls="panel1-content" + id="panel1-header" + > + Accordion + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse malesuada + lacus ex, sit amet blandit leo lobortis eget. + + +``` + ### Chip Previously, the Chip component lost focus when the escape button was pressed, which differed from how other button-like components work. diff --git a/docs/pages/material-ui/api/accordion.json b/docs/pages/material-ui/api/accordion.json index 94c89fd40989f1..a093fff40226a5 100644 --- a/docs/pages/material-ui/api/accordion.json +++ b/docs/pages/material-ui/api/accordion.json @@ -14,11 +14,17 @@ } }, "slotProps": { - "type": { "name": "shape", "description": "{ transition?: func
| object }" }, + "type": { + "name": "shape", + "description": "{ heading?: func
| object, transition?: func
| object }" + }, "default": "{}" }, "slots": { - "type": { "name": "shape", "description": "{ transition?: elementType }" }, + "type": { + "name": "shape", + "description": "{ heading?: elementType, transition?: elementType }" + }, "default": "{}" }, "square": { "type": { "name": "bool" }, "default": "false" }, @@ -38,6 +44,12 @@ "import { Accordion } from '@mui/material';" ], "slots": [ + { + "name": "heading", + "description": "The component that renders the heading.", + "default": "'h3'", + "class": "MuiAccordion-heading" + }, { "name": "transition", "description": "The component that renders the transition.\n[Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.", diff --git a/docs/translations/api-docs/accordion/accordion.json b/docs/translations/api-docs/accordion/accordion.json index a5f88b5260eb03..894051258146ae 100644 --- a/docs/translations/api-docs/accordion/accordion.json +++ b/docs/translations/api-docs/accordion/accordion.json @@ -60,6 +60,7 @@ } }, "slotDescriptions": { + "heading": "The component that renders the heading.", "transition": "The component that renders the transition. Follow this guide to learn more about the requirements for this component." } } diff --git a/packages/mui-material/src/Accordion/Accordion.d.ts b/packages/mui-material/src/Accordion/Accordion.d.ts index d3880f53622e01..c1f207ffa6df15 100644 --- a/packages/mui-material/src/Accordion/Accordion.d.ts +++ b/packages/mui-material/src/Accordion/Accordion.d.ts @@ -8,6 +8,11 @@ import { ExtendPaperTypeMap } from '../Paper/Paper'; import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; export interface AccordionSlots { + /** + * The component that renders the heading. + * @default 'h3' + */ + heading?: React.ElementType; /** * The component that renders the transition. * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. @@ -19,10 +24,16 @@ export interface AccordionSlots { } export interface AccordionTransitionSlotPropsOverrides {} +export interface AccordionHeadingSlotPropsOverrides {} export type AccordionSlotsAndSlotProps = CreateSlotsAndSlotProps< AccordionSlots, { + heading: SlotProps< + React.ElementType>, + AccordionHeadingSlotPropsOverrides, + AccordionOwnerState + >; transition: SlotProps< React.ElementType, AccordionTransitionSlotPropsOverrides, diff --git a/packages/mui-material/src/Accordion/Accordion.js b/packages/mui-material/src/Accordion/Accordion.js index a0dab71d213812..5f17646ae8c415 100644 --- a/packages/mui-material/src/Accordion/Accordion.js +++ b/packages/mui-material/src/Accordion/Accordion.js @@ -25,6 +25,7 @@ const useUtilityClasses = (ownerState) => { disabled && 'disabled', !disableGutters && 'gutters', ], + heading: ['heading'], region: ['region'], }; @@ -124,6 +125,14 @@ const AccordionRoot = styled(Paper, { }), ); +const AccordionHeading = styled('h3', { + name: 'MuiAccordion', + slot: 'Heading', + overridesResolver: (props, styles) => styles.heading, +})({ + all: 'unset', +}); + const Accordion = React.forwardRef(function Accordion(inProps, ref) { const props = useDefaultProps({ props: inProps, name: 'MuiAccordion' }); const { @@ -179,12 +188,21 @@ const Accordion = React.forwardRef(function Accordion(inProps, ref) { const backwardCompatibleSlots = { transition: TransitionComponentProp, ...slots }; const backwardCompatibleSlotProps = { transition: TransitionPropsProp, ...slotProps }; + const externalForwardedProps = { + slots: backwardCompatibleSlots, + slotProps: backwardCompatibleSlotProps, + }; + + const [AccordionHeadingSlot, accordionProps] = useSlot('heading', { + elementType: AccordionHeading, + externalForwardedProps, + className: classes.heading, + ownerState, + }); + const [TransitionSlot, transitionProps] = useSlot('transition', { elementType: Collapse, - externalForwardedProps: { - slots: backwardCompatibleSlots, - slotProps: backwardCompatibleSlotProps, - }, + externalForwardedProps, ownerState, }); @@ -196,7 +214,9 @@ const Accordion = React.forwardRef(function Accordion(inProps, ref) { square={square} {...other} > - {summary} + + {summary} +
{
); }; + +// slotProps type test. Changing heading level. + +
+; diff --git a/packages/mui-material/src/Accordion/Accordion.test.js b/packages/mui-material/src/Accordion/Accordion.test.js index c1d8f3889c73d8..815f8afba2bf5f 100644 --- a/packages/mui-material/src/Accordion/Accordion.test.js +++ b/packages/mui-material/src/Accordion/Accordion.test.js @@ -33,6 +33,10 @@ describe('', () => { transition: { testWithElement: null, }, + heading: { + testWithElement: 'h4', + expectedClassName: classes.heading, + }, }, skip: ['componentProp', 'componentsProp'], })); diff --git a/packages/mui-material/src/Accordion/accordionClasses.ts b/packages/mui-material/src/Accordion/accordionClasses.ts index d655634446b5ec..1ea5cbab5ef667 100644 --- a/packages/mui-material/src/Accordion/accordionClasses.ts +++ b/packages/mui-material/src/Accordion/accordionClasses.ts @@ -4,6 +4,8 @@ import generateUtilityClass from '@mui/utils/generateUtilityClass'; export interface AccordionClasses { /** Styles applied to the root element. */ root: string; + /** Styles applied to the heading element. */ + heading: string; /** Styles applied to the root element unless `square={true}`. */ rounded: string; /** State class applied to the root element if `expanded={true}`. */ @@ -24,6 +26,7 @@ export function getAccordionUtilityClass(slot: string): string { const accordionClasses: AccordionClasses = generateUtilityClasses('MuiAccordion', [ 'root', + 'heading', 'rounded', 'expanded', 'disabled',