diff --git a/docs/pages/material-ui/api/collapse.json b/docs/pages/material-ui/api/collapse.json index 88adc7e4f91873..539424730bef55 100644 --- a/docs/pages/material-ui/api/collapse.json +++ b/docs/pages/material-ui/api/collapse.json @@ -19,6 +19,20 @@ "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, "default": "'vertical'" }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ root?: func
| object, wrapper?: func
| object, wrapperInner?: func
| object }" + }, + "default": "{}" + }, + "slots": { + "type": { + "name": "shape", + "description": "{ root?: elementType, wrapper?: elementType, wrapperInner?: elementType }" + }, + "default": "{}" + }, "sx": { "type": { "name": "union", @@ -39,6 +53,26 @@ "import Collapse from '@mui/material/Collapse';", "import { Collapse } from '@mui/material';" ], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": "MuiCollapse-root" + }, + { + "name": "wrapper", + "description": "The component that renders the wrapper.", + "default": "'div'", + "class": "MuiCollapse-wrapper" + }, + { + "name": "wrapperInner", + "description": "The component that renders the inner wrapper.", + "default": "'div'", + "class": "MuiCollapse-wrapperInner" + } + ], "classes": [ { "key": "entered", @@ -57,24 +91,6 @@ "className": "MuiCollapse-horizontal", "description": "State class applied to the root element if `orientation=\"horizontal\"`.", "isGlobal": false - }, - { - "key": "root", - "className": "MuiCollapse-root", - "description": "Styles applied to the root element.", - "isGlobal": false - }, - { - "key": "wrapper", - "className": "MuiCollapse-wrapper", - "description": "Styles applied to the outer wrapper element.", - "isGlobal": false - }, - { - "key": "wrapperInner", - "className": "MuiCollapse-wrapperInner", - "description": "Styles applied to the inner wrapper element.", - "isGlobal": false } ], "spread": true, diff --git a/docs/translations/api-docs/collapse/collapse.json b/docs/translations/api-docs/collapse/collapse.json index f3bcd44dc8b552..3cee03c5880cba 100644 --- a/docs/translations/api-docs/collapse/collapse.json +++ b/docs/translations/api-docs/collapse/collapse.json @@ -18,6 +18,8 @@ }, "in": { "description": "If true, the component will transition in." }, "orientation": { "description": "The transition orientation." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, "sx": { "description": "The system prop that allows defining system overrides as well as additional CSS styles." }, @@ -40,15 +42,11 @@ "description": "State class applied to {{nodeName}} if {{conditions}}.", "nodeName": "the root element", "conditions": "orientation=\"horizontal\"" - }, - "root": { "description": "Styles applied to the root element." }, - "wrapper": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the outer wrapper element" - }, - "wrapperInner": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the inner wrapper element" } + }, + "slotDescriptions": { + "root": "The component that renders the root.", + "wrapper": "The component that renders the wrapper.", + "wrapperInner": "The component that renders the inner wrapper." } } diff --git a/packages/mui-material/src/Collapse/Collapse.d.ts b/packages/mui-material/src/Collapse/Collapse.d.ts index bdab394f7fa3fd..46f3ae39a62cc8 100644 --- a/packages/mui-material/src/Collapse/Collapse.d.ts +++ b/packages/mui-material/src/Collapse/Collapse.d.ts @@ -1,11 +1,48 @@ import * as React from 'react'; import { SxProps } from '@mui/system'; +import { TransitionStatus } from 'react-transition-group'; import { Theme } from '../styles'; import { InternalStandardProps as StandardProps } from '../internal'; import { TransitionProps } from '../transitions/transition'; import { CollapseClasses } from './collapseClasses'; +import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; -export interface CollapseProps extends StandardProps { +export interface CollapseSlots { + /** + * The component that renders the root. + * @default 'div' + */ + root?: React.ElementType; + /** + * The component that renders the wrapper. + * @default 'div' + */ + wrapper?: React.ElementType; + /** + * The component that renders the inner wrapper. + * @default 'div' + */ + wrapperInner?: React.ElementType; +} + +export interface CollapseRootSlotPropsOverrides {} + +export interface CollapseWrapperSlotPropsOverrides {} + +export interface CollapseWrapperInnerSlotPropsOverrides {} + +export type CollapseSlotsAndSlotProps = CreateSlotsAndSlotProps< + CollapseSlots, + { + root: SlotProps<'div', CollapseRootSlotPropsOverrides, CollapseOwnerState>; + wrapper: SlotProps<'div', CollapseWrapperSlotPropsOverrides, CollapseOwnerState>; + wrapperInner: SlotProps<'div', CollapseWrapperInnerSlotPropsOverrides, CollapseOwnerState>; + } +>; + +export interface CollapseProps + extends StandardProps, + CollapseSlotsAndSlotProps { /** * The content node to be collapsed. */ @@ -53,6 +90,10 @@ export interface CollapseProps extends StandardProps sx?: SxProps; } +export interface CollapseOwnerState extends CollapseProps { + state: TransitionStatus; +} + /** * The Collapse transition is used by the * [Vertical Stepper](https://mui.com/material-ui/react-stepper/#vertical-stepper) StepContent component. diff --git a/packages/mui-material/src/Collapse/Collapse.js b/packages/mui-material/src/Collapse/Collapse.js index d401f5fd822f5c..d2766ab7263a3e 100644 --- a/packages/mui-material/src/Collapse/Collapse.js +++ b/packages/mui-material/src/Collapse/Collapse.js @@ -12,6 +12,7 @@ import { useDefaultProps } from '../DefaultPropsProvider'; import { duration } from '../styles/createTransitions'; import { getTransitionProps } from '../transitions/utils'; import { useForkRef } from '../utils'; +import useSlot from '../utils/useSlot'; import { getCollapseUtilityClass } from './collapseClasses'; const useUtilityClasses = (ownerState) => { @@ -149,6 +150,8 @@ const Collapse = React.forwardRef(function Collapse(inProps, ref) { onExited, onExiting, orientation = 'vertical', + slots = {}, + slotProps = {}, style, timeout = duration.standard, // eslint-disable-next-line react/prop-types @@ -292,6 +295,41 @@ const Collapse = React.forwardRef(function Collapse(inProps, ref) { } }; + const externalForwardedProps = { + slots, + slotProps, + component, + }; + + const [RootSlot, rootSlotProps] = useSlot('root', { + ref: handleRef, + className: clsx(classes.root, className), + elementType: CollapseRoot, + externalForwardedProps, + ownerState, + additionalProps: { + style: { + [isHorizontal ? 'minWidth' : 'minHeight']: collapsedSize, + ...style, + }, + }, + }); + + const [WrapperSlot, wrapperSlotProps] = useSlot('wrapper', { + ref: wrapperRef, + className: classes.wrapper, + elementType: CollapseWrapper, + externalForwardedProps, + ownerState, + }); + + const [WrapperInnerSlot, wrapperInnerSlotProps] = useSlot('wrapperInner', { + className: classes.wrapperInner, + elementType: CollapseWrapperInner, + externalForwardedProps, + ownerState, + }); + return ( {/* Destructure child props to prevent the component's "ownerState" from being overridden by incomingOwnerState. */} - {(state, { ownerState: incomingOwnerState, ...restChildProps }) => ( - { + const stateOwnerState = { ...ownerState, state }; + return ( + - - - {children} - - - - )} + + + {children} + + + + ); + }} ); }); @@ -421,6 +446,24 @@ Collapse.propTypes /* remove-proptypes */ = { * @default 'vertical' */ orientation: PropTypes.oneOf(['horizontal', 'vertical']), + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + wrapper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + wrapperInner: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + root: PropTypes.elementType, + wrapper: PropTypes.elementType, + wrapperInner: PropTypes.elementType, + }), /** * @ignore */ diff --git a/packages/mui-material/src/Collapse/Collapse.test.js b/packages/mui-material/src/Collapse/Collapse.test.js index 24896c949a6a48..d46b25e9cc251b 100644 --- a/packages/mui-material/src/Collapse/Collapse.test.js +++ b/packages/mui-material/src/Collapse/Collapse.test.js @@ -1,3 +1,4 @@ +import * as React from 'react'; import { expect } from 'chai'; import { spy, stub } from 'sinon'; import { act, createRenderer } from '@mui/internal-test-utils'; @@ -6,6 +7,16 @@ import { ThemeProvider, createTheme } from '@mui/material/styles'; import Collapse, { collapseClasses as classes } from '@mui/material/Collapse'; import describeConformance from '../../test/describeConformance'; +const CustomCollapse = React.forwardRef(({ ownerState, ...props }, ref) => ( +
+)); +const CustomWrapper = React.forwardRef(({ ownerState, ...props }, ref) => ( +
+)); +const CustomWrapperInner = React.forwardRef(({ ownerState, ...props }, ref) => ( +
+)); + describe('', () => { const { clock, render } = createRenderer(); @@ -22,6 +33,14 @@ describe('', () => { muiName: 'MuiCollapse', testVariantProps: { orientation: 'horizontal' }, testDeepOverrides: { slotName: 'wrapper', slotClassName: classes.wrapper }, + slots: { + root: { expectedClassName: classes.root, testWithElement: CustomCollapse }, + wrapper: { expectedClassName: classes.wrapper, testWithElement: CustomWrapper }, + wrapperInner: { + expectedClassName: classes.wrapperInner, + testWithElement: CustomWrapperInner, + }, + }, skip: ['componentsProp'], }));