diff --git a/docs/pages/api-docs/alert.json b/docs/pages/api-docs/alert.json index 78ef0408a53584..9e05e4951d0154 100644 --- a/docs/pages/api-docs/alert.json +++ b/docs/pages/api-docs/alert.json @@ -26,6 +26,7 @@ }, "default": "'success'" }, + "sx": { "type": { "name": "object" } }, "variant": { "type": { "name": "union", @@ -65,5 +66,5 @@ "filename": "/packages/material-ui/src/Alert/Alert.js", "inheritance": { "component": "Paper", "pathname": "/api/paper/" }, "demos": "", - "styledComponent": false + "styledComponent": true } diff --git a/docs/translations/api-docs/alert/alert.json b/docs/translations/api-docs/alert/alert.json index 3ea8dad4644191..fd550471f34f7c 100644 --- a/docs/translations/api-docs/alert/alert.json +++ b/docs/translations/api-docs/alert/alert.json @@ -11,6 +11,7 @@ "onClose": "Callback fired when the component requests to be closed. When provided and no action prop is set, a close icon button is displayed that triggers the callback when clicked.

Signature:
function(event: object) => void
event: The event source of the callback.", "role": "The ARIA role attribute of the element.", "severity": "The severity of the alert. This defines the color and icon used.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", "variant": "The variant to use." }, "classDescriptions": { diff --git a/packages/material-ui/src/Alert/Alert.d.ts b/packages/material-ui/src/Alert/Alert.d.ts index e13764ef9dd5fe..2771dd7f7c1ed2 100644 --- a/packages/material-ui/src/Alert/Alert.d.ts +++ b/packages/material-ui/src/Alert/Alert.d.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import { OverridableStringUnion } from '@material-ui/types'; -import { InternalStandardProps as StandardProps } from '..'; +import { SxProps } from '@material-ui/system'; +import { InternalStandardProps as StandardProps, Theme } from '..'; import { PaperProps } from '../Paper'; export type Color = 'success' | 'info' | 'warning' | 'error'; @@ -101,6 +102,10 @@ export interface AlertProps extends StandardProps { * @default 'standard' */ variant?: OverridableStringUnion; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; } export type AlertClassKey = keyof NonNullable; diff --git a/packages/material-ui/src/Alert/Alert.js b/packages/material-ui/src/Alert/Alert.js index 0eca98c4075665..ef81adb81b1d6c 100644 --- a/packages/material-ui/src/Alert/Alert.js +++ b/packages/material-ui/src/Alert/Alert.js @@ -1,11 +1,14 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -import { useThemeVariants } from '@material-ui/styles'; -import withStyles from '../styles/withStyles'; +import { deepmerge } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; +import experimentalStyled from '../styles/experimentalStyled'; +import useThemeProps from '../styles/useThemeProps'; import { darken, lighten } from '../styles/colorManipulator'; import capitalize from '../utils/capitalize'; import Paper from '../Paper'; +import alertClasses, { getAlertUtilityClass } from './alertClasses'; import IconButton from '../IconButton'; import SuccessOutlinedIcon from '../internal/svg-icons/SuccessOutlined'; import ReportProblemOutlinedIcon from '../internal/svg-icons/ReportProblemOutlined'; @@ -13,135 +16,122 @@ import ErrorOutlineIcon from '../internal/svg-icons/ErrorOutline'; import InfoOutlinedIcon from '../internal/svg-icons/InfoOutlined'; import CloseIcon from '../internal/svg-icons/Close'; -export const styles = (theme) => { +const overridesResolver = (props, styles) => { + const { styleProps } = props; + + return deepmerge(styles.root || {}, { + ...styles[styleProps.variant], + ...styles[`${styleProps.variant}${capitalize(styleProps.color || styleProps.severity)}`], + [`& .${alertClasses.icon}`]: styles.icon, + [`& .${alertClasses.message}`]: styles.message, + [`& .${alertClasses.action}`]: styles.action, + }); +}; + +const useUtilityClasses = (styleProps) => { + const { variant, color, severity, classes } = styleProps; + + const slots = { + root: ['root', `${variant}${capitalize(color || severity)}`, `${variant}`], + icon: ['icon'], + message: ['message'], + action: ['action'], + }; + + return composeClasses(slots, getAlertUtilityClass, classes); +}; + +const AlertRoot = experimentalStyled( + Paper, + {}, + { + name: 'MuiAlert', + slot: 'Root', + overridesResolver, + }, +)(({ theme, styleProps }) => { const getColor = theme.palette.mode === 'light' ? darken : lighten; const getBackgroundColor = theme.palette.mode === 'light' ? lighten : darken; + const color = styleProps.color || styleProps.severity; return { /* Styles applied to the root element. */ - root: { - ...theme.typography.body2, - borderRadius: theme.shape.borderRadius, - backgroundColor: 'transparent', - display: 'flex', - padding: '6px 16px', - }, - /* Styles applied to the root element if `variant="filled"`. */ - filled: {}, - /* Styles applied to the root element if `variant="outlined"`. */ - outlined: {}, - /* Styles applied to the root element if `variant="standard"`. */ - standard: {}, - /* Styles applied to the root element if `variant="standard"` and `color="success"`. */ - standardSuccess: { - color: getColor(theme.palette.success.main, 0.6), - backgroundColor: getBackgroundColor(theme.palette.success.main, 0.9), - '& $icon': { - color: theme.palette.success.main, - }, - }, - /* Styles applied to the root element if `variant="standard"` and `color="info"`. */ - standardInfo: { - color: getColor(theme.palette.info.main, 0.6), - backgroundColor: getBackgroundColor(theme.palette.info.main, 0.9), - '& $icon': { - color: theme.palette.info.main, - }, - }, - /* Styles applied to the root element if `variant="standard"` and `color="warning"`. */ - standardWarning: { - color: getColor(theme.palette.warning.main, 0.6), - backgroundColor: getBackgroundColor(theme.palette.warning.main, 0.9), - '& $icon': { - color: theme.palette.warning.main, - }, - }, - /* Styles applied to the root element if `variant="standard"` and `color="error"`. */ - standardError: { - color: getColor(theme.palette.error.main, 0.6), - backgroundColor: getBackgroundColor(theme.palette.error.main, 0.9), - '& $icon': { - color: theme.palette.error.main, - }, - }, - /* Styles applied to the root element if `variant="outlined"` and `color="success"`. */ - outlinedSuccess: { - color: getColor(theme.palette.success.main, 0.6), - border: `1px solid ${theme.palette.success.main}`, - '& $icon': { - color: theme.palette.success.main, - }, - }, - /* Styles applied to the root element if `variant="outlined"` and `color="info"`. */ - outlinedInfo: { - color: getColor(theme.palette.info.main, 0.6), - border: `1px solid ${theme.palette.info.main}`, - '& $icon': { - color: theme.palette.info.main, - }, - }, - /* Styles applied to the root element if `variant="outlined"` and `color="warning"`. */ - outlinedWarning: { - color: getColor(theme.palette.warning.main, 0.6), - border: `1px solid ${theme.palette.warning.main}`, - '& $icon': { - color: theme.palette.warning.main, - }, - }, - /* Styles applied to the root element if `variant="outlined"` and `color="error"`. */ - outlinedError: { - color: getColor(theme.palette.error.main, 0.6), - border: `1px solid ${theme.palette.error.main}`, - '& $icon': { - color: theme.palette.error.main, - }, - }, - /* Styles applied to the root element if `variant="filled"` and `color="success"`. */ - filledSuccess: { - color: '#fff', - fontWeight: theme.typography.fontWeightMedium, - backgroundColor: theme.palette.success.main, - }, - /* Styles applied to the root element if `variant="filled"` and `color="info"`. */ - filledInfo: { - color: '#fff', - fontWeight: theme.typography.fontWeightMedium, - backgroundColor: theme.palette.info.main, - }, - /* Styles applied to the root element if `variant="filled"` and `color="warning"`. */ - filledWarning: { - color: '#fff', - fontWeight: theme.typography.fontWeightMedium, - backgroundColor: theme.palette.warning.main, - }, - /* Styles applied to the root element if `variant="filled"` and `color="error"`. */ - filledError: { - color: '#fff', - fontWeight: theme.typography.fontWeightMedium, - backgroundColor: theme.palette.error.main, - }, - /* Styles applied to the icon wrapper element. */ - icon: { - marginRight: 12, - padding: '7px 0', - display: 'flex', - fontSize: 22, - opacity: 0.9, - }, - /* Styles applied to the message wrapper element. */ - message: { - padding: '8px 0', - }, - /* Styles applied to the action wrapper element if `action` is provided. */ - action: { - display: 'flex', - alignItems: 'center', - marginLeft: 'auto', - paddingLeft: 16, - marginRight: -8, - }, + ...theme.typography.body2, + borderRadius: theme.shape.borderRadius, + backgroundColor: 'transparent', + display: 'flex', + padding: '6px 16px', + /* Styles applied to the root element if variant="standard". */ + ...(color && + styleProps.variant === 'standard' && { + color: getColor(theme.palette[color].main, 0.6), + backgroundColor: getBackgroundColor(theme.palette[color].main, 0.9), + [`& .${alertClasses.icon}`]: { + color: theme.palette[color].main, + }, + }), + /* Styles applied to the root element if variant="outlined". */ + ...(color && + styleProps.variant === 'outlined' && { + color: getColor(theme.palette[color].main, 0.6), + border: `1px solid ${theme.palette[color].main}`, + [`& .${alertClasses.icon}`]: { + color: theme.palette[color].main, + }, + }), + /* Styles applied to the root element if variant="filled". */ + ...(color && + styleProps.variant === 'filled' && { + color: '#fff', + fontWeight: theme.typography.fontWeightMedium, + backgroundColor: theme.palette[color].main, + }), }; -}; +}); + +/* Styles applied to the icon wrapper element. */ +const AlertIcon = experimentalStyled( + 'div', + {}, + { + name: 'MuiAlert', + slot: 'Icon', + }, +)({ + marginRight: 12, + padding: '7px 0', + display: 'flex', + fontSize: 22, + opacity: 0.9, +}); + +/* Styles applied to the message wrapper element. */ +const AlertMessage = experimentalStyled( + 'div', + {}, + { + name: 'MuiAlert', + slot: 'Message', + }, +)({ + padding: '8px 0', +}); + +/* Styles applied to the action wrapper element if `action` is provided. */ +const AlertAction = experimentalStyled( + 'div', + {}, + { + name: 'MuiAlert', + slot: 'Action', + }, +)({ + display: 'flex', + alignItems: 'center', + marginLeft: 'auto', + paddingLeft: 16, + marginRight: -8, +}); const defaultIconMapping = { success: , @@ -150,11 +140,11 @@ const defaultIconMapping = { info: , }; -const Alert = React.forwardRef(function Alert(props, ref) { +const Alert = React.forwardRef(function Alert(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiAlert' }); const { action, children, - classes, className, closeText = 'Close', color, @@ -167,42 +157,36 @@ const Alert = React.forwardRef(function Alert(props, ref) { ...other } = props; - const themeVariantsClasses = useThemeVariants( - { - ...props, - closeText, - iconMapping, - role, - severity, - variant, - }, - 'MuiAlert', - ); + const styleProps = { + ...other, + variant, + color, + severity, + }; + + const classes = useUtilityClasses(styleProps); return ( - {icon !== false ? ( -
+ {icon || iconMapping[severity] || defaultIconMapping[severity]} -
+ ) : null} -
{children}
- {action != null ?
{action}
: null} + + {children} + + {action != null ? {action} : null} {action == null && onClose ? ( -
+ -
+ ) : null} -
+ ); }); @@ -284,6 +268,10 @@ Alert.propTypes = { * @default 'success' */ severity: PropTypes.oneOf(['error', 'info', 'success', 'warning']), + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, /** * The variant to use. * @default 'standard' @@ -294,4 +282,4 @@ Alert.propTypes = { ]), }; -export default withStyles(styles, { name: 'MuiAlert' })(Alert); +export default Alert; diff --git a/packages/material-ui/src/Alert/Alert.test.js b/packages/material-ui/src/Alert/Alert.test.js index 635ea6c1612023..6dd3f8573aa285 100644 --- a/packages/material-ui/src/Alert/Alert.test.js +++ b/packages/material-ui/src/Alert/Alert.test.js @@ -1,21 +1,20 @@ import * as React from 'react'; -import { getClasses, createMount, describeConformance } from 'test/utils'; +import { createMount, describeConformanceV5 } from 'test/utils'; +import classes from './alertClasses'; import Paper from '../Paper'; import Alert from './Alert'; describe('', () => { const mount = createMount(); - let classes; - before(() => { - classes = getClasses(); - }); - - describeConformance(, () => ({ + describeConformanceV5(, () => ({ classes, inheritComponent: Paper, mount, refInstanceof: window.HTMLDivElement, - skip: ['componentProp'], + muiName: 'MuiAlert', + testVariantProps: { variant: 'standard', color: 'success' }, + testDeepOverrides: { slotName: 'message', slotClassName: classes.message }, + skip: ['componentsProp'], })); }); diff --git a/packages/material-ui/src/Alert/alertClasses.d.ts b/packages/material-ui/src/Alert/alertClasses.d.ts new file mode 100644 index 00000000000000..0045864b3595e8 --- /dev/null +++ b/packages/material-ui/src/Alert/alertClasses.d.ts @@ -0,0 +1,24 @@ +export interface AlertClasses { + root: string; + action: string; + icon: string; + message: string; + filledSuccess: string; + filledInfo: string; + filledWarning: string; + filledError: string; + outlinedSuccess: string; + outlinedInfo: string; + outlinedWarning: string; + outlinedError: string; + standardSuccess: string; + standardInfo: string; + standardWarning: string; + standardError: string; +} + +declare const alertClasses: AlertClasses; + +export function getAlertUtilityClass(slot: string): string; + +export default alertClasses; diff --git a/packages/material-ui/src/Alert/alertClasses.js b/packages/material-ui/src/Alert/alertClasses.js new file mode 100644 index 00000000000000..fb3fc4518f5b4a --- /dev/null +++ b/packages/material-ui/src/Alert/alertClasses.js @@ -0,0 +1,26 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getAlertUtilityClass(slot) { + return generateUtilityClass('MuiAlert', slot); +} + +const alertClasses = generateUtilityClasses('MuiAlert', [ + 'root', + 'action', + 'icon', + 'message', + 'filledSuccess', + 'filledInfo', + 'filledWarning', + 'filledError', + 'outlinedSuccess', + 'outlinedInfo', + 'outlinedWarning', + 'outlinedError', + 'standardSuccess', + 'standardInfo', + 'standardWarning', + 'standardError', +]); + +export default alertClasses; diff --git a/packages/material-ui/src/Alert/index.d.ts b/packages/material-ui/src/Alert/index.d.ts index f8bf7e6b67860c..1543777bf847c1 100644 --- a/packages/material-ui/src/Alert/index.d.ts +++ b/packages/material-ui/src/Alert/index.d.ts @@ -1,2 +1,5 @@ export { default } from './Alert'; export * from './Alert'; + +export { default as alertClasses } from './alertClasses'; +export * from './alertClasses'; diff --git a/packages/material-ui/src/Alert/index.js b/packages/material-ui/src/Alert/index.js index becaea750b71e9..ae354db3f6bcac 100644 --- a/packages/material-ui/src/Alert/index.js +++ b/packages/material-ui/src/Alert/index.js @@ -1 +1,4 @@ export { default } from './Alert'; + +export { default as alertClasses } from './alertClasses'; +export * from './alertClasses';