From 107790684708a3df8840db59c3a640693ed6cdf7 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Fri, 16 Feb 2024 10:52:19 -0300 Subject: [PATCH] [material-ui][Alert] Deprecate components and componentsProps props (#40681) --- .../migrating-from-deprecated-apis.md | 30 +++ docs/pages/material-ui/api/alert.json | 27 ++- docs/translations/api-docs/alert/alert.json | 18 +- packages/mui-codemod/README.md | 26 +++ .../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 | 18 ++ .../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 ++ packages/mui-material/src/Alert/Alert.d.ts | 55 +++--- packages/mui-material/src/Alert/Alert.js | 56 +++--- packages/mui-material/src/Alert/Alert.test.js | 13 +- .../mui-material/src/IconButton/IconButton.js | 2 +- 17 files changed, 532 insertions(+), 71 deletions(-) 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/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/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 0b5aca52f3d139..6fea5908c00a95 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. How to migrate." }, "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. How to migrate." }, "icon": { "type": { "name": "node" } }, "iconMapping": { @@ -44,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": { @@ -71,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 0deebdf2f157e3..a1e17498e79a4b 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." @@ -32,12 +30,8 @@ "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.
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." - }, + "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." }, @@ -133,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-codemod/README.md b/packages/mui-codemod/README.md index 709373675e397c..9fe3cfe94910ba 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/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..380a640bce04fc --- /dev/null +++ b/packages/mui-codemod/src/deprecations/alert-props/test-cases/actual.js @@ -0,0 +1,18 @@ +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; +} diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 7fa875d665e7aa..70875f28848eea 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 { CreateSlotsAndSlotProps, SlotProps } from '../utils/types'; export type AlertColor = 'success' | 'info' | 'warning' | 'error'; @@ -11,6 +12,27 @@ export interface AlertPropsVariantOverrides {} export interface AlertPropsColorOverrides {} +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 AlertProps extends StandardProps { /** * The action to display. It renders after the message, at the end of the alert. @@ -36,8 +58,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. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -49,8 +70,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. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -94,35 +114,14 @@ 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. - * - * This prop is an alias for the `componentsProps` prop, which will be deprecated in the future. - * - * @default {} - */ - slotProps?: { - closeButton?: IconButtonProps; - closeIcon?: SvgIconProps; - }; - /** - * 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?: { - 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 {} + /** * * Demos: @@ -134,4 +133,4 @@ export interface AlertProps extends StandardProps { * - [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: AlertProps & AlertSlotsAndSlotProps): JSX.Element; diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index db4a52e3fc21b6..9701368cbbf77c 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -2,10 +2,11 @@ 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'; import capitalize from '../utils/capitalize'; import Paper from '../Paper'; import alertClasses, { getAlertUtilityClass } from './alertClasses'; @@ -167,11 +168,29 @@ 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 externalForwardedProps = { + slots: { + closeButton: components.CloseButton, + closeIcon: components.CloseIcon, + ...slots, + }, + slotProps: { + ...componentsProps, + ...slotProps, + }, + }; + + const [CloseButtonSlot, closeButtonProps] = useSlot('closeButton', { + elementType: IconButton, + externalForwardedProps, + ownerState, + }); - const closeButtonProps = slotProps.closeButton ?? componentsProps.closeButton; - const closeIconProps = slotProps.closeIcon ?? componentsProps.closeIcon; + const [CloseIconSlot, closeIconProps] = useSlot('closeIcon', { + elementType: CloseIcon, + externalForwardedProps, + ownerState, + }); return ( - - - + + ) : null} @@ -253,8 +272,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. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -266,8 +284,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. [How to migrate](/material-ui/migration/migrating-from-deprecated-apis/). * * @default {} */ @@ -313,22 +330,15 @@ Alert.propTypes /* remove-proptypes */ = { PropTypes.string, ]), /** - * 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. - * + * The props used for each slot inside. * @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. - * - * 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 ee8d748c46a2bb..5ea81fc2eb384f 100644 --- a/packages/mui-material/src/Alert/Alert.test.js +++ b/packages/mui-material/src/Alert/Alert.test.js @@ -20,13 +20,14 @@ describe('', () => { testDeepOverrides: { slotName: 'message', slotClassName: classes.message }, testLegacyComponentsProp: true, slots: { - closeButton: {}, - closeIcon: {}, + closeButton: { + expectedClassName: classes.closeButton, + }, + closeIcon: { + expectedClassName: classes.closeIcon, + }, }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], + skip: ['componentsProp'], })); describe('prop: square', () => { 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}