From 3f18a52337f8fcd1f7bfa608e98772251236fc07 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Wed, 17 Jan 2024 16:28:43 -0300 Subject: [PATCH 01/13] Deprecate components and componentsProps props --- docs/pages/material-ui/api/alert.json | 8 +++- docs/translations/api-docs/alert/alert.json | 12 ++--- packages/mui-material/src/Alert/Alert.d.ts | 10 +--- packages/mui-material/src/Alert/Alert.js | 47 +++++++++++++------ packages/mui-material/src/Alert/Alert.test.js | 8 +++- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 0b5aca52f3d139..3d2b9476e3fe55 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -15,11 +15,15 @@ "name": "shape", "description": "{ CloseButton?: elementType, CloseIcon?: elementType }" }, - "default": "{}" + "default": "{}", + "deprecated": true, + "deprecationInfo": "use the slots prop instead. This prop will be removed in v7." }, "componentsProps": { "type": { "name": "shape", "description": "{ closeButton?: object, closeIcon?: object }" }, - "default": "{}" + "default": "{}", + "deprecated": true, + "deprecationInfo": "use the slotProps prop instead. This prop will be removed in v7." }, "icon": { "type": { "name": "node" } }, "iconMapping": { diff --git a/docs/translations/api-docs/alert/alert.json b/docs/translations/api-docs/alert/alert.json index 0deebdf2f157e3..12f16b8de45689 100644 --- a/docs/translations/api-docs/alert/alert.json +++ b/docs/translations/api-docs/alert/alert.json @@ -12,11 +12,9 @@ "color": { "description": "The color of the component. Unless provided, the value is taken from the severity prop. It supports both default and custom theme colors, which can be added as shown in the palette customization guide." }, - "components": { - "description": "The components used for each slot inside.
This prop is an alias for the slots prop. It's recommended to use the slots prop instead." - }, + "components": { "description": "The components used for each slot inside." }, "componentsProps": { - "description": "The extra props for the slot components. You can override the existing props or add new ones.
This prop is an alias for the slotProps prop. It's recommended to use the slotProps prop instead, as componentsProps will be deprecated in the future." + "description": "The extra props for the slot components. You can override the existing props or add new ones." }, "icon": { "description": "Override the icon displayed before the children. Unless provided, the icon is mapped to the value of the severity prop. Set to false to remove the icon." @@ -33,11 +31,9 @@ "description": "The severity of the alert. This defines the color and icon used." }, "slotProps": { - "description": "The extra props for the slot components. You can override the existing props or add new ones.
This prop is an alias for the componentsProps prop, which will be deprecated in the future." - }, - "slots": { - "description": "The components used for each slot inside.
This prop is an alias for the components prop, which will be deprecated in the future." + "description": "The extra props for the slot components. You can override the existing props or add new ones." }, + "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." }, diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 7fa875d665e7aa..9bffa2ae7f84d9 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -36,8 +36,7 @@ export interface AlertProps extends StandardProps { /** * The components used for each slot inside. * - * This prop is an alias for the `slots` prop. - * It's recommended to use the `slots` prop instead. + * @deprecated use the `slots` prop instead. This prop will be removed in v7. * * @default {} */ @@ -49,8 +48,7 @@ export interface AlertProps extends StandardProps { * The extra props for the slot components. * You can override the existing props or add new ones. * - * This prop is an alias for the `slotProps` prop. - * It's recommended to use the `slotProps` prop instead, as `componentsProps` will be deprecated in the future. + * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. * * @default {} */ @@ -98,8 +96,6 @@ export interface AlertProps extends StandardProps { * The extra props for the slot components. * You can override the existing props or add new ones. * - * This prop is an alias for the `componentsProps` prop, which will be deprecated in the future. - * * @default {} */ slotProps?: { @@ -109,8 +105,6 @@ export interface AlertProps extends StandardProps { /** * The components used for each slot inside. * - * This prop is an alias for the `components` prop, which will be deprecated in the future. - * * @default {} */ slots?: { diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index b063487a913c3b..98ed7543b9fce3 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -6,6 +6,7 @@ import { unstable_composeClasses as composeClasses } from '@mui/base/composeClas import { darken, lighten } from '@mui/system'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; +import useSlot from '../utils/useSlot'; import capitalize from '../utils/capitalize'; import Paper from '../Paper'; import alertClasses, { getAlertUtilityClass } from './alertClasses'; @@ -167,11 +168,33 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { const classes = useUtilityClasses(ownerState); - const AlertCloseButton = slots.closeButton ?? components.CloseButton ?? IconButton; - const AlertCloseIcon = slots.closeIcon ?? components.CloseIcon ?? CloseIcon; + const backwardCompatibleSlots = { + closeButton: components.CloseButton, + closeIcon: components.CloseIcon, + ...slots, + }; + const backwardCompatibleSlotProps = { + ...componentsProps, + ...slotProps, + }; + + const [CloseButtonSlot, closeButtonProps] = useSlot('closeButton', { + elementType: IconButton, + externalForwardedProps: { + slots: backwardCompatibleSlots, + slotProps: backwardCompatibleSlotProps, + }, + ownerState, + }); - const closeButtonProps = slotProps.closeButton ?? componentsProps.closeButton; - const closeIconProps = slotProps.closeIcon ?? componentsProps.closeIcon; + const [CloseIconSlot, closeIconProps] = useSlot('closeIcon', { + elementType: CloseIcon, + externalForwardedProps: { + slots: backwardCompatibleSlots, + slotProps: backwardCompatibleSlotProps, + }, + ownerState, + }); return ( - - - + + ) : null} @@ -253,8 +276,7 @@ Alert.propTypes /* remove-proptypes */ = { /** * The components used for each slot inside. * - * This prop is an alias for the `slots` prop. - * It's recommended to use the `slots` prop instead. + * @deprecated use the `slots` prop instead. This prop will be removed in v7. * * @default {} */ @@ -266,8 +288,7 @@ Alert.propTypes /* remove-proptypes */ = { * The extra props for the slot components. * You can override the existing props or add new ones. * - * This prop is an alias for the `slotProps` prop. - * It's recommended to use the `slotProps` prop instead, as `componentsProps` will be deprecated in the future. + * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. * * @default {} */ @@ -316,8 +337,6 @@ Alert.propTypes /* remove-proptypes */ = { * The extra props for the slot components. * You can override the existing props or add new ones. * - * This prop is an alias for the `componentsProps` prop, which will be deprecated in the future. - * * @default {} */ slotProps: PropTypes.shape({ @@ -327,8 +346,6 @@ Alert.propTypes /* remove-proptypes */ = { /** * The components used for each slot inside. * - * This prop is an alias for the `components` prop, which will be deprecated in the future. - * * @default {} */ slots: PropTypes.shape({ diff --git a/packages/mui-material/src/Alert/Alert.test.js b/packages/mui-material/src/Alert/Alert.test.js index 8914928c7ff6b4..2a98aee399c0ff 100644 --- a/packages/mui-material/src/Alert/Alert.test.js +++ b/packages/mui-material/src/Alert/Alert.test.js @@ -20,8 +20,12 @@ describe('', () => { testDeepOverrides: { slotName: 'message', slotClassName: classes.message }, testLegacyComponentsProp: true, slots: { - closeButton: {}, - closeIcon: {}, + closeButton: { + expectedClassName: classes.closeButton, + }, + closeIcon: { + expectedClassName: classes.closeIcon, + }, }, skip: [ 'componentsProp', From 23f106c64095ab6cdee1bd25109d40ea467969cb Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 18 Jan 2024 16:30:23 -0300 Subject: [PATCH 02/13] Support for slotProps callbacks --- packages/mui-material/src/Alert/Alert.d.ts | 19 +++++++++++++++++-- packages/mui-material/src/Alert/Alert.js | 4 ++-- packages/mui-material/src/Alert/Alert.test.js | 5 +---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 9bffa2ae7f84d9..da244379a56e94 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -4,6 +4,7 @@ import { SxProps } from '@mui/system'; import { IconButtonProps, InternalStandardProps as StandardProps, SvgIconProps, Theme } from '..'; import { PaperProps } from '../Paper'; import { AlertClasses } from './alertClasses'; +import { SlotProps } from '../utils/types'; export type AlertColor = 'success' | 'info' | 'warning' | 'error'; @@ -11,6 +12,10 @@ export interface AlertPropsVariantOverrides {} export interface AlertPropsColorOverrides {} +export interface AlertCloseButtonSlotPropsOverrides {} + +export interface AlertCloseIconSlotPropsOverrides {} + export interface AlertProps extends StandardProps { /** * The action to display. It renders after the message, at the end of the alert. @@ -99,8 +104,16 @@ export interface AlertProps extends StandardProps { * @default {} */ slotProps?: { - closeButton?: IconButtonProps; - closeIcon?: SvgIconProps; + closeButton?: SlotProps< + React.ElementType, + AlertCloseButtonSlotPropsOverrides, + AlertOwnerState + >; + closeIcon?: SlotProps< + React.ElementType, + AlertCloseIconSlotPropsOverrides, + AlertOwnerState + >; }; /** * The components used for each slot inside. @@ -117,6 +130,8 @@ export interface AlertProps extends StandardProps { sx?: SxProps; } +export interface AlertOwnerState extends AlertProps {} + /** * * Demos: diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 98ed7543b9fce3..f4f91559b4eb95 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -340,8 +340,8 @@ Alert.propTypes /* remove-proptypes */ = { * @default {} */ slotProps: PropTypes.shape({ - closeButton: PropTypes.object, - closeIcon: PropTypes.object, + closeButton: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + closeIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), }), /** * The components used for each slot inside. diff --git a/packages/mui-material/src/Alert/Alert.test.js b/packages/mui-material/src/Alert/Alert.test.js index 2a98aee399c0ff..0609b830138957 100644 --- a/packages/mui-material/src/Alert/Alert.test.js +++ b/packages/mui-material/src/Alert/Alert.test.js @@ -27,10 +27,7 @@ describe('', () => { expectedClassName: classes.closeIcon, }, }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], + skip: ['componentsProp'], })); describe('prop: square', () => { From 32ff0b8f8e5022548026533e985eafdcd9af5828 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 18 Jan 2024 16:37:38 -0300 Subject: [PATCH 03/13] Run docs:api --- docs/pages/material-ui/api/alert.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 3d2b9476e3fe55..21ff8a0d1a5904 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -48,7 +48,10 @@ "default": "'success'" }, "slotProps": { - "type": { "name": "shape", "description": "{ closeButton?: object, closeIcon?: object }" }, + "type": { + "name": "shape", + "description": "{ closeButton?: func
| object, closeIcon?: func
| object }" + }, "default": "{}" }, "slots": { From 2a7780b8b36eba73ffbb3c2114ca9904ee5b99c6 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 19 Jan 2024 08:35:52 -0300 Subject: [PATCH 04/13] Refactor externalForwardedProps --- packages/mui-material/src/Alert/Alert.js | 28 ++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index f4f91559b4eb95..228b539f7ba295 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -168,31 +168,27 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { const classes = useUtilityClasses(ownerState); - const backwardCompatibleSlots = { - closeButton: components.CloseButton, - closeIcon: components.CloseIcon, - ...slots, - }; - const backwardCompatibleSlotProps = { - ...componentsProps, - ...slotProps, + const externalForwardedProps = { + slots: { + closeButton: components.CloseButton, + closeIcon: components.CloseIcon, + ...slots, + }, + slotProps: { + ...componentsProps, + ...slotProps, + }, }; const [CloseButtonSlot, closeButtonProps] = useSlot('closeButton', { elementType: IconButton, - externalForwardedProps: { - slots: backwardCompatibleSlots, - slotProps: backwardCompatibleSlotProps, - }, + externalForwardedProps, ownerState, }); const [CloseIconSlot, closeIconProps] = useSlot('closeIcon', { elementType: CloseIcon, - externalForwardedProps: { - slots: backwardCompatibleSlots, - slotProps: backwardCompatibleSlotProps, - }, + externalForwardedProps, ownerState, }); From 20e0c3eb5e789ef24579018daa1a25680c04a3c8 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 12 Feb 2024 15:03:14 -0300 Subject: [PATCH 05/13] Remove alert slots override interfaces --- packages/mui-material/src/Alert/Alert.d.ts | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index da244379a56e94..e3e01a36400512 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -12,10 +12,6 @@ export interface AlertPropsVariantOverrides {} export interface AlertPropsColorOverrides {} -export interface AlertCloseButtonSlotPropsOverrides {} - -export interface AlertCloseIconSlotPropsOverrides {} - export interface AlertProps extends StandardProps { /** * The action to display. It renders after the message, at the end of the alert. @@ -104,16 +100,8 @@ export interface AlertProps extends StandardProps { * @default {} */ slotProps?: { - closeButton?: SlotProps< - React.ElementType, - AlertCloseButtonSlotPropsOverrides, - AlertOwnerState - >; - closeIcon?: SlotProps< - React.ElementType, - AlertCloseIconSlotPropsOverrides, - AlertOwnerState - >; + closeButton?: SlotProps, {}, AlertOwnerState>; + closeIcon?: SlotProps, {}, AlertOwnerState>; }; /** * The components used for each slot inside. From 68fa6153f3bab97d22760e04f797261cc6436381 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 12 Feb 2024 15:15:49 -0300 Subject: [PATCH 06/13] Update Alert slot types --- docs/pages/material-ui/api/alert.json | 14 ++++++ docs/translations/api-docs/alert/alert.json | 8 ++-- packages/mui-material/src/Alert/Alert.d.ts | 48 +++++++++++---------- packages/mui-material/src/Alert/Alert.js | 5 +-- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 21ff8a0d1a5904..0ac352af3a73b8 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -78,6 +78,20 @@ }, "name": "Alert", "imports": ["import Alert from '@mui/material/Alert';", "import { Alert } from '@mui/material';"], + "slots": [ + { + "name": "closeButton", + "description": "The component that renders the close button.", + "default": "IconButton", + "class": null + }, + { + "name": "closeIcon", + "description": "The component that renders the close icon.", + "default": "svg", + "class": null + } + ], "classes": [ { "key": "action", diff --git a/docs/translations/api-docs/alert/alert.json b/docs/translations/api-docs/alert/alert.json index 12f16b8de45689..a1e17498e79a4b 100644 --- a/docs/translations/api-docs/alert/alert.json +++ b/docs/translations/api-docs/alert/alert.json @@ -30,9 +30,7 @@ "severity": { "description": "The severity of the alert. This defines the color and icon used." }, - "slotProps": { - "description": "The extra props for the slot components. You can override the existing props or add new ones." - }, + "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." @@ -129,5 +127,9 @@ "nodeName": "the root element", "conditions": "variant=\"standard\" and color=\"warning\"" } + }, + "slotDescriptions": { + "closeButton": "The component that renders the close button.", + "closeIcon": "The component that renders the close icon." } } diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index e3e01a36400512..67b34cd27c1719 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -4,7 +4,7 @@ import { SxProps } from '@mui/system'; import { IconButtonProps, InternalStandardProps as StandardProps, SvgIconProps, Theme } from '..'; import { PaperProps } from '../Paper'; import { AlertClasses } from './alertClasses'; -import { SlotProps } from '../utils/types'; +import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; export type AlertColor = 'success' | 'info' | 'warning' | 'error'; @@ -12,7 +12,28 @@ export interface AlertPropsVariantOverrides {} export interface AlertPropsColorOverrides {} -export interface AlertProps extends StandardProps { +export interface AlertSlots { + /** + * The component that renders the close button. + * @default IconButton + */ + closeButton?: React.ElementType; + /** + * The component that renders the close icon. + * @default svg + */ + closeIcon?: React.ElementType; +} + +export type AlertSlotsAndSlotProps = CreateSlotsAndSlotProps< + AlertSlots, + { + closeButton: SlotProps, {}, AlertOwnerState>; + closeIcon: SlotProps, {}, AlertOwnerState>; + } +>; + +export interface AlertOwnProps extends StandardProps { /** * The action to display. It renders after the message, at the end of the alert. */ @@ -93,32 +114,13 @@ export interface AlertProps extends StandardProps { * @default 'standard' */ variant?: OverridableStringUnion<'standard' | 'filled' | 'outlined', AlertPropsVariantOverrides>; - /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * - * @default {} - */ - slotProps?: { - closeButton?: SlotProps, {}, AlertOwnerState>; - closeIcon?: SlotProps, {}, AlertOwnerState>; - }; - /** - * The components used for each slot inside. - * - * @default {} - */ - slots?: { - closeButton?: React.ElementType; - closeIcon?: React.ElementType; - }; /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx?: SxProps; } -export interface AlertOwnerState extends AlertProps {} +export interface AlertOwnerState extends AlertOwnProps {} /** * @@ -131,4 +133,4 @@ export interface AlertOwnerState extends AlertProps {} * - [Alert API](https://mui.com/material-ui/api/alert/) * - inherits [Paper API](https://mui.com/material-ui/api/paper/) */ -export default function Alert(props: AlertProps): JSX.Element; +export default function Alert(props: AlertOwnProps & AlertSlotsAndSlotProps): JSX.Element; diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 228b539f7ba295..0390cd3c6fd109 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -330,9 +330,7 @@ Alert.propTypes /* remove-proptypes */ = { PropTypes.string, ]), /** - * The extra props for the slot components. - * You can override the existing props or add new ones. - * + * The props used for each slot inside. * @default {} */ slotProps: PropTypes.shape({ @@ -341,7 +339,6 @@ Alert.propTypes /* remove-proptypes */ = { }), /** * The components used for each slot inside. - * * @default {} */ slots: PropTypes.shape({ From 24c50033b7d4bd4fff0ce0c9f2a9a4790075e452 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 12 Feb 2024 15:23:45 -0300 Subject: [PATCH 07/13] Revert AlertProps name change --- packages/mui-material/src/Alert/Alert.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 67b34cd27c1719..3e009b173cc695 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -33,7 +33,7 @@ export type AlertSlotsAndSlotProps = CreateSlotsAndSlotProps< } >; -export interface AlertOwnProps extends StandardProps { +export interface AlertProps extends StandardProps { /** * The action to display. It renders after the message, at the end of the alert. */ @@ -120,7 +120,7 @@ export interface AlertOwnProps extends StandardProps { sx?: SxProps; } -export interface AlertOwnerState extends AlertOwnProps {} +export interface AlertOwnerState extends AlertProps {} /** * @@ -133,4 +133,4 @@ export interface AlertOwnerState extends AlertOwnProps {} * - [Alert API](https://mui.com/material-ui/api/alert/) * - inherits [Paper API](https://mui.com/material-ui/api/paper/) */ -export default function Alert(props: AlertOwnProps & AlertSlotsAndSlotProps): JSX.Element; +export default function Alert(props: AlertProps & AlertSlotsAndSlotProps): JSX.Element; From f8663dcecb86c0f2d61ee64ac67754905c4b91b2 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 15 Feb 2024 16:33:31 -0300 Subject: [PATCH 08/13] Add codemod --- .../deprecations/alert-props/alert-props.js | 15 ++ .../alert-props/alert-props.test.js | 53 ++++++ .../src/deprecations/alert-props/index.js | 1 + .../alert-props/test-cases/actual.js | 20 ++ .../alert-props/test-cases/expected.js | 20 ++ .../alert-props/test-cases/theme.actual.js | 30 +++ .../alert-props/test-cases/theme.expected.js | 45 +++++ .../utils/replaceComponentsWithSlots.js | 177 ++++++++++++++++++ .../src/util/findComponentDefaultProps.js | 17 ++ 9 files changed, 378 insertions(+) create mode 100644 packages/mui-codemod/src/deprecations/alert-props/alert-props.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/index.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js create mode 100644 packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js create mode 100644 packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js create mode 100644 packages/mui-codemod/src/util/findComponentDefaultProps.js diff --git a/packages/mui-codemod/src/deprecations/alert-props/alert-props.js b/packages/mui-codemod/src/deprecations/alert-props/alert-props.js new file mode 100644 index 00000000000000..2fe8969c09c01b --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/alert-props.js @@ -0,0 +1,15 @@ +import replaceComponentsWithSlots from '../utils/replaceComponentsWithSlots'; + +/** + * @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; + + replaceComponentsWithSlots(j, { root, componentName: 'Alert' }); + + return root.toSource(printOptions); +} diff --git a/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js b/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js new file mode 100644 index 00000000000000..d9741ce1a1e13c --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/alert-props.test.js @@ -0,0 +1,53 @@ +import path from 'path'; +import { expect } from 'chai'; +import { jscodeshift } from '../../../testUtils'; +import transform from './alert-props'; +import readFile from '../../util/readFile'; + +function read(fileName) { + return readFile(path.join(__dirname, fileName)); +} + +describe('@mui/codemod', () => { + describe('deprecations', () => { + describe('alert-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] alert-props', () => { + it('transforms props as needed', () => { + const actual = transform( + { source: read('./test-cases/theme.actual.js') }, + { jscodeshift }, + { printOptions: { trailingComma: true } }, + ); + + 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/alert-props/index.js b/packages/mui-codemod/src/deprecations/alert-props/index.js new file mode 100644 index 00000000000000..55caff1bc01b51 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/index.js @@ -0,0 +1 @@ +export { default } from './alert-props'; diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js new file mode 100644 index 00000000000000..1c1a02c7b7e601 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js @@ -0,0 +1,20 @@ +import Alert from '@mui/material/Alert'; + +; +; +; diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js new file mode 100644 index 00000000000000..1c1a02c7b7e601 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/expected.js @@ -0,0 +1,20 @@ +import Alert from '@mui/material/Alert'; + +; +; +; diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js new file mode 100644 index 00000000000000..53747aee31ea17 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.actual.js @@ -0,0 +1,30 @@ +fn({ + MuiAlert: { + defaultProps: { + components: { CloseButton: ComponentsButton }, + componentsProps: { closeButton: componentsButtonProps }, + }, + }, +}); + +fn({ + MuiAlert: { + defaultProps: { + components: { CloseButton: ComponentsButton }, + slots: { closeIcon: SlotsIcon }, + componentsProps: { closeButton: componentsButtonProps }, + slotProps: { closeIcon: slotsIconProps }, + }, + }, +}); + +fn({ + MuiAlert: { + defaultProps: { + components: { CloseButton: ComponentsButton }, + slots: { closeIcon: SlotsIcon, closeButton: SlotsButton }, + componentsProps: { closeButton: componentsButtonProps }, + slotProps: { closeIcon: slotsIconProps, closeButton: slotsButtonProps }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js new file mode 100644 index 00000000000000..dfab24532bd162 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/theme.expected.js @@ -0,0 +1,45 @@ +fn({ + MuiAlert: { + defaultProps: { + slots: { + closeButton: ComponentsButton, + }, + + slotProps: { + closeButton: componentsButtonProps, + }, + }, + }, +}); + +fn({ + MuiAlert: { + defaultProps: { + slots: { + closeButton: ComponentsButton, + closeIcon: SlotsIcon, + }, + + slotProps: { + closeButton: componentsButtonProps, + closeIcon: slotsIconProps, + }, + }, + }, +}); + +fn({ + MuiAlert: { + defaultProps: { + slots: { + closeButton: SlotsButton, + closeIcon: SlotsIcon, + }, + + slotProps: { + closeButton: slotsButtonProps, + closeIcon: slotsIconProps, + }, + }, + }, +}); diff --git a/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js b/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js new file mode 100644 index 00000000000000..fb1ee1d87edad4 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/utils/replaceComponentsWithSlots.js @@ -0,0 +1,177 @@ +import findComponentJSX from '../../util/findComponentJSX'; +import findComponentDefaultProps from '../../util/findComponentDefaultProps'; +import assignObject from '../../util/assignObject'; +import appendAttribute from '../../util/appendAttribute'; + +function componentsKeyToSlotsKey(str) { + return str[0].toLowerCase() + str.slice(1); +} + +function replaceJsxComponentsProp(j, elementPath) { + const element = elementPath.node; + const index = element.openingElement.attributes.findIndex( + (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'components', + ); + if (index !== -1) { + const removed = element.openingElement.attributes.splice(index, 1); + const camelCaseComponents = removed[0].value.expression.properties.reduce((acc, prop) => { + return { ...acc, [componentsKeyToSlotsKey(prop.key.name)]: prop.value }; + }, {}); + let hasNode = false; + element.openingElement.attributes.forEach((attr) => { + if (attr.name?.name === 'slots') { + hasNode = true; + const slots = attr.value.expression.properties.reduce((acc, prop) => { + return { ...acc, [prop.key.name]: prop.value }; + }, {}); + Object.entries(camelCaseComponents).forEach(([slot, value]) => { + if (!slots[slot]) { + assignObject(j, { + target: attr, + key: slot, + expression: value, + }); + } + }); + } + }); + if (!hasNode) { + appendAttribute(j, { + target: element, + attributeName: 'slots', + expression: j.objectExpression( + Object.entries(camelCaseComponents).map(([slot, value]) => { + return j.objectProperty(j.identifier(slot), value); + }), + ), + }); + } + } +} + +function replaceJsxComponentsPropsProp(j, element) { + const index = element.openingElement.attributes.findIndex( + (attr) => attr.type === 'JSXAttribute' && attr.name.name === 'componentsProps', + ); + if (index !== -1) { + const removed = element.openingElement.attributes.splice(index, 1); + let hasNode = false; + element.openingElement.attributes.forEach((attr) => { + if (attr.name?.name === 'slotProps') { + hasNode = true; + const slotProps = attr.value.expression.properties.reduce((acc, prop) => { + return { ...acc, [prop.key.name]: prop.value }; + }, {}); + removed[0].value.expression.properties.forEach((prop) => { + if (!slotProps[prop.key.name]) { + assignObject(j, { + target: attr, + key: prop.key.name, + expression: prop.value, + }); + } + }); + } + }); + if (!hasNode) { + appendAttribute(j, { + target: element, + attributeName: 'slotProps', + expression: removed[0].value.expression, + }); + } + } +} + +function replaceDefaultPropsComponentsProp(j, defaultPropsPathCollection) { + defaultPropsPathCollection + .find(j.ObjectProperty, { key: { name: 'components' } }) + .forEach((path) => { + const { properties: defaultPropsProperties } = path.parent.value; + + const components = path.value.value.properties.reduce((acc, prop) => { + return { ...acc, [componentsKeyToSlotsKey(prop.key.name)]: prop.value }; + }, {}); + + const existingSlots = defaultPropsProperties.find((prop) => prop.key.name === 'slots'); + + const slots = existingSlots + ? existingSlots.value.properties.reduce((acc, prop) => { + return { ...acc, [prop.key.name]: prop.value }; + }, {}) + : {}; + + const updatedSlots = j.objectExpression( + Object.entries({ ...components, ...slots }).map(([slot, value]) => { + return j.objectProperty(j.identifier(slot), value); + }), + ); + + if (existingSlots) { + existingSlots.value = updatedSlots; + } else { + defaultPropsProperties.push(j.property('init', j.identifier('slots'), updatedSlots)); + } + + path.prune(); + }); +} + +function replaceDefaultPropsComponentsPropsProp(j, defaultPropsPathCollection) { + defaultPropsPathCollection + .find(j.ObjectProperty, { key: { name: 'componentsProps' } }) + .forEach((path) => { + const { properties: defaultPropsProperties } = path.parent.value; + + const components = path.value.value.properties.reduce((acc, prop) => { + return { ...acc, [prop.key.name]: prop.value }; + }, {}); + + const existingSlots = defaultPropsProperties.find((prop) => prop.key.name === 'slotProps'); + + const slots = existingSlots + ? existingSlots.value.properties.reduce((acc, prop) => { + return { ...acc, [prop.key.name]: prop.value }; + }, {}) + : {}; + + const updatedSlots = j.objectExpression( + Object.entries({ ...components, ...slots }).map(([slot, value]) => { + return j.objectProperty(j.identifier(slot), value); + }), + ); + + if (existingSlots) { + existingSlots.value = updatedSlots; + } else { + defaultPropsProperties.push(j.property('init', j.identifier('slotProps'), updatedSlots)); + } + + path.prune(); + }); +} + +/** + * Replaces components and componentsProps props with slots and slotProps. + * Handles local object and variable declaration. + * If the slots prop exists, it will add the components to the slots. + * If there are duplicated values, the slots values will be used. + * + * @param {import('jscodeshift')} j + * @param {{ element: import('jscodeshift').JSXElement }} options + * + * @example => + */ +export default function replaceComponentsWithSlots(j, options) { + const { root, componentName } = options; + + findComponentJSX(j, { root, componentName }, (elementPath) => { + replaceJsxComponentsProp(j, elementPath); + replaceJsxComponentsPropsProp(j, elementPath.node); + }); + + const defaultPropsPathCollection = findComponentDefaultProps(j, { root, componentName }); + + replaceDefaultPropsComponentsProp(j, defaultPropsPathCollection); + replaceDefaultPropsComponentsPropsProp(j, defaultPropsPathCollection); +} diff --git a/packages/mui-codemod/src/util/findComponentDefaultProps.js b/packages/mui-codemod/src/util/findComponentDefaultProps.js new file mode 100644 index 00000000000000..f37358d0ef4bb7 --- /dev/null +++ b/packages/mui-codemod/src/util/findComponentDefaultProps.js @@ -0,0 +1,17 @@ +/** + * Find all the default props path of a given component name. + * + * @param {import('jscodeshift')} j + * @param {{ root: import('jscodeshift').Collection; componentName: string }} options + * @returns {import('jscodeshift').Collection} + * + */ +export default function findComponentDefaultProps(j, options) { + const { root, componentName } = options; + + const defaultPropsPathCollection = root + .find(j.ObjectProperty, { key: { name: `Mui${componentName}` } }) + .find(j.ObjectProperty, { key: { name: 'defaultProps' } }); + + return defaultPropsPathCollection; +} From be6b94da600ba70bc5e47785d1852a428b5ba00d Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 15 Feb 2024 16:40:41 -0300 Subject: [PATCH 09/13] Add migration guide --- .../migrating-from-deprecated-apis.md | 30 +++++++++++++++++++ packages/mui-codemod/README.md | 26 ++++++++++++++++ .../alert-props/test-cases/actual.js | 24 +++++++-------- 3 files changed, 67 insertions(+), 13 deletions(-) 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 cb06f952f07491..903eb619001eac 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 @@ -90,6 +90,36 @@ Bear in mind that the `.MuiAccordionSummary-gutters` class is applied to the com }, ``` +## Alert + +Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#alert-props) below to migrate the code as described in the following sections: + +```bash +npx @mui/codemod@latest deprecations/alert-props +``` + +### components + +The Alert's `components` was deprecated in favor of `slots`: + +```diff + +``` + +### componentsProps + +The Alert's `componentsProps` was deprecated in favor of `slotProps`: + +```diff + +``` + ## Avatar Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#avatar-props) below to migrate the code as described in the following sections: diff --git a/packages/mui-codemod/README.md b/packages/mui-codemod/README.md index 709373675e397c..479a4fd75405a0 100644 --- a/packages/mui-codemod/README.md +++ b/packages/mui-codemod/README.md @@ -136,6 +136,32 @@ CSS transforms: npx @mui/codemod@latest deprecations/accordion-summary-classes ``` +#### `alert-props` + +```diff + +``` + +```diff + MuiAlert: { + defaultProps: { +- components: { CloseButton: CustomButton } ++ slots: { closeButton: CustomButton }, +- componentsProps: { closeButton: { testid: 'test-id' }} ++ slotProps: { closeButton: { testid: 'test-id' } }, + }, +}, +``` + +```bash +npx @mui/codemod@latest deprecations/alert-props +``` + #### `avatar-props` ```diff diff --git a/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js index 1c1a02c7b7e601..380a640bce04fc 100644 --- a/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js @@ -1,20 +1,18 @@ import Alert from '@mui/material/Alert'; ; ; + slots={{ closeIcon: SlotsIcon }} + components={{ CloseButton: ComponentsButton }} + slotProps={{ closeIcon: slotsIconProps }} + componentsProps={{ closeButton: componentsButtonProps }} +/>; ; + components={{ CloseButton: ComponentsButton }} + slotProps={{ closeIcon: slotsIconProps, closeButton: slotsButtonProps }} + componentsProps={{ closeButton: componentsButtonProps }} +/>; From c9dc3521417fedc5fe555ce9dd908b657672a38c Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 15 Feb 2024 16:51:53 -0300 Subject: [PATCH 10/13] Add link to migration guide --- docs/pages/material-ui/api/alert.json | 4 ++-- packages/mui-material/src/Alert/Alert.d.ts | 4 ++-- packages/mui-material/src/Alert/Alert.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 0ac352af3a73b8..6fea5908c00a95 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -17,13 +17,13 @@ }, "default": "{}", "deprecated": true, - "deprecationInfo": "use the slots prop instead. This prop will be removed in v7." + "deprecationInfo": "use the slots prop instead. This prop will be removed in v7. How to migrate." }, "componentsProps": { "type": { "name": "shape", "description": "{ closeButton?: object, closeIcon?: object }" }, "default": "{}", "deprecated": true, - "deprecationInfo": "use the slotProps prop instead. This prop will be removed in v7." + "deprecationInfo": "use the slotProps prop instead. This prop will be removed in v7. How to migrate." }, "icon": { "type": { "name": "node" } }, "iconMapping": { diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 3e009b173cc695..70875f28848eea 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -58,7 +58,7 @@ export interface AlertProps extends StandardProps { /** * The components used for each slot inside. * - * @deprecated use the `slots` prop instead. This prop will be removed in v7. + * @deprecated use the `slots` prop instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -70,7 +70,7 @@ export interface AlertProps extends StandardProps { * The extra props for the slot components. * You can override the existing props or add new ones. * - * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. + * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 49004f7556150c..6ef3b031dcad90 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -272,7 +272,7 @@ Alert.propTypes /* remove-proptypes */ = { /** * The components used for each slot inside. * - * @deprecated use the `slots` prop instead. This prop will be removed in v7. + * @deprecated use the `slots` prop instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -284,7 +284,7 @@ Alert.propTypes /* remove-proptypes */ = { * The extra props for the slot components. * You can override the existing props or add new ones. * - * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. + * @deprecated use the `slotProps` prop instead. This prop will be removed in v7. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ From d50268254ff08915820d783d6b887803c0047494 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Thu, 15 Feb 2024 18:18:32 -0300 Subject: [PATCH 11/13] markdown lint --- packages/mui-codemod/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mui-codemod/README.md b/packages/mui-codemod/README.md index 479a4fd75405a0..9fe3cfe94910ba 100644 --- a/packages/mui-codemod/README.md +++ b/packages/mui-codemod/README.md @@ -155,7 +155,7 @@ npx @mui/codemod@latest deprecations/accordion-summary-classes - componentsProps: { closeButton: { testid: 'test-id' }} + slotProps: { closeButton: { testid: 'test-id' } }, }, -}, + }, ``` ```bash From d8989131a37f48a7d30782e4df711eece1233ac4 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 16 Feb 2024 09:07:52 -0300 Subject: [PATCH 12/13] Use package root imports --- packages/mui-material/src/Alert/Alert.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 6ef3b031dcad90..9701368cbbf77c 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -2,8 +2,8 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import composeClasses from '@mui/utils/composeClasses'; -import { darken, lighten } from '@mui/system/colorManipulator'; +import { unstable_composeClasses as composeClasses } from '@mui/base'; +import { darken, lighten } from '@mui/system'; import styled from '../styles/styled'; import useThemeProps from '../styles/useThemeProps'; import useSlot from '../utils/useSlot'; From cbcf35914ce86d6982e5e75b2888b0c6d2727aa4 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 16 Feb 2024 09:08:12 -0300 Subject: [PATCH 13/13] Fix incorrectly forwarded ownerState --- packages/mui-material/src/IconButton/IconButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mui-material/src/IconButton/IconButton.js b/packages/mui-material/src/IconButton/IconButton.js index 0c6a5351555cc2..902008d145f9ba 100644 --- a/packages/mui-material/src/IconButton/IconButton.js +++ b/packages/mui-material/src/IconButton/IconButton.js @@ -144,8 +144,8 @@ const IconButton = React.forwardRef(function IconButton(inProps, ref) { focusRipple={!disableFocusRipple} disabled={disabled} ref={ref} - ownerState={ownerState} {...other} + ownerState={ownerState} > {children}