diff --git a/docs/pages/api-docs/step-label.json b/docs/pages/api-docs/step-label.json index d3f365f4806cd6..5ef791c7597688 100644 --- a/docs/pages/api-docs/step-label.json +++ b/docs/pages/api-docs/step-label.json @@ -6,7 +6,8 @@ "icon": { "type": { "name": "node" } }, "optional": { "type": { "name": "node" } }, "StepIconComponent": { "type": { "name": "elementType" } }, - "StepIconProps": { "type": { "name": "object" } } + "StepIconProps": { "type": { "name": "object" } }, + "sx": { "type": { "name": "object" } } }, "name": "StepLabel", "styles": { @@ -31,6 +32,6 @@ "filename": "/packages/material-ui/src/StepLabel/StepLabel.js", "inheritance": null, "demos": "", - "styledComponent": false, + "styledComponent": true, "cssComponent": false } diff --git a/docs/translations/api-docs/step-label/step-label.json b/docs/translations/api-docs/step-label/step-label.json index 3b68832166d09e..b2c54b7c3995a6 100644 --- a/docs/translations/api-docs/step-label/step-label.json +++ b/docs/translations/api-docs/step-label/step-label.json @@ -7,7 +7,8 @@ "icon": "Override the default label of the step icon.", "optional": "The optional node to display.", "StepIconComponent": "The component to render in place of the StepIcon.", - "StepIconProps": "Props applied to the StepIcon element." + "StepIconProps": "Props applied to the StepIcon element.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details." }, "classDescriptions": { "root": { "description": "Styles applied to the root element." }, diff --git a/packages/material-ui/src/Step/Step.test.js b/packages/material-ui/src/Step/Step.test.js index d45a9b39096641..cf867cbc529f4b 100644 --- a/packages/material-ui/src/Step/Step.test.js +++ b/packages/material-ui/src/Step/Step.test.js @@ -1,21 +1,18 @@ import * as React from 'react'; import { expect } from 'chai'; import { getClasses, createMount, createClientRender, describeConformanceV5 } from 'test/utils'; -import Step from './Step'; -import Stepper from '../Stepper'; -import StepLabel from '../StepLabel'; -import StepButton from '../StepButton'; -import classes from './stepClasses'; +import Step, { stepClasses as classes } from '@material-ui/core/Step'; +import Stepper from '@material-ui/core/Stepper'; +import StepLabel, { stepLabelClasses } from '@material-ui/core/StepLabel'; +import StepButton from '@material-ui/core/StepButton'; describe('', () => { let stepButtonClasses; - let stepLabelClasses; const render = createClientRender(); const mount = createMount(); before(() => { stepButtonClasses = getClasses(); - stepLabelClasses = getClasses(); }); describeConformanceV5(, () => ({ diff --git a/packages/material-ui/src/StepButton/StepButton.test.js b/packages/material-ui/src/StepButton/StepButton.test.js index 672d909649c243..65c977a3b1dfbc 100644 --- a/packages/material-ui/src/StepButton/StepButton.test.js +++ b/packages/material-ui/src/StepButton/StepButton.test.js @@ -5,17 +5,15 @@ import { getClasses, createMount, createClientRender, describeConformance } from import { fireEvent } from '@testing-library/dom'; import StepButton from './StepButton'; import Step from '../Step'; -import StepLabel from '../StepLabel'; +import StepLabel, { stepLabelClasses } from '../StepLabel'; import ButtonBase from '../ButtonBase'; describe('', () => { let classes; - let stepLabelClasses; const render = createClientRender(); before(() => { classes = getClasses(); - stepLabelClasses = getClasses(); }); describe('internals', () => { diff --git a/packages/material-ui/src/StepLabel/StepLabel.d.ts b/packages/material-ui/src/StepLabel/StepLabel.d.ts index 093d0a52eb38ed..68b71aa3149b88 100644 --- a/packages/material-ui/src/StepLabel/StepLabel.d.ts +++ b/packages/material-ui/src/StepLabel/StepLabel.d.ts @@ -1,6 +1,8 @@ import * as React from 'react'; +import { SxProps } from '@material-ui/system'; import { InternalStandardProps as StandardProps } from '..'; import { StepIconProps } from '../StepIcon'; +import { Theme } from '../styles'; export interface StepLabelProps extends StandardProps> { /** @@ -55,6 +57,10 @@ export interface StepLabelProps extends StandardProps; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; } export type StepLabelClasskey = keyof NonNullable; diff --git a/packages/material-ui/src/StepLabel/StepLabel.js b/packages/material-ui/src/StepLabel/StepLabel.js index 16e3dcae9c072e..82e2120297f556 100644 --- a/packages/material-ui/src/StepLabel/StepLabel.js +++ b/packages/material-ui/src/StepLabel/StepLabel.js @@ -1,82 +1,147 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; -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 Typography from '../Typography'; import StepIcon from '../StepIcon'; import StepperContext from '../Stepper/StepperContext'; import StepContext from '../Step/StepContext'; +import stepLabelClasses, { getStepLabelUtilityClass } from './stepLabelClasses'; -export const styles = (theme) => ({ - /* Styles applied to the root element. */ - root: { - display: 'flex', - alignItems: 'center', - '&$alternativeLabel': { - flexDirection: 'column', - }, - '&$disabled': { - cursor: 'default', +const overridesResolver = (props, styles) => { + const { styleProps } = props; + + return deepmerge( + { + ...styles[styleProps.orientation], + [`& .${stepLabelClasses.label}`]: styles.label, + [`& .${stepLabelClasses.iconContainer}`]: styles.iconContainer, + [`& .${stepLabelClasses.labelContainer}`]: styles.labelContainer, }, + styles.root || {}, + ); +}; + +const useUtilityClasses = (styleProps) => { + const { classes, orientation, active, completed, error, disabled, alternativeLabel } = styleProps; + + const slots = { + root: [ + 'root', + orientation, + error && 'error', + disabled && 'disabled', + alternativeLabel && 'alternativeLabel', + ], + label: [ + 'label', + active && 'active', + completed && 'completed', + error && 'error', + disabled && 'disabled', + alternativeLabel && 'alternativeLabel', + ], + iconContainer: ['iconContainer', alternativeLabel && 'alternativeLabel'], + labelContainer: ['labelContainer'], + }; + + return composeClasses(slots, getStepLabelUtilityClass, classes); +}; + +const StepLabelRoot = experimentalStyled( + 'span', + {}, + { + name: 'MuiStepLabel', + slot: 'Root', + overridesResolver, + }, +)(({ styleProps }) => ({ + /* Styles applied to the root element. */ + display: 'flex', + alignItems: 'center', + [`&.${stepLabelClasses.alternativeLabel}`]: { + flexDirection: 'column', + }, + [`&.${stepLabelClasses.disabled}`]: { + cursor: 'default', }, - /* Styles applied to the root element if `orientation="horizontal"`. */ - horizontal: {}, /* Styles applied to the root element if `orientation="vertical"`. */ - vertical: { + ...(styleProps.orientation === 'vertical' && { textAlign: 'left', padding: '8px 0', + }), +})); + +const StepLabelLabel = experimentalStyled( + Typography, + {}, + { + name: 'MuiStepLabel', + slot: 'Label', + overridesResolver, }, +)(({ theme }) => ({ /* Styles applied to the Typography component that wraps `children`. */ - label: { - transition: theme.transitions.create('color', { - duration: theme.transitions.duration.shortest, - }), - '&$active': { - color: theme.palette.text.primary, - fontWeight: 500, - }, - '&$completed': { - color: theme.palette.text.primary, - fontWeight: 500, - }, - '&$alternativeLabel': { - textAlign: 'center', - marginTop: 16, - }, - '&$error': { - color: theme.palette.error.main, - }, + transition: theme.transitions.create('color', { + duration: theme.transitions.duration.shortest, + }), + [`&.${stepLabelClasses.active}`]: { + color: theme.palette.text.primary, + fontWeight: 500, + }, + [`&.${stepLabelClasses.completed}`]: { + color: theme.palette.text.primary, + fontWeight: 500, + }, + [`&.${stepLabelClasses.alternativeLabel}`]: { + textAlign: 'center', + marginTop: 16, }, - /* Pseudo-class applied to the `Typography` component if `active={true}`. */ - active: {}, - /* Pseudo-class applied to the `Typography` component if `completed={true}`. */ - completed: {}, - /* Pseudo-class applied to the root element and `Typography` component if `error={true}`. */ - error: {}, - /* Pseudo-class applied to the root element and `Typography` component if `disabled={true}`. */ - disabled: {}, + [`&.${stepLabelClasses.error}`]: { + color: theme.palette.error.main, + }, +})); + +const StepLabelIconContainer = experimentalStyled( + 'span', + {}, + { + name: 'MuiStepLabel', + slot: 'IconContainer', + overridesResolver, + }, +)(() => ({ /* Styles applied to the `icon` container element. */ - iconContainer: { - flexShrink: 0, // Fix IE11 issue - display: 'flex', - paddingRight: 8, - '&$alternativeLabel': { - paddingRight: 0, - }, + flexShrink: 0, // Fix IE11 issue + display: 'flex', + paddingRight: 8, + [`&.${stepLabelClasses.alternativeLabel}`]: { + paddingRight: 0, }, - /* Pseudo-class applied to the root and icon container and `Typography` if `alternativeLabel={true}`. */ - alternativeLabel: {}, - /* Styles applied to the container element which wraps `Typography` and `optional`. */ - labelContainer: { - width: '100%', - color: theme.palette.text.secondary, +})); + +const StepLabelLabelContainer = experimentalStyled( + 'span', + {}, + { + name: 'MuiStepLabel', + slot: 'LabelContainer', + overridesResolver, }, -}); +)(({ theme }) => ({ + /* Styles applied to the container element which wraps `Typography` and `optional`. */ + width: '100%', + color: theme.palette.text.secondary, +})); -const StepLabel = React.forwardRef(function StepLabel(props, ref) { +const StepLabel = React.forwardRef(function StepLabel(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiStepLabel' }); const { children, - classes, className, error = false, icon: iconProp, @@ -96,27 +161,27 @@ const StepLabel = React.forwardRef(function StepLabel(props, ref) { StepIconComponent = StepIcon; } + const styleProps = { + ...props, + orientation, + active, + completed, + error, + disabled, + alternativeLabel, + }; + + const classes = useUtilityClasses(styleProps); + return ( - {icon || StepIconComponent ? ( - + - + ) : null} - + {children ? ( - {children} - + ) : null} {optional} - - + + ); }); @@ -186,8 +247,12 @@ StepLabel.propTypes /* remove-proptypes */ = { * Props applied to the [`StepIcon`](/api/step-icon/) element. */ StepIconProps: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.object, }; StepLabel.muiName = 'StepLabel'; -export default withStyles(styles, { name: 'MuiStepLabel' })(StepLabel); +export default StepLabel; diff --git a/packages/material-ui/src/StepLabel/StepLabel.test.js b/packages/material-ui/src/StepLabel/StepLabel.test.js index 306b03955a1142..081e7f94e831d2 100644 --- a/packages/material-ui/src/StepLabel/StepLabel.test.js +++ b/packages/material-ui/src/StepLabel/StepLabel.test.js @@ -1,27 +1,25 @@ import * as React from 'react'; import { expect } from 'chai'; -import { getClasses, createClientRender, createMount, describeConformance } from 'test/utils'; -import Typography, { typographyClasses } from '../Typography'; -import Stepper from '../Stepper'; -import Step from '../Step'; -import { stepIconClasses as iconClasses } from '../StepIcon'; -import StepLabel from './StepLabel'; +import { createClientRender, createMount, describeConformanceV5 } from 'test/utils'; +import Typography, { typographyClasses } from '@material-ui/core/Typography'; +import Stepper from '@material-ui/core/Stepper'; +import Step from '@material-ui/core/Step'; +import { stepIconClasses as iconClasses } from '@material-ui/core/StepIcon'; +import StepLabel, { stepLabelClasses as classes } from '@material-ui/core/StepLabel'; describe('', () => { - let classes; const mount = createMount(); const render = createClientRender(); - before(() => { - classes = getClasses(); - }); - - describeConformance(, () => ({ + describeConformanceV5(, () => ({ classes, inheritComponent: 'span', + muiName: 'MuiStepLabel', mount, + render, refInstanceof: window.HTMLSpanElement, - skip: ['componentProp'], + testVariantProps: { error: true }, + skip: ['componentProp', 'componentsProp'], })); describe('label content', () => { diff --git a/packages/material-ui/src/StepLabel/index.d.ts b/packages/material-ui/src/StepLabel/index.d.ts index 21db69591a757a..f0a00aaa26d8dd 100644 --- a/packages/material-ui/src/StepLabel/index.d.ts +++ b/packages/material-ui/src/StepLabel/index.d.ts @@ -1,2 +1,5 @@ export { default } from './StepLabel'; export * from './StepLabel'; + +export { default as stepLabelClasses } from './stepLabelClasses'; +export * from './stepLabelClasses'; diff --git a/packages/material-ui/src/StepLabel/index.js b/packages/material-ui/src/StepLabel/index.js index a7565fa6e0f180..b60fad9c328271 100644 --- a/packages/material-ui/src/StepLabel/index.js +++ b/packages/material-ui/src/StepLabel/index.js @@ -1 +1,4 @@ export { default } from './StepLabel'; + +export { default as stepLabelClasses } from './stepLabelClasses'; +export * from './stepLabelClasses'; diff --git a/packages/material-ui/src/StepLabel/stepLabelClasses.d.ts b/packages/material-ui/src/StepLabel/stepLabelClasses.d.ts new file mode 100644 index 00000000000000..e33f51b5eceb1a --- /dev/null +++ b/packages/material-ui/src/StepLabel/stepLabelClasses.d.ts @@ -0,0 +1,7 @@ +import { StepLabelClasskey } from './StepLabel'; + +declare const stepLabelClasses: Record; + +export function getStepLabelUtilityClass(slot: string): string; + +export default stepLabelClasses; diff --git a/packages/material-ui/src/StepLabel/stepLabelClasses.js b/packages/material-ui/src/StepLabel/stepLabelClasses.js new file mode 100644 index 00000000000000..560f8868a8ccbf --- /dev/null +++ b/packages/material-ui/src/StepLabel/stepLabelClasses.js @@ -0,0 +1,21 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getStepLabelUtilityClass(slot) { + return generateUtilityClass('MuiStepLabel', slot); +} + +const stepLabelClasses = generateUtilityClasses('MuiStepLabel', [ + 'root', + 'horizontal', + 'vertical', + 'label', + 'active', + 'completed', + 'error', + 'disabled', + 'iconContainer', + 'alternativeLabel', + 'labelContainer', +]); + +export default stepLabelClasses;