diff --git a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md index 40e3b9d2f1ac53..c70fd914bb4d5d 100644 --- a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md +++ b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md @@ -1093,3 +1093,32 @@ Here's how to migrate: }, }, ``` + +## SpeedDial + +Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#speed-dial-props) below to migrate the code as described in the following sections: + +```bash +npx @mui/codemod@next deprecations/speed-dial-props +``` + +### TransitionComponent + +The SpeedDial's `TransitionComponent` prop was deprecated in favor of `slots.transition`: + +```diff + +``` diff --git a/docs/pages/material-ui/api/speed-dial.json b/docs/pages/material-ui/api/speed-dial.json index fea2a75021da29..e5ac729f1ff19f 100644 --- a/docs/pages/material-ui/api/speed-dial.json +++ b/docs/pages/material-ui/api/speed-dial.json @@ -29,6 +29,14 @@ }, "open": { "type": { "name": "bool" } }, "openIcon": { "type": { "name": "node" } }, + "slotProps": { + "type": { "name": "shape", "description": "{ transition?: func
| object }" }, + "default": "{}" + }, + "slots": { + "type": { "name": "shape", "description": "{ transition?: elementType }" }, + "default": "{}" + }, "sx": { "type": { "name": "union", @@ -36,7 +44,12 @@ }, "additionalInfo": { "sx": true } }, - "TransitionComponent": { "type": { "name": "elementType" }, "default": "Zoom" }, + "TransitionComponent": { + "type": { "name": "elementType" }, + "default": "Zoom\n* @deprecated Use `slots.transition` instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/)", + "deprecated": true, + "deprecationInfo": "Use slots.transition instead. This prop will be removed in v7. How to migrate" + }, "transitionDuration": { "type": { "name": "union", @@ -44,13 +57,25 @@ }, "default": "{\n enter: theme.transitions.duration.enteringScreen,\n exit: theme.transitions.duration.leavingScreen,\n}" }, - "TransitionProps": { "type": { "name": "object" } } + "TransitionProps": { + "type": { "name": "object" }, + "deprecated": true, + "deprecationInfo": "Use slotProps.transition instead. This prop will be removed in v7. How to migrate" + } }, "name": "SpeedDial", "imports": [ "import SpeedDial from '@mui/material/SpeedDial';", "import { SpeedDial } from '@mui/material';" ], + "slots": [ + { + "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.", + "default": "{}", + "class": null + } + ], "classes": [ { "key": "actions", diff --git a/docs/translations/api-docs/speed-dial/speed-dial.json b/docs/translations/api-docs/speed-dial/speed-dial.json index 878f9981ebe621..361d0b25fb3df5 100644 --- a/docs/translations/api-docs/speed-dial/speed-dial.json +++ b/docs/translations/api-docs/speed-dial/speed-dial.json @@ -36,6 +36,8 @@ "openIcon": { "description": "The icon to display in the SpeedDial Fab when the SpeedDial is open." }, + "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." }, @@ -73,5 +75,8 @@ }, "fab": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the Fab component" }, "root": { "description": "Styles applied to the root element." } + }, + "slotDescriptions": { + "transition": "The component that renders the transition. Follow this guide to learn more about the requirements for this component." } } diff --git a/packages/mui-codemod/src/deprecations/all/deprecations-all.js b/packages/mui-codemod/src/deprecations/all/deprecations-all.js index 3bddd0595b93e3..dc4f471e69ac48 100644 --- a/packages/mui-codemod/src/deprecations/all/deprecations-all.js +++ b/packages/mui-codemod/src/deprecations/all/deprecations-all.js @@ -12,6 +12,7 @@ import transformToggleButtonGroupClasses from '../toggle-button-group-classes'; import transformStepLabelProps from '../step-label-props'; import transformBackdropProps from '../backdrop-props'; import transformStepConnectorClasses from '../step-connector-classes'; +import transformSpeedDialProps from '../speed-dial-props'; /** * @param {import('jscodeshift').FileInfo} file @@ -32,6 +33,7 @@ export default function deprecationsAll(file, api, options) { file.source = transformStepLabelProps(file, api, options); file.source = transformBackdropProps(file, api, options); file.source = transformStepConnectorClasses(file, api, options); + file.source = transformSpeedDialProps(file, api, options); return file.source; } diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/index.js b/packages/mui-codemod/src/deprecations/speed-dial-props/index.js new file mode 100644 index 00000000000000..d4a6ee9d390685 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/index.js @@ -0,0 +1 @@ +export { default } from './speed-dial-props'; diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.js b/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.js new file mode 100644 index 00000000000000..879a27a59c80b5 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.js @@ -0,0 +1,28 @@ +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: 'SpeedDial', + propName: 'TransitionComponent', + slotName: 'transition', + }); + + movePropIntoSlotProps(j, { + root, + componentName: 'SpeedDial', + propName: 'TransitionProps', + slotName: 'transition', + }); + + return root.toSource(printOptions); +} diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.test.js b/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.test.js new file mode 100644 index 00000000000000..bc31494da93870 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/speed-dial-props.test.js @@ -0,0 +1,53 @@ +import path from 'path'; +import { expect } from 'chai'; +import { jscodeshift } from '../../../testUtils'; +import transform from './speed-dial-props'; +import readFile from '../../util/readFile'; + +function read(fileName) { + return readFile(path.join(__dirname, fileName)); +} + +describe('@mui/codemod', () => { + describe('deprecations', () => { + describe('speed-dial-props', () => { + it('transforms props as needed', () => { + const actual = transform({ source: read('./test-cases/actual.js') }, { jscodeshift }, {}); + + const expected = read('./test-cases/expected.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + + it('should be idempotent', () => { + const actual = transform({ source: read('./test-cases/expected.js') }, { jscodeshift }, {}); + + const expected = read('./test-cases/expected.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + }); + + describe('[theme] speed-dial-props', () => { + it('transforms props as needed', () => { + const actual = transform( + { source: read('./test-cases/theme.actual.js') }, + { jscodeshift }, + {}, + ); + + const expected = read('./test-cases/theme.expected.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + + it('should be idempotent', () => { + const actual = transform( + { source: read('./test-cases/theme.expected.js') }, + { jscodeshift }, + {}, + ); + + const expected = read('./test-cases/theme.expected.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + }); + }); +}); diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/actual.js new file mode 100644 index 00000000000000..8913121edfdf06 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/actual.js @@ -0,0 +1,29 @@ +import SpeedDial from '@mui/material/SpeedDial'; +import { SpeedDial as MySpeedDial } from '@mui/material'; + +; +; +; +; +; +// should skip non MUI components +; diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/expected.js new file mode 100644 index 00000000000000..1eb5d7a6a64194 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/expected.js @@ -0,0 +1,39 @@ +import SpeedDial from '@mui/material/SpeedDial'; +import { SpeedDial as MySpeedDial } from '@mui/material'; + +; +; +; +; +; +// should skip non MUI components +; diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.actual.js new file mode 100644 index 00000000000000..f900d2c37e2aef --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.actual.js @@ -0,0 +1,33 @@ +fn({ + MuiSpeedDial: { + defaultProps: { + TransitionComponent: CustomTransition, + TransitionProps: CustomTransitionProps, + }, + }, +}); + +fn({ + MuiSpeedDial: { + defaultProps: { + TransitionComponent: CustomTransition, + TransitionProps: CustomTransitionProps, + slots: { + root: 'div', + }, + }, + }, +}); + +fn({ + MuiSpeedDial: { + defaultProps: { + TransitionComponent: ComponentTransition, + TransitionProps: CustomTransitionProps, + slots: { + root: 'div', + transition: SlotTransition, + }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.expected.js new file mode 100644 index 00000000000000..1b7872867de54e --- /dev/null +++ b/packages/mui-codemod/src/deprecations/speed-dial-props/test-cases/theme.expected.js @@ -0,0 +1,43 @@ +fn({ + MuiSpeedDial: { + defaultProps: { + slots: { + transition: CustomTransition + }, + + slotProps: { + transition: CustomTransitionProps + } + }, + }, +}); + +fn({ + MuiSpeedDial: { + defaultProps: { + slots: { + root: 'div', + transition: CustomTransition + }, + + slotProps: { + transition: CustomTransitionProps + } + }, + }, +}); + +fn({ + MuiSpeedDial: { + defaultProps: { + slots: { + root: 'div', + transition: SlotTransition, + }, + + slotProps: { + transition: CustomTransitionProps + } + }, + }, +}); diff --git a/packages/mui-material/src/SpeedDial/SpeedDial.d.ts b/packages/mui-material/src/SpeedDial/SpeedDial.d.ts index b8c498d78fceec..fcbd55f7ad01d8 100644 --- a/packages/mui-material/src/SpeedDial/SpeedDial.d.ts +++ b/packages/mui-material/src/SpeedDial/SpeedDial.d.ts @@ -5,12 +5,35 @@ import { InternalStandardProps as StandardProps } from '..'; import { FabProps } from '../Fab'; import { TransitionProps } from '../transitions'; import { SpeedDialClasses } from './speedDialClasses'; +import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; export type CloseReason = 'toggle' | 'blur' | 'mouseLeave' | 'escapeKeyDown'; export type OpenReason = 'toggle' | 'focus' | 'mouseEnter'; +export interface SpeedDialSlots { + /** + * The component that renders the transition. + * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. + * @default {} + */ + transition?: React.JSXElementConstructor< + TransitionProps & { children: React.ReactElement } + >; +} + +export type SpeedDialSlotsAndSlotProps = CreateSlotsAndSlotProps< + SpeedDialSlots, + { + transition: SlotProps, {}, SpeedDialOwnerState>; + } +>; + export interface SpeedDialProps - extends StandardProps, 'children'> { + extends Omit< + StandardProps, 'children'>, + 'slots' | 'slotProps' + >, + SpeedDialSlotsAndSlotProps { /** * SpeedDialActions to display when the SpeedDial is `open`. */ @@ -74,6 +97,7 @@ export interface SpeedDialProps * The component used for the transition. * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. * @default Zoom + * * @deprecated Use `slots.transition` instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/) */ TransitionComponent?: React.JSXElementConstructor; /** @@ -88,10 +112,13 @@ export interface SpeedDialProps /** * Props applied to the transition element. * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component. + * @deprecated Use `slotProps.transition` instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/) */ TransitionProps?: TransitionProps; } +export interface SpeedDialOwnerState extends SpeedDialProps {} + /** * * Demos: diff --git a/packages/mui-material/src/SpeedDial/SpeedDial.js b/packages/mui-material/src/SpeedDial/SpeedDial.js index 9445349b829d76..cb35e3a25de29d 100644 --- a/packages/mui-material/src/SpeedDial/SpeedDial.js +++ b/packages/mui-material/src/SpeedDial/SpeedDial.js @@ -16,6 +16,7 @@ import isMuiElement from '../utils/isMuiElement'; import useForkRef from '../utils/useForkRef'; import useControlled from '../utils/useControlled'; import speedDialClasses, { getSpeedDialUtilityClass } from './speedDialClasses'; +import useSlot from '../utils/useSlot'; const useUtilityClasses = (ownerState) => { const { classes, open, direction } = ownerState; @@ -139,9 +140,11 @@ const SpeedDial = React.forwardRef(function SpeedDial(inProps, ref) { onOpen, open: openProp, openIcon, - TransitionComponent = Zoom, + slots = {}, + slotProps = {}, + TransitionComponent: TransitionComponentProp, + TransitionProps: TransitionPropsProp, transitionDuration = defaultTransitionDuration, - TransitionProps, ...other } = props; @@ -356,6 +359,19 @@ const SpeedDial = React.forwardRef(function SpeedDial(inProps, ref) { }); }); + const backwardCompatibleSlots = { transition: TransitionComponentProp, ...slots }; + const backwardCompatibleSlotProps = { transition: TransitionPropsProp, ...slotProps }; + const externalForwardedProps = { + slots: backwardCompatibleSlots, + slotProps: backwardCompatibleSlotProps, + }; + + const [TransitionSlot, transitionProps] = useSlot('transition', { + elementType: Zoom, + externalForwardedProps, + ownerState, + }); + return ( - + - + ', () => { refInstanceof: window.HTMLDivElement, muiName: 'MuiSpeedDial', testVariantProps: { direction: 'right' }, + slots: { transition: { testWithElement: null } }, skip: [ 'componentProp', // react-transition-group issue 'componentsProp', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afe4625bd74d3a..907e77dfc14e31 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6061,6 +6061,7 @@ packages: /@next/env@14.2.3: resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} + dev: false /@next/eslint-plugin-next@14.2.3: resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==}