diff --git a/docs/data/material/components/drawers/ResponsiveDrawer.js b/docs/data/material/components/drawers/ResponsiveDrawer.js index 19a30e76db165d..94a20757fba1db 100644 --- a/docs/data/material/components/drawers/ResponsiveDrawer.js +++ b/docs/data/material/components/drawers/ResponsiveDrawer.js @@ -111,13 +111,15 @@ function ResponsiveDrawer(props) { open={mobileOpen} onTransitionEnd={handleDrawerTransitionEnd} onClose={handleDrawerClose} - ModalProps={{ - keepMounted: true, // Better open performance on mobile. - }} sx={{ display: { xs: 'block', sm: 'none' }, '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, }} + slotProps={{ + root: { + keepMounted: true, // Better open performance on mobile. + }, + }} > {drawer} diff --git a/docs/data/material/components/drawers/ResponsiveDrawer.tsx b/docs/data/material/components/drawers/ResponsiveDrawer.tsx index 5daa81e4cba73b..abd4df47889566 100644 --- a/docs/data/material/components/drawers/ResponsiveDrawer.tsx +++ b/docs/data/material/components/drawers/ResponsiveDrawer.tsx @@ -118,13 +118,15 @@ export default function ResponsiveDrawer(props: Props) { open={mobileOpen} onTransitionEnd={handleDrawerTransitionEnd} onClose={handleDrawerClose} - ModalProps={{ - keepMounted: true, // Better open performance on mobile. - }} sx={{ display: { xs: 'block', sm: 'none' }, '& .MuiDrawer-paper': { boxSizing: 'border-box', width: drawerWidth }, }} + slotProps={{ + root: { + keepMounted: true, // Better open performance on mobile. + }, + }} > {drawer} diff --git a/docs/data/material/components/drawers/SwipeableEdgeDrawer.js b/docs/data/material/components/drawers/SwipeableEdgeDrawer.js index 9e2e08b8444d4f..f13650901db2b8 100644 --- a/docs/data/material/components/drawers/SwipeableEdgeDrawer.js +++ b/docs/data/material/components/drawers/SwipeableEdgeDrawer.js @@ -73,9 +73,7 @@ function SwipeableEdgeDrawer(props) { onOpen={toggleDrawer(true)} swipeAreaWidth={drawerBleeding} disableSwipeToOpen={false} - ModalProps={{ - keepMounted: true, - }} + keepMounted > +``` + +### BackdropProps + +The Drawer's `BackdropProps` prop was deprecated in favor of `slotProps.backdrop`: + +```diff + +``` + +### PaperProps + +The Drawer's `PaperProps` prop was deprecated in favor of `slotProps.paper`: + +```diff + +``` + +### SlideProps + +The Drawer's `SlideProps` prop was deprecated in favor of `slotProps.transition`: + +```diff + +``` + Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#drawer-classes) below to migrate the code as described in the following sections: ```bash @@ -1931,6 +1970,47 @@ The Slider's `componentsProps` prop was deprecated in favor of `slotProps`: /> ``` +## SwipeableDrawer + +Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#drawer-props) below to migrate the code as described in the following sections: + +```bash +npx @mui/codemod@latest deprecations/drawer-props +``` + +### BackdropProps + +The SwipeableDrawer's `BackdropProps` prop was deprecated in favor of `slotProps.backdrop`: + +```diff + +``` + +### PaperProps + +The SwipeableDrawer's `PaperProps` prop was deprecated in favor of `slotProps.paper`: + +```diff + +``` + +### SlideProps + +The SwipeableDrawer's `SlideProps` prop was deprecated in favor of `slotProps.transition`: + +```diff + +``` + ## ToggleButtonGroup Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#toggle-button-group-classes) below to migrate the code as described in the following sections: diff --git a/docs/pages/material-ui/api/drawer.json b/docs/pages/material-ui/api/drawer.json index 579de12405fdc3..e5e1feb6d1e8d5 100644 --- a/docs/pages/material-ui/api/drawer.json +++ b/docs/pages/material-ui/api/drawer.json @@ -20,8 +20,31 @@ } }, "open": { "type": { "name": "bool" }, "default": "false" }, - "PaperProps": { "type": { "name": "object" }, "default": "{}" }, - "SlideProps": { "type": { "name": "object" } }, + "PaperProps": { + "type": { "name": "object" }, + "default": "{}", + "deprecated": true, + "deprecationInfo": "use the slotProps.paper prop instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details." + }, + "SlideProps": { + "type": { "name": "object" }, + "deprecated": true, + "deprecationInfo": "use the slotProps.transition prop instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details." + }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ backdrop?: func
| object, docked?: func
| object, paper?: func
| object, root?: func
| object, transition?: func
| object }" + }, + "default": "{}" + }, + "slots": { + "type": { + "name": "shape", + "description": "{ backdrop?: elementType, docked?: elementType, paper?: elementType, root?: elementType, transition?: elementType }" + }, + "default": "{}" + }, "sx": { "type": { "name": "union", @@ -49,6 +72,38 @@ "import Drawer from '@mui/material/Drawer';", "import { Drawer } from '@mui/material';" ], + "slots": [ + { + "name": "root", + "description": "The component used for the root when the variant is `temporary`.", + "default": "Modal", + "class": "MuiDrawer-root" + }, + { + "name": "backdrop", + "description": "The component used for the Modal backdrop.", + "default": "Backdrop", + "class": null + }, + { + "name": "docked", + "description": "The component used for the root element when the variant is `permanent` or `persistent`.", + "default": "div", + "class": "MuiDrawer-docked" + }, + { + "name": "paper", + "description": "The component used for the paper.", + "default": "Paper", + "class": "MuiDrawer-paper" + }, + { + "name": "transition", + "description": "The component used for the transition.\n[Follow this guide](https://mui.com/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.", + "default": "Slide", + "class": null + } + ], "classes": [ { "key": "anchorBottom", @@ -74,24 +129,12 @@ "description": "Styles applied to the root element if `anchor=\"top\"`.", "isGlobal": false }, - { - "key": "docked", - "className": "MuiDrawer-docked", - "description": "Styles applied to the root element if `variant=\"permanent or persistent\"`.", - "isGlobal": false - }, { "key": "modal", "className": "MuiDrawer-modal", "description": "Styles applied to the Modal component.", "isGlobal": false }, - { - "key": "paper", - "className": "MuiDrawer-paper", - "description": "Styles applied to the Paper component.", - "isGlobal": false - }, { "key": "paperAnchorBottom", "className": "MuiDrawer-paperAnchorBottom", @@ -147,12 +190,6 @@ "description": "Styles applied to the Paper component if `anchor=\"top\"`.", "isGlobal": false, "isDeprecated": true - }, - { - "key": "root", - "className": "MuiDrawer-root", - "description": "Styles applied to the root element.", - "isGlobal": false } ], "spread": true, diff --git a/docs/pages/material-ui/api/swipeable-drawer.json b/docs/pages/material-ui/api/swipeable-drawer.json index 81cf64e11bf975..3ac376fc5d5dcd 100644 --- a/docs/pages/material-ui/api/swipeable-drawer.json +++ b/docs/pages/material-ui/api/swipeable-drawer.json @@ -30,7 +30,25 @@ "hysteresis": { "type": { "name": "number" }, "default": "0.52" }, "minFlingVelocity": { "type": { "name": "number" }, "default": "450" }, "open": { "type": { "name": "bool" }, "default": "false" }, - "SwipeAreaProps": { "type": { "name": "object" } }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ backdrop?: func
| object, docked?: func
| object, paper?: func
| object, root?: func
| object, swipeArea?: func
| object, transition?: func
| object }" + }, + "default": "{}" + }, + "slots": { + "type": { + "name": "shape", + "description": "{ backdrop?: elementType, docked?: elementType, paper?: elementType, root?: elementType, swipeArea?: elementType, transition?: elementType }" + }, + "default": "{}" + }, + "SwipeAreaProps": { + "type": { "name": "object" }, + "deprecated": true, + "deprecationInfo": "use the slotProps.swipeArea prop instead. This prop will be removed in v7. See Migrating from deprecated APIs for more details." + }, "swipeAreaWidth": { "type": { "name": "number" }, "default": "20" }, "transitionDuration": { "type": { @@ -45,6 +63,14 @@ "import SwipeableDrawer from '@mui/material/SwipeableDrawer';", "import { SwipeableDrawer } from '@mui/material';" ], + "slots": [ + { + "name": "swipeArea", + "description": "The component used for the swipeArea slot.", + "default": "div", + "class": null + } + ], "classes": [ { "key": "anchorBottom", diff --git a/docs/translations/api-docs/drawer/drawer.json b/docs/translations/api-docs/drawer/drawer.json index 3cc78791398f99..1360692763afc6 100644 --- a/docs/translations/api-docs/drawer/drawer.json +++ b/docs/translations/api-docs/drawer/drawer.json @@ -23,6 +23,8 @@ "SlideProps": { "description": "Props applied to the Slide element." }, + "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." }, @@ -52,19 +54,10 @@ "nodeName": "the root element", "conditions": "anchor=\"top\"" }, - "docked": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the root element", - "conditions": "variant=\"permanent or persistent\"" - }, "modal": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the Modal component" }, - "paper": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the Paper component" - }, "paperAnchorBottom": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the Paper component", @@ -112,7 +105,13 @@ "nodeName": "the Paper component", "conditions": "anchor=\"top\"", "deprecationInfo": "Combine the .MuiDrawer-anchorTop and .MuiDrawer-paper classes instead. How to migrate" - }, - "root": { "description": "Styles applied to the root element." } + } + }, + "slotDescriptions": { + "backdrop": "The component used for the Modal backdrop.", + "docked": "The component used for the root element when the variant is permanent or persistent.", + "paper": "The component used for the paper.", + "root": "The component used for the root when the variant is temporary.", + "transition": "The component used for the transition. Follow this guide to learn more about the requirements for this component." } } diff --git a/docs/translations/api-docs/swipeable-drawer/swipeable-drawer.json b/docs/translations/api-docs/swipeable-drawer/swipeable-drawer.json index cd2af8987c4ec8..461021c2cf5403 100644 --- a/docs/translations/api-docs/swipeable-drawer/swipeable-drawer.json +++ b/docs/translations/api-docs/swipeable-drawer/swipeable-drawer.json @@ -29,6 +29,8 @@ "typeDescriptions": { "event": "The event source of the callback." } }, "open": { "description": "If true, the component is shown." }, + "slotProps": { "description": "The props used for each slot inside." }, + "slots": { "description": "The components used for each slot inside." }, "SwipeAreaProps": { "description": "The element is used to intercept the touch events on the edge." }, @@ -122,5 +124,6 @@ "deprecationInfo": "Combine the .MuiDrawer-anchorTop and .MuiDrawer-paper classes instead. How to migrate" }, "root": { "description": "Styles applied to the root element." } - } + }, + "slotDescriptions": { "swipeArea": "The component used for the swipeArea slot." } } diff --git a/packages-internal/test-utils/src/describeConformance.tsx b/packages-internal/test-utils/src/describeConformance.tsx index 943c71e214fecd..cba4e019b43fd2 100644 --- a/packages-internal/test-utils/src/describeConformance.tsx +++ b/packages-internal/test-utils/src/describeConformance.tsx @@ -644,7 +644,7 @@ function testSlotPropsCallbackWithPropsAsOwnerState( const { queryByTestId } = await render( React.cloneElement(element, { slotProps, className: 'custom' }), ); - const slotComponent = queryByTestId('custom'); + const slotComponent = queryByTestId('custom', { exact: false }); expect(slotComponent).not.to.equal(null); }); }); diff --git a/packages/mui-codemod/src/deprecations/all/deprecations-all.js b/packages/mui-codemod/src/deprecations/all/deprecations-all.js index b0f9b4ffb22aa1..856799ffced9c0 100644 --- a/packages/mui-codemod/src/deprecations/all/deprecations-all.js +++ b/packages/mui-codemod/src/deprecations/all/deprecations-all.js @@ -38,6 +38,7 @@ import transformSnackbarProps from '../snackbar-props'; import transformSliderClasses from '../slider-classes'; import transformerTabsProps from '../tabs-props'; import transformerTabsClasses from '../tabs-classes'; +import transformDrawerProps from '../drawer-props'; /** * @param {import('jscodeshift').FileInfo} file @@ -84,6 +85,7 @@ export default function deprecationsAll(file, api, options) { file.source = transformSliderClasses(file, api, options); file.source = transformerTabsProps(file, api, options); file.source = transformerTabsClasses(file, api, options); + file.source = transformDrawerProps(file, api, options); return file.source; } diff --git a/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.js b/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.js new file mode 100644 index 00000000000000..e9e554a696745d --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.js @@ -0,0 +1,66 @@ +import movePropIntoSlots from '../utils/movePropIntoSlots'; +import movePropIntoSlotProps from '../utils/movePropIntoSlotProps'; + +/** + * @param {import('jscodeshift').FileInfo} file + * @param {import('jscodeshift').API} api + */ +export default function transformer(file, api, options) { + const j = api.jscodeshift; + const root = j(file.source); + const printOptions = options.printOptions; + + movePropIntoSlots(j, { + root, + componentName: 'Drawer', + propName: 'BackdropComponent', + slotName: 'backdrop', + }); + movePropIntoSlots(j, { + root, + componentName: 'SwipeableDrawer', + propName: 'BackdropComponent', + slotName: 'backdrop', + }); + + movePropIntoSlotProps(j, { + root, + componentName: 'Drawer', + propName: 'BackdropProps', + slotName: 'backdrop', + }); + movePropIntoSlotProps(j, { + root, + componentName: 'SwipeableDrawer', + propName: 'BackdropProps', + slotName: 'backdrop', + }); + + movePropIntoSlotProps(j, { + root, + componentName: 'Drawer', + propName: 'PaperProps', + slotName: 'paper', + }); + movePropIntoSlotProps(j, { + root, + componentName: 'SwipeableDrawer', + propName: 'PaperProps', + slotName: 'paper', + }); + + movePropIntoSlotProps(j, { + root, + componentName: 'Drawer', + propName: 'SlideProps', + slotName: 'transition', + }); + movePropIntoSlotProps(j, { + root, + componentName: 'SwipeableDrawer', + propName: 'SlideProps', + slotName: 'transition', + }); + + return root.toSource(printOptions); +} diff --git a/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.test.js b/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.test.js new file mode 100644 index 00000000000000..115755b40f1327 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/drawer-props.test.js @@ -0,0 +1,16 @@ +import { describeJscodeshiftTransform } from '../../../testUtils'; +import transform from './drawer-props'; + +describe('@mui/codemod', () => { + describe('deprecations', () => { + describeJscodeshiftTransform({ + transform, + transformName: 'drawer-props', + dirname: __dirname, + testCases: [ + { actual: '/test-cases/actual.js', expected: '/test-cases/expected.js' }, + { actual: '/test-cases/theme.actual.js', expected: '/test-cases/theme.expected.js' }, + ], + }); + }); +}); diff --git a/packages/mui-codemod/src/deprecations/drawer-props/index.js b/packages/mui-codemod/src/deprecations/drawer-props/index.js new file mode 100644 index 00000000000000..d56efd1114b23c --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/index.js @@ -0,0 +1 @@ +export { default } from './drawer-props'; diff --git a/packages/mui-codemod/src/deprecations/drawer-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/actual.js new file mode 100644 index 00000000000000..7c4cabf8988b82 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/actual.js @@ -0,0 +1,32 @@ +import Drawer from '@mui/material/Drawer'; +import SwipeableDrawer from '@mui/material/SwipeableDrawer'; +import { Drawer as MyDrawer, SwipeableDrawer as MySwipeableDrawer } from '@mui/material'; + +; +; + +; +; + +; +; + +; +; + +; diff --git a/packages/mui-codemod/src/deprecations/drawer-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/expected.js new file mode 100644 index 00000000000000..30d78eb29dfd30 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/expected.js @@ -0,0 +1,54 @@ +import Drawer from '@mui/material/Drawer'; +import SwipeableDrawer from '@mui/material/SwipeableDrawer'; +import { Drawer as MyDrawer, SwipeableDrawer as MySwipeableDrawer } from '@mui/material'; + +; +; + +; +; + +; +; + +; +; + +; diff --git a/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.actual.js new file mode 100644 index 00000000000000..6c934eb22d3fdf --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.actual.js @@ -0,0 +1,58 @@ +fn({ + MuiDrawer: { + defaultProps: { + BackdropComponent: Backdrop, + BackdropProps: { + transitionDuration: 300, + }, + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + BackdropComponent: Backdrop, + BackdropProps: { + transitionDuration: 300, + }, + }, + }, +}); + +fn({ + MuiDrawer: { + defaultProps: { + PaperProps: { + elevation: 20, + }, + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + PaperProps: { + elevation: 20, + }, + }, + }, +}); + +fn({ + MuiDrawer: { + defaultProps: { + SlideProps: { + direction: 'right', + }, + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + SlideProps: { + direction: 'right', + }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.expected.js new file mode 100644 index 00000000000000..293e56029b2130 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/drawer-props/test-cases/theme.expected.js @@ -0,0 +1,76 @@ +fn({ + MuiDrawer: { + defaultProps: { + slots: { + backdrop: Backdrop + }, + + slotProps: { + backdrop: { + transitionDuration: 300, + } + } + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + slots: { + backdrop: Backdrop + }, + + slotProps: { + backdrop: { + transitionDuration: 300, + } + } + }, + }, +}); + +fn({ + MuiDrawer: { + defaultProps: { + slotProps: { + paper: { + elevation: 20, + } + }, + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + slotProps: { + paper: { + elevation: 20, + } + }, + }, + }, +}); + +fn({ + MuiDrawer: { + defaultProps: { + slotProps: { + transition: { + direction: 'right', + } + }, + }, + }, +}); +fn({ + MuiSwipeableDrawer: { + defaultProps: { + slotProps: { + transition: { + direction: 'right', + } + }, + }, + }, +}); diff --git a/packages/mui-material/src/Drawer/Drawer.d.ts b/packages/mui-material/src/Drawer/Drawer.d.ts index eacf778a430786..3b7b02eda45d3b 100644 --- a/packages/mui-material/src/Drawer/Drawer.d.ts +++ b/packages/mui-material/src/Drawer/Drawer.d.ts @@ -1,13 +1,99 @@ import * as React from 'react'; import { SxProps } from '@mui/system'; import { InternalStandardProps as StandardProps, Theme } from '..'; +import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; import { ModalProps } from '../Modal'; +import { BackdropProps } from '../Backdrop'; import { SlideProps } from '../Slide'; import { PaperProps } from '../Paper'; import { TransitionProps } from '../transitions/transition'; import { DrawerClasses } from './drawerClasses'; -export interface DrawerProps extends StandardProps { +export interface DrawerRootSlotPropsOverrides {} + +export interface DrawerDockedSlotPropsOverrides {} + +export interface DrawerPaperSlotPropsOverrides {} + +export interface DrawerTransitionSlotPropsOverrides {} + +export interface DrawerBackdropSlotPropsOverrides {} + +export interface DrawerSlots { + /** + * The component used for the root when the variant is `temporary`. + * @default Modal + */ + root: React.ElementType; + /** + * The component used for the Modal backdrop. + * @default Backdrop + */ + backdrop: React.ElementType; + /** + * The component used for the root element when the variant is `permanent` or `persistent`. + * @default div + */ + docked: React.ElementType; + /** + * The component used for the paper. + * @default Paper + */ + paper: React.ElementType; + /** + * The component used for the transition. + * [Follow this guide](https://mui.com/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. + * @default Slide + */ + transition: React.ElementType; +} + +export type DrawerSlotsAndSlotProps = CreateSlotsAndSlotProps< + DrawerSlots, + { + /** + * Props forwarded to the root slot. + * By default, the avaible props are based on the [Modal](https://mui.com/material-ui/api/modal/#props) component. + */ + root: SlotProps, DrawerRootSlotPropsOverrides, DrawerOwnerState>; + /** + * Props forwarded to the backdrop slot. + * By default, the avaible props are based on the [Backdrop](https://mui.com/material-ui/api/backdrop/#props) component. + */ + backdrop: SlotProps< + React.ElementType, + DrawerBackdropSlotPropsOverrides, + DrawerOwnerState + >; + /** + * Props forwarded to the docked slot. + * By default, the avaible props are based on a div element. + */ + docked: SlotProps<'div', DrawerDockedSlotPropsOverrides, DrawerOwnerState>; + /** + * Props forwarded to the paper slot. + * By default, the avaible props are based on the [Paper](https://mui.com/material-ui/api/paper/#props) component. + */ + paper: SlotProps< + React.ElementType, + DrawerPaperSlotPropsOverrides, + DrawerOwnerState + >; + /** + * Props forwarded to the transition slot. + * By default, the avaible props are based on the [Slide](https://mui.com/material-ui/api/slide/#props) component. + */ + transition: SlotProps< + React.ElementType, + TransitionProps & DrawerTransitionSlotPropsOverrides, + DrawerOwnerState + >; + } +>; + +export interface DrawerProps + extends StandardProps, + DrawerSlotsAndSlotProps { /** * Side from which the drawer will appear. * @default 'left' @@ -46,11 +132,13 @@ export interface DrawerProps extends StandardProps>; /** * Props applied to the [`Slide`](https://mui.com/material-ui/api/slide/) element. + * @deprecated use the `slotProps.transition` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details. */ SlideProps?: Partial; /** @@ -73,6 +161,9 @@ export interface DrawerProps extends StandardProps {} + /** * The props of the [Modal](https://next.mui.com/material-ui/api/modal/) component are available * when `variant="temporary"` is set. diff --git a/packages/mui-material/src/Drawer/Drawer.js b/packages/mui-material/src/Drawer/Drawer.js index 99822d95498628..da11f1d1ae6534 100644 --- a/packages/mui-material/src/Drawer/Drawer.js +++ b/packages/mui-material/src/Drawer/Drawer.js @@ -14,6 +14,8 @@ import { styled, useTheme } from '../zero-styled'; import memoTheme from '../utils/memoTheme'; import { useDefaultProps } from '../DefaultPropsProvider'; import { getDrawerUtilityClass } from './drawerClasses'; +import useSlot from '../utils/useSlot'; +import { mergeSlotProps } from '../utils'; const overridesResolver = (props, styles) => { const { ownerState } = props; @@ -207,9 +209,11 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) { PaperProps = {}, SlideProps, // eslint-disable-next-line react/prop-types - TransitionComponent = Slide, + TransitionComponent, transitionDuration = defaultTransitionDuration, variant = 'temporary', + slots = {}, + slotProps = {}, ...other } = props; @@ -235,76 +239,92 @@ const Drawer = React.forwardRef(function Drawer(inProps, ref) { const classes = useUtilityClasses(ownerState); - const drawer = ( - - {children} - - ); + const externalForwardedProps = { + slots: { + transition: TransitionComponent, + ...slots, + }, + slotProps: { + paper: PaperProps, + transition: SlideProps, + ...slotProps, + backdrop: mergeSlotProps(slotProps.backdrop || { ...BackdropProps, ...BackdropPropsProp }, { + transitionDuration, + }), + }, + }; + + const [RootSlot, rootSlotProps] = useSlot('root', { + ref, + elementType: DrawerRoot, + className: clsx(classes.root, classes.modal, className), + shouldForwardComponentProp: true, + ownerState, + externalForwardedProps: { + ...externalForwardedProps, + ...other, + ...ModalProps, + }, + additionalProps: { + open, + onClose, + hideBackdrop, + slots: { + backdrop: externalForwardedProps.slots.backdrop, + }, + slotProps: { + backdrop: externalForwardedProps.slotProps.backdrop, + }, + }, + }); + + const [PaperSlot, paperSlotProps] = useSlot('paper', { + elementType: DrawerPaper, + shouldForwardComponentProp: true, + className: clsx(classes.paper, PaperProps.className), + ownerState, + externalForwardedProps, + additionalProps: { + elevation: variant === 'temporary' ? elevation : 0, + square: true, + }, + }); + + const [DockedSlot, dockedSlotProps] = useSlot('docked', { + elementType: DrawerDockedRoot, + ref, + className: clsx(classes.root, classes.docked, className), + ownerState, + externalForwardedProps, + additionalProps: other, // pass `other` here because `DockedSlot` is also a root slot for some variants + }); + + const [TransitionSlot, transitionSlotProps] = useSlot('transition', { + elementType: Slide, + ownerState, + externalForwardedProps, + additionalProps: { + in: open, + direction: oppositeDirection[anchorInvariant], + timeout: transitionDuration, + appear: mounted.current, + }, + }); + + const drawer = {children}; if (variant === 'permanent') { - return ( - - {drawer} - - ); + return {drawer}; } - const slidingDrawer = ( - - {drawer} - - ); + const slidingDrawer = {drawer}; if (variant === 'persistent') { - return ( - - {slidingDrawer} - - ); + return {slidingDrawer}; } // variant === temporary - return ( - - {slidingDrawer} - - ); + return {slidingDrawer}; }); Drawer.propTypes /* remove-proptypes */ = { @@ -363,13 +383,37 @@ Drawer.propTypes /* remove-proptypes */ = { open: PropTypes.bool, /** * Props applied to the [`Paper`](https://mui.com/material-ui/api/paper/) element. + * @deprecated use the `slotProps.paper` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details. * @default {} */ PaperProps: PropTypes.object, /** * Props applied to the [`Slide`](https://mui.com/material-ui/api/slide/) element. + * @deprecated use the `slotProps.transition` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details. */ SlideProps: PropTypes.object, + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + backdrop: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + docked: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + transition: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + backdrop: PropTypes.elementType, + docked: PropTypes.elementType, + paper: PropTypes.elementType, + root: PropTypes.elementType, + transition: PropTypes.elementType, + }), /** * The system prop that allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/mui-material/src/Drawer/Drawer.spec.tsx b/packages/mui-material/src/Drawer/Drawer.spec.tsx index 8c1fef49349092..5cf479fe793988 100644 --- a/packages/mui-material/src/Drawer/Drawer.spec.tsx +++ b/packages/mui-material/src/Drawer/Drawer.spec.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { expectType } from '@mui/types'; -import Drawer from '@mui/material/Drawer'; +import Drawer, { DrawerProps } from '@mui/material/Drawer'; +import Grow from '@mui/material/Grow'; import { PaperProps } from '@mui/material/Paper'; const paperProps: PaperProps<'span'> = { @@ -17,3 +18,62 @@ function Test() { ); } + +; + +function Noop() { + return null; +} +; + +function Custom(props: DrawerProps) { + const { slotProps, ...dialogProps } = props; + return ( + { + const transitionProps = + typeof slotProps?.transition === 'function' + ? slotProps.transition(ownerState) + : slotProps?.transition; + return { + ...transitionProps, + onExited: (node) => { + transitionProps?.onExited?.(node); + }, + }; + }, + }} + {...dialogProps} + > + test + + ); +} diff --git a/packages/mui-material/src/Drawer/Drawer.test.js b/packages/mui-material/src/Drawer/Drawer.test.js index b19220b1ef67bf..347d57cbea8108 100644 --- a/packages/mui-material/src/Drawer/Drawer.test.js +++ b/packages/mui-material/src/Drawer/Drawer.test.js @@ -4,12 +4,31 @@ import { spy } from 'sinon'; import { createRenderer, screen } from '@mui/internal-test-utils'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import Drawer, { drawerClasses as classes } from '@mui/material/Drawer'; +import { modalClasses } from '@mui/material/Modal'; import { getAnchor, isHorizontal } from './Drawer'; import describeConformance from '../../test/describeConformance'; describe('', () => { const { clock, render } = createRenderer({ clock: 'fake' }); + const CustomPaper = React.forwardRef( + ({ className, children, ownerState, square, ...props }, ref) => ( + + {children} + + ), + ); + + const CustomBackdrop = React.forwardRef(({ transitionDuration, ownerState, ...props }, ref) => ( + + )); + + const CustomTransition = React.forwardRef( + ({ onEnter, onExit, onExited, appear, in: inProp, ownerState, ...props }, ref) => ( + + ), + ); + describeConformance(
@@ -19,13 +38,51 @@ describe('', () => { inheritComponent: 'div', render, muiName: 'MuiDrawer', - testVariantProps: { variant: 'persistent' }, testDeepOverrides: { slotName: 'paper', slotClassName: classes.paper }, refInstanceof: window.HTMLDivElement, + slots: { + root: { + expectedClassName: classes.root, + testWithComponent: null, + testWithElement: null, + }, + paper: { + expectedClassName: classes.paper, + testWithComponent: CustomPaper, + testWithElement: null, // already tested with CustomPaper + }, + backdrop: { expectedClassName: modalClasses.backdrop, testWithElement: CustomBackdrop }, + transition: { + expectedClassName: null, + testWithComponent: CustomTransition, + testWithElement: CustomTransition, + }, + }, skip: ['componentProp', 'componentsProp', 'themeVariants'], }), ); + // For `permanent` variant, the root is a div instead of a Modal. + describeConformance( + +
+ , + () => ({ + classes, + inheritComponent: 'div', + render, + muiName: 'MuiDrawer', + testVariantProps: { variant: 'persistent' }, + refInstanceof: window.HTMLDivElement, + slots: { + docked: { + expectedClassName: classes.docked, + }, + }, + skip: ['componentProp', 'componentsProp'], + }), + ); + describe('prop: variant=temporary', () => { describe('transitionDuration property', () => { const transitionDuration = { diff --git a/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.d.ts b/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.d.ts index e5a57d97e6e21a..c64601bb8ffca4 100644 --- a/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.d.ts +++ b/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.d.ts @@ -1,7 +1,32 @@ import * as React from 'react'; -import { DrawerProps } from '../Drawer'; +import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; +import { DrawerProps, DrawerOwnerState, DrawerSlotsAndSlotProps } from '../Drawer'; -export interface SwipeableDrawerProps extends Omit { +export interface SwipeableDrawerSwipeAreaSlotPropsOverrides {} + +export interface SwipeableDrawerSlots { + /** + * The component used for the swipeArea slot. + * @default div + */ + swipeArea?: React.ElementType; +} + +type SwipeableDrawerSlotsAndSlotProps = DrawerSlotsAndSlotProps & + CreateSlotsAndSlotProps< + SwipeableDrawerSlots, + { + /** + * Props forwarded to the docked slot. + * By default, the avaible props are based on a div element. + */ + swipeArea: SlotProps<'div', SwipeableDrawerSwipeAreaSlotPropsOverrides, DrawerOwnerState>; + } + >; + +export interface SwipeableDrawerProps + extends Omit, + SwipeableDrawerSlotsAndSlotProps { /** * If set to true, the swipe event will open the drawer even if the user begins the swipe on one of the drawer's children. * This can be useful in scenarios where the drawer is partially visible. @@ -67,6 +92,7 @@ export interface SwipeableDrawerProps extends Omit {!disableSwipeToOpen && variant === 'temporary' && ( - + )} @@ -723,8 +745,33 @@ SwipeableDrawer.propTypes /* remove-proptypes */ = { component: elementTypeAcceptingRef, style: PropTypes.object, }), + /** + * The props used for each slot inside. + * @default {} + */ + slotProps: PropTypes.shape({ + backdrop: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + docked: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + swipeArea: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + transition: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + }), + /** + * The components used for each slot inside. + * @default {} + */ + slots: PropTypes.shape({ + backdrop: PropTypes.elementType, + docked: PropTypes.elementType, + paper: PropTypes.elementType, + root: PropTypes.elementType, + swipeArea: PropTypes.elementType, + transition: PropTypes.elementType, + }), /** * The element is used to intercept the touch events on the edge. + * @deprecated use the `slotProps.swipeArea` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details. */ SwipeAreaProps: PropTypes.object, /** diff --git a/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.test.js b/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.test.js index f3b0cd3fd57e65..21d49bb9bc010a 100644 --- a/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.test.js +++ b/packages/mui-material/src/SwipeableDrawer/SwipeableDrawer.test.js @@ -87,7 +87,9 @@ describe('', () => { onOpen={() => {}} onClose={() => {}} open - PaperProps={{ 'data-test': 'foo' }} + slotProps={{ + paper: { 'data-test': 'foo' }, + }} />, ); @@ -176,7 +178,9 @@ describe('', () => { onOpen={handleOpen} onClose={handleClose} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -234,8 +238,10 @@ describe('', () => { onOpen={handleOpen} onClose={handleClose} open={false} - PaperProps={{ component: FakePaper }} transitionDuration={0} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -304,7 +310,9 @@ describe('', () => { onOpen={handleOpen} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -335,7 +343,9 @@ describe('', () => { onOpen={() => {}} onClose={handleClose} open - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -366,7 +376,9 @@ describe('', () => { onOpen={handleOpen} onClose={handleClose} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -398,7 +410,9 @@ describe('', () => { onOpen={handleOpen} onClose={handleClose} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -427,7 +441,9 @@ describe('', () => { onOpen={() => {}} onClose={handleClose} open - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -455,7 +471,9 @@ describe('', () => { onOpen={() => {}} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -482,7 +500,9 @@ describe('', () => { onOpen={() => {}} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -515,19 +535,21 @@ describe('', () => { onClose={() => {}} open={false} swipeAreaWidth={20} - SwipeAreaProps={{ - style: { - // ensure clicks will not be grabbed by swipe area to ensure testing just this functionality - pointerEvents: 'none', - }, - }} - PaperProps={{ component: FakePaper }} ModalProps={{ keepMounted: true, sx: { transform: `translateY(${handleHeight}px) !important`, }, }} + slotProps={{ + paper: { component: FakePaper }, + swipeArea: { + style: { + // ensure clicks will not be grabbed by swipe area to ensure testing just this functionality + pointerEvents: 'none', + }, + }, + }} >
', () => { onClose={() => {}} open={false} swipeAreaWidth={20} - SwipeAreaProps={{ - style: { - // ensure clicks will not be grabbed by swipe area to ensure testing just this functionality - pointerEvents: 'none', - }, - }} - PaperProps={{ component: FakePaper }} ModalProps={{ keepMounted: true, sx: { @@ -601,6 +616,16 @@ describe('', () => { }, }, }} + slotProps={{ + root: {}, + paper: { component: FakePaper }, + swipeArea: { + style: { + // ensure clicks will not be grabbed by swipe area to ensure testing just this functionality + pointerEvents: 'none', + }, + }, + }} >
', () => { it('should be able to attach paper ref passed through PaperProps', () => { const ref = React.createRef(); render( - {}} onClose={() => {}} PaperProps={{ ref }} open> + {}} + onClose={() => {}} + open + slotProps={{ + paper: { ref }, + }} + >
, ); @@ -689,7 +721,9 @@ describe('', () => { onOpen={handleOpen} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} + slotProps={{ + paper: { component: FakePaper }, + }} >
SwipeableDrawer
, @@ -718,7 +752,9 @@ describe('', () => { onOpen={() => {}} onClose={handleClose} open - PaperProps={{ component: FakePaper, 'data-testid': 'paper' }} + slotProps={{ + paper: { component: FakePaper, 'data-testid': 'paper' }, + }} >
SwipeableDrawer
, @@ -750,8 +786,10 @@ describe('', () => { onOpen={handleOpen} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} - SwipeAreaProps={{ 'data-testid': 'swipearea' }} + slotProps={{ + paper: { component: FakePaper }, + swipeArea: { 'data-testid': 'swipearea' }, + }} >
Drawer1
@@ -759,8 +797,10 @@ describe('', () => { onOpen={handleOpen} onClose={() => {}} open={false} - PaperProps={{ component: FakePaper }} - SwipeAreaProps={{ 'data-testid': 'swipearea' }} + slotProps={{ + paper: { component: FakePaper }, + swipeArea: { 'data-testid': 'swipearea' }, + }} >
Drawer2
@@ -794,8 +834,10 @@ describe('', () => { onOpen={() => {}} onClose={() => {}} open={false} - PaperProps={{ component: NullPaper }} - SwipeAreaProps={{ 'data-testid': 'swipearea' }} + slotProps={{ + paper: { component: NullPaper }, + swipeArea: { 'data-testid': 'swipearea' }, + }} >
SwipeableDrawer
,