diff --git a/docs/pages/material-ui/api/alert.json b/docs/pages/material-ui/api/alert.json
index f1c0e452bb0968..0a5490bb494611 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
| object, closeButton?: func
| object, closeIcon?: func
| object, icon?: func
| object, message?: 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-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.d.ts b/packages/mui-material/src/Alert/Alert.d.ts
index 2bbaf416f28055..24b8f2e53655af 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,51 @@ 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<
+ React.ElementType>,
+ AlertMessageSlotPropsOverrides,
+ AlertOwnerState
+ >;
+ /**
+ * Props forwarded to the action slot.
+ * By default, the avaible props are based on a div element.
+ */
+ 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.
+ */
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 33c3f68e7b8f4c..6ff5ba766a9a35 100644
--- a/packages/mui-material/src/Alert/Alert.js
+++ b/packages/mui-material/src/Alert/Alert.js
@@ -202,6 +202,43 @@ 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,
+ ...other,
+ },
+ ownerState,
+ additionalProps: {
+ role,
+ elevation: 0,
+ },
+ });
+
+ 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 +252,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}
-
+
);
});
@@ -356,16 +380,24 @@ Alert.propTypes /* remove-proptypes */ = {
* @default {}
*/
slotProps: PropTypes.shape({
+ 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.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({
},
},
});
+
+;
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,
},
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,