From ea170a694ac40b38b653f67223fde481daa574d2 Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Wed, 8 Jan 2025 14:13:54 +0700 Subject: [PATCH 1/5] complete the rest of the slots --- .../test-utils/src/describeConformance.tsx | 12 +++- packages/mui-material/src/Alert/Alert.js | 59 ++++++++++++------- packages/mui-material/src/Alert/Alert.test.js | 14 ++++- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/packages-internal/test-utils/src/describeConformance.tsx b/packages-internal/test-utils/src/describeConformance.tsx index 2cd9d11010c56b..943c71e214fecd 100644 --- a/packages-internal/test-utils/src/describeConformance.tsx +++ b/packages-internal/test-utils/src/describeConformance.tsx @@ -64,7 +64,7 @@ export interface ConformanceOptions { testStateOverrides?: { prop?: string; value?: any; styleKey: string }; testCustomVariant?: boolean; testVariantProps?: object; - testLegacyComponentsProp?: boolean; + testLegacyComponentsProp?: boolean | string[]; slots?: Record; ThemeProvider?: React.ElementType; /** @@ -387,7 +387,10 @@ function testSlotsProp( } // For testing Material UI components v5, and v6. Likely to be removed in v7. - if (testLegacyComponentsProp) { + if ( + testLegacyComponentsProp === true || + (Array.isArray(testLegacyComponentsProp) && testLegacyComponentsProp.includes(slotName)) + ) { it(`allows overriding the ${slotName} slot with a component using the components.${capitalize( slotName, )} prop`, async () => { @@ -541,7 +544,10 @@ function testSlotPropsProp( }); } - if (testLegacyComponentsProp) { + if ( + testLegacyComponentsProp === true || + (Array.isArray(testLegacyComponentsProp) && testLegacyComponentsProp.includes(slotName)) + ) { it(`sets custom properties on the ${slotName} slot's element with the componentsProps.${slotName} prop`, async () => { const componentsProps = { [slotName]: { diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 33c3f68e7b8f4c..72fdd2f8f6e53d 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -191,6 +191,7 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { const classes = useUtilityClasses(ownerState); const externalForwardedProps = { + ...other, slots: { closeButton: components.CloseButton, closeIcon: components.CloseIcon, @@ -202,6 +203,35 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { }, }; + const [RootSlot, rootSlotProps] = useSlot('root', { + ref, + className: clsx(classes.root, className), + elementType: AlertRoot, + externalForwardedProps, + ownerState, + }); + + const [IconSlot, iconSlotProps] = useSlot('icon', { + className: classes.icon, + elementType: AlertIcon, + externalForwardedProps, + ownerState, + }); + + const [MessageSlot, messageSlotProps] = useSlot('message', { + className: classes.message, + elementType: AlertMessage, + externalForwardedProps, + ownerState, + }); + + const [ActionSlot, actionSlotProps] = useSlot('action', { + className: classes.action, + elementType: AlertAction, + externalForwardedProps, + ownerState, + }); + const [CloseButtonSlot, closeButtonProps] = useSlot('closeButton', { elementType: IconButton, externalForwardedProps, @@ -215,29 +245,16 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { }); return ( - + {icon !== false ? ( - + {icon || iconMapping[severity] || defaultIconMapping[severity]} - - ) : null} - - {children} - - {action != null ? ( - - {action} - + ) : null} + {children} + {action != null ? {action} : null} {action == null && onClose ? ( - + - + ) : null} - + ); }); diff --git a/packages/mui-material/src/Alert/Alert.test.js b/packages/mui-material/src/Alert/Alert.test.js index e5d4ff4b54f2e7..0a6b0b04dd9fc9 100644 --- a/packages/mui-material/src/Alert/Alert.test.js +++ b/packages/mui-material/src/Alert/Alert.test.js @@ -20,8 +20,20 @@ describe('', () => { muiName: 'MuiAlert', testVariantProps: { variant: 'standard', color: 'success' }, testDeepOverrides: { slotName: 'message', slotClassName: classes.message }, - testLegacyComponentsProp: true, + testLegacyComponentsProp: ['closeButton', 'closeIcon'], slots: { + root: { + expectedClassName: classes.root, + }, + icon: { + expectedClassName: classes.icon, + }, + message: { + expectedClassName: classes.message, + }, + action: { + expectedClassName: classes.action, + }, closeButton: { expectedClassName: classes.closeButton, }, From f27138bd9e6dd1c2186e678ce24c46aac1ae53ae Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Wed, 8 Jan 2025 14:24:13 +0700 Subject: [PATCH 2/5] fix: complete all slots with jsdocs --- docs/pages/material-ui/api/alert.json | 52 ++++++++-------- docs/translations/api-docs/alert/alert.json | 20 ++---- packages/mui-material/src/Alert/Alert.d.ts | 61 +++++++++++++++++++ packages/mui-material/src/Alert/Alert.js | 32 ++++++++++ .../mui-material/src/Alert/Alert.spec.tsx | 25 ++++++++ 5 files changed, 149 insertions(+), 41 deletions(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index f1c0e452bb0968..51d9a243bef167 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -50,14 +50,14 @@ "slotProps": { "type": { "name": "shape", - "description": "{ closeButton?: func
| object, closeIcon?: func
| object }" + "description": "{ action?: func
| { component?: elementType, sx?: Array<func
| object
| bool>
| func
| object }, closeButton?: func
| object, closeIcon?: func
| object, icon?: func
| object, message?: func
| { component?: elementType, sx?: Array<func
| object
| bool>
| func
| object }, root?: func
| object }" }, "default": "{}" }, "slots": { "type": { "name": "shape", - "description": "{ closeButton?: elementType, closeIcon?: elementType }" + "description": "{ action?: elementType, closeButton?: elementType, closeIcon?: elementType, icon?: elementType, message?: elementType, root?: elementType }" }, "default": "{}" }, @@ -79,6 +79,30 @@ "name": "Alert", "imports": ["import Alert from '@mui/material/Alert';", "import { Alert } from '@mui/material';"], "slots": [ + { + "name": "root", + "description": "The component that renders the root slot.", + "default": "Paper", + "class": "MuiAlert-root" + }, + { + "name": "icon", + "description": "The component that renders the icon slot.", + "default": "div", + "class": "MuiAlert-icon" + }, + { + "name": "message", + "description": "The component that renders the message slot.", + "default": "div", + "class": "MuiAlert-message" + }, + { + "name": "action", + "description": "The component that renders the action slot.", + "default": "div", + "class": "MuiAlert-action" + }, { "name": "closeButton", "description": "The component that renders the close button.", @@ -93,12 +117,6 @@ } ], "classes": [ - { - "key": "action", - "className": "MuiAlert-action", - "description": "Styles applied to the action wrapper element if `action` is provided.", - "isGlobal": false - }, { "key": "colorError", "className": "MuiAlert-colorError", @@ -157,18 +175,6 @@ "isGlobal": false, "isDeprecated": true }, - { - "key": "icon", - "className": "MuiAlert-icon", - "description": "Styles applied to the icon wrapper element.", - "isGlobal": false - }, - { - "key": "message", - "className": "MuiAlert-message", - "description": "Styles applied to the message wrapper element.", - "isGlobal": false - }, { "key": "outlined", "className": "MuiAlert-outlined", @@ -203,12 +209,6 @@ "isGlobal": false, "isDeprecated": true }, - { - "key": "root", - "className": "MuiAlert-root", - "description": "Styles applied to the root element.", - "isGlobal": false - }, { "key": "standard", "className": "MuiAlert-standard", diff --git a/docs/translations/api-docs/alert/alert.json b/docs/translations/api-docs/alert/alert.json index 6bf0c4bce51033..5577b02873c7ff 100644 --- a/docs/translations/api-docs/alert/alert.json +++ b/docs/translations/api-docs/alert/alert.json @@ -38,11 +38,6 @@ "variant": { "description": "The variant to use." } }, "classDescriptions": { - "action": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the action wrapper element", - "conditions": "action is provided" - }, "colorError": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the root element", @@ -90,14 +85,6 @@ "description": "Styles applied to the root element if variant="filled" and color="warning"", "deprecationInfo": "Combine the .MuiAlert-filled and .MuiAlert-colorWarning classes instead. See Migrating from deprecated APIs for more details." }, - "icon": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the icon wrapper element" - }, - "message": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the message wrapper element" - }, "outlined": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the root element", @@ -127,7 +114,6 @@ "conditions": "variant=\"outlined\" and color=\"warning\"", "deprecationInfo": "Combine the .MuiAlert-outlined and .MuiAlert-colorWarning classes instead. See Migrating from deprecated APIs for more details." }, - "root": { "description": "Styles applied to the root element." }, "standard": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the root element", @@ -159,7 +145,11 @@ } }, "slotDescriptions": { + "action": "The component that renders the action slot.", "closeButton": "The component that renders the close button.", - "closeIcon": "The component that renders the close icon." + "closeIcon": "The component that renders the close icon.", + "icon": "The component that renders the icon slot.", + "message": "The component that renders the message slot.", + "root": "The component that renders the root slot." } } diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 2bbaf416f28055..9998cdc738da4b 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -10,10 +10,39 @@ export type AlertColor = 'success' | 'info' | 'warning' | 'error'; export interface AlertPropsVariantOverrides {} export interface AlertPropsColorOverrides {} + +export interface AlertRootSlotPropsOverrides {} + +export interface AlertIconSlotPropsOverrides {} + +export interface AlertMessageSlotPropsOverrides {} + +export interface AlertActionSlotPropsOverrides {} + export interface AlertCloseButtonSlotPropsOverrides {} export interface AlertCloseIconSlotPropsOverrides {} export interface AlertSlots { + /** + * The component that renders the root slot. + * @default Paper + */ + root: React.ElementType; + /** + * The component that renders the icon slot. + * @default div + */ + icon: React.ElementType; + /** + * The component that renders the message slot. + * @default div + */ + message: React.ElementType; + /** + * The component that renders the action slot. + * @default div + */ + action: React.ElementType; /** * The component that renders the close button. * @default IconButton @@ -29,11 +58,43 @@ export interface AlertSlots { export type AlertSlotsAndSlotProps = CreateSlotsAndSlotProps< AlertSlots, { + /** + * Props forwarded to the root slot. + * By default, the avaible props are based on the [Paper](https://mui.com/material-ui/api/paper/#props) component. + */ + root: SlotProps, AlertRootSlotPropsOverrides, AlertOwnerState>; + /** + * Props forwarded to the icon slot. + * By default, the avaible props are based on a div element. + */ + icon: SlotProps< + React.ElementType>, + AlertIconSlotPropsOverrides, + AlertOwnerState + >; + /** + * Props forwarded to the message slot. + * By default, the avaible props are based on a div element. + */ + message: SlotProps; + /** + * Props forwarded to the action slot. + * By default, the avaible props are based on a div element. + */ + action: SlotProps; + /** + * Props forwarded to the closeButton slot. + * By default, the avaible props are based on the [IconButton](https://mui.com/material-ui/api/icon-button/#props) component. + */ closeButton: SlotProps< React.ElementType, AlertCloseButtonSlotPropsOverrides, AlertOwnerState >; + /** + * Props forwarded to the closeIcon slot. + * By default, the avaible props are based on the [SvgIcon](https://mui.com/material-ui/api/svg-icon/#props) component. + */ closeIcon: SlotProps< React.ElementType, AlertCloseIconSlotPropsOverrides, diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 72fdd2f8f6e53d..9f1757e08ebbf9 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -373,16 +373,48 @@ Alert.propTypes /* remove-proptypes */ = { * @default {} */ slotProps: PropTypes.shape({ + action: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + component: PropTypes.elementType, + sx: PropTypes.oneOfType([ + PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]), + ), + PropTypes.func, + PropTypes.object, + ]), + }), + ]), closeButton: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), closeIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), + message: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ + component: PropTypes.elementType, + sx: PropTypes.oneOfType([ + PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]), + ), + PropTypes.func, + PropTypes.object, + ]), + }), + ]), + root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), }), /** * The components used for each slot inside. * @default {} */ slots: PropTypes.shape({ + action: PropTypes.elementType, closeButton: PropTypes.elementType, closeIcon: PropTypes.elementType, + icon: PropTypes.elementType, + message: PropTypes.elementType, + root: PropTypes.elementType, }), /** * The system prop that allows defining system overrides as well as additional CSS styles. diff --git a/packages/mui-material/src/Alert/Alert.spec.tsx b/packages/mui-material/src/Alert/Alert.spec.tsx index 349694b19f9ba4..e57e4f80d0e794 100644 --- a/packages/mui-material/src/Alert/Alert.spec.tsx +++ b/packages/mui-material/src/Alert/Alert.spec.tsx @@ -1,5 +1,7 @@ +import * as React from 'react'; import CloseRounded from '@mui/icons-material/CloseRounded'; import { createTheme } from '@mui/material'; +import Alert from '@mui/material/Alert'; createTheme({ components: { @@ -12,3 +14,26 @@ createTheme({ }, }, }); + +; From 43ef1a18706ed76d00597dc9bdc922bb8af4d92d Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Thu, 9 Jan 2025 10:03:57 +0700 Subject: [PATCH 3/5] fix slotProps types --- docs/pages/material-ui/api/alert.json | 2 +- packages/mui-material/src/Alert/Alert.d.ts | 12 ++++++++-- packages/mui-material/src/Alert/Alert.js | 28 ++-------------------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json index 51d9a243bef167..0a5490bb494611 100644 --- a/docs/pages/material-ui/api/alert.json +++ b/docs/pages/material-ui/api/alert.json @@ -50,7 +50,7 @@ "slotProps": { "type": { "name": "shape", - "description": "{ action?: func
| { component?: elementType, sx?: Array<func
| object
| bool>
| func
| object }, closeButton?: func
| object, closeIcon?: func
| object, icon?: func
| object, message?: func
| { component?: elementType, sx?: Array<func
| object
| bool>
| func
| object }, root?: func
| object }" + "description": "{ action?: func
| object, closeButton?: func
| object, closeIcon?: func
| object, icon?: func
| object, message?: func
| object, root?: func
| object }" }, "default": "{}" }, diff --git a/packages/mui-material/src/Alert/Alert.d.ts b/packages/mui-material/src/Alert/Alert.d.ts index 9998cdc738da4b..24b8f2e53655af 100644 --- a/packages/mui-material/src/Alert/Alert.d.ts +++ b/packages/mui-material/src/Alert/Alert.d.ts @@ -76,12 +76,20 @@ export type AlertSlotsAndSlotProps = CreateSlotsAndSlotProps< * Props forwarded to the message slot. * By default, the avaible props are based on a div element. */ - message: SlotProps; + message: SlotProps< + React.ElementType>, + AlertMessageSlotPropsOverrides, + AlertOwnerState + >; /** * Props forwarded to the action slot. * By default, the avaible props are based on a div element. */ - action: SlotProps; + action: SlotProps< + React.ElementType>, + AlertActionSlotPropsOverrides, + AlertOwnerState + >; /** * Props forwarded to the closeButton slot. * By default, the avaible props are based on the [IconButton](https://mui.com/material-ui/api/icon-button/#props) component. diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index 9f1757e08ebbf9..b15421e10a8cf3 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -373,35 +373,11 @@ Alert.propTypes /* remove-proptypes */ = { * @default {} */ slotProps: PropTypes.shape({ - action: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ - component: PropTypes.elementType, - sx: PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]), - ), - PropTypes.func, - PropTypes.object, - ]), - }), - ]), + action: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), closeButton: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), closeIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), icon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - message: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.shape({ - component: PropTypes.elementType, - sx: PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]), - ), - PropTypes.func, - PropTypes.object, - ]), - }), - ]), + message: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), }), /** From ccccc28dfcc1c03b934df6da8c8124ebd1cccd12 Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Thu, 9 Jan 2025 10:08:47 +0700 Subject: [PATCH 4/5] move to additionalProps --- packages/mui-material/src/Alert/Alert.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index b15421e10a8cf3..db8c63695f526f 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -209,6 +209,10 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { elementType: AlertRoot, externalForwardedProps, ownerState, + additionalProps: { + role, + elevation: 0, + }, }); const [IconSlot, iconSlotProps] = useSlot('icon', { @@ -245,7 +249,7 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { }); return ( - + {icon !== false ? ( {icon || iconMapping[severity] || defaultIconMapping[severity]} From 0e094741d9c943b1cc80b04c7e9ac9243fb80c85 Mon Sep 17 00:00:00 2001 From: siriwatknp Date: Fri, 10 Jan 2025 13:02:00 +0700 Subject: [PATCH 5/5] apply suggestion --- packages/mui-material/src/Alert/Alert.js | 7 +++++-- packages/mui-material/src/utils/useSlot.ts | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/mui-material/src/Alert/Alert.js b/packages/mui-material/src/Alert/Alert.js index db8c63695f526f..6ff5ba766a9a35 100644 --- a/packages/mui-material/src/Alert/Alert.js +++ b/packages/mui-material/src/Alert/Alert.js @@ -191,7 +191,6 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { const classes = useUtilityClasses(ownerState); const externalForwardedProps = { - ...other, slots: { closeButton: components.CloseButton, closeIcon: components.CloseIcon, @@ -205,9 +204,13 @@ const Alert = React.forwardRef(function Alert(inProps, ref) { const [RootSlot, rootSlotProps] = useSlot('root', { ref, + shouldForwardComponentProp: true, className: clsx(classes.root, className), elementType: AlertRoot, - externalForwardedProps, + externalForwardedProps: { + ...externalForwardedProps, + ...other, + }, ownerState, additionalProps: { role, diff --git a/packages/mui-material/src/utils/useSlot.ts b/packages/mui-material/src/utils/useSlot.ts index c3ac14e91956d5..90f9ffc4bdd180 100644 --- a/packages/mui-material/src/utils/useSlot.ts +++ b/packages/mui-material/src/utils/useSlot.ts @@ -82,6 +82,14 @@ export default function useSlot< * e.g. Autocomplete's listbox uses Popper + StyledComponent */ internalForwardedProps?: any; + /** + * Set to true if the `elementType` is a styled component of another Material UI component. + * + * For example, the AlertRoot is a styled component of the Paper component. + * This flag is used to forward the `component` and `slotProps.root.component` to the Paper component. + * Otherwise, the `component` prop will be converted to `as` prop which replaces the Paper component (the paper styles are gone). + */ + shouldForwardComponentProp?: boolean; }, ) { const { @@ -90,6 +98,7 @@ export default function useSlot< ownerState, externalForwardedProps, internalForwardedProps, + shouldForwardComponentProp = false, ...useSlotPropsParams } = parameters; const { @@ -127,9 +136,14 @@ export default function useSlot< ...(name === 'root' && !rootComponent && !slots[name] && internalForwardedProps), ...(name !== 'root' && !slots[name] && internalForwardedProps), ...mergedProps, - ...(LeafComponent && { - as: LeafComponent, - }), + ...(LeafComponent && + !shouldForwardComponentProp && { + as: LeafComponent, + }), + ...(LeafComponent && + shouldForwardComponentProp && { + component: LeafComponent, + }), ref, }, ownerState,