Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 34 additions & 18 deletions docs/pages/material-ui/api/collapse.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
"type": { "name": "enum", "description": "'horizontal'<br>&#124;&nbsp;'vertical'" },
"default": "'vertical'"
},
"slotProps": {
"type": {
"name": "shape",
"description": "{ root?: func<br>&#124;&nbsp;object, wrapper?: func<br>&#124;&nbsp;object, wrapperInner?: func<br>&#124;&nbsp;object }"
},
"default": "{}"
},
"slots": {
"type": {
"name": "shape",
"description": "{ root?: elementType, wrapper?: elementType, wrapperInner?: elementType }"
},
"default": "{}"
},
"sx": {
"type": {
"name": "union",
Expand All @@ -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",
Expand All @@ -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,
Expand Down
16 changes: 7 additions & 9 deletions docs/translations/api-docs/collapse/collapse.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
},
"in": { "description": "If <code>true</code>, 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."
},
Expand All @@ -40,15 +42,11 @@
"description": "State class applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
"conditions": "<code>orientation=\"horizontal\"</code>"
},
"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."
}
}
43 changes: 42 additions & 1 deletion packages/mui-material/src/Collapse/Collapse.d.ts
Original file line number Diff line number Diff line change
@@ -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<TransitionProps, 'timeout'> {
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<TransitionProps, 'timeout'>,
CollapseSlotsAndSlotProps {
/**
* The content node to be collapsed.
*/
Expand Down Expand Up @@ -53,6 +90,10 @@ export interface CollapseProps extends StandardProps<TransitionProps, 'timeout'>
sx?: SxProps<Theme>;
}

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.
Expand Down
103 changes: 73 additions & 30 deletions packages/mui-material/src/Collapse/Collapse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 (
<TransitionComponent
in={inProp}
Expand All @@ -307,39 +345,26 @@ const Collapse = React.forwardRef(function Collapse(inProps, ref) {
{...other}
>
{/* Destructure child props to prevent the component's "ownerState" from being overridden by incomingOwnerState. */}
{(state, { ownerState: incomingOwnerState, ...restChildProps }) => (
<CollapseRoot
as={component}
className={clsx(
classes.root,
{
{(state, { ownerState: incomingOwnerState, ...restChildProps }) => {
const stateOwnerState = { ...ownerState, state };
return (
<RootSlot
{...rootSlotProps}
className={clsx(rootSlotProps.className, {
[classes.entered]: state === 'entered',
[classes.hidden]: state === 'exited' && !inProp && collapsedSize === '0px',
},
className,
)}
style={{
[isHorizontal ? 'minWidth' : 'minHeight']: collapsedSize,
...style,
}}
ref={handleRef}
ownerState={{ ...ownerState, state }}
{...restChildProps}
>
<CollapseWrapper
ownerState={{ ...ownerState, state }}
className={classes.wrapper}
ref={wrapperRef}
})}
ownerState={stateOwnerState}
{...restChildProps}
>
<CollapseWrapperInner
ownerState={{ ...ownerState, state }}
className={classes.wrapperInner}
>
{children}
</CollapseWrapperInner>
</CollapseWrapper>
</CollapseRoot>
)}
<WrapperSlot {...wrapperSlotProps} ownerState={stateOwnerState}>
<WrapperInnerSlot {...wrapperInnerSlotProps} ownerState={stateOwnerState}>
{children}
</WrapperInnerSlot>
</WrapperSlot>
</RootSlot>
);
}}
</TransitionComponent>
);
});
Expand Down Expand Up @@ -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
*/
Expand Down
19 changes: 19 additions & 0 deletions packages/mui-material/src/Collapse/Collapse.test.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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) => (
<div ref={ref} {...props} />
));
const CustomWrapper = React.forwardRef(({ ownerState, ...props }, ref) => (
<div ref={ref} {...props} />
));
const CustomWrapperInner = React.forwardRef(({ ownerState, ...props }, ref) => (
<div ref={ref} {...props} />
));

describe('<Collapse />', () => {
const { clock, render } = createRenderer();

Expand All @@ -22,6 +33,14 @@ describe('<Collapse />', () => {
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'],
}));

Expand Down
Loading