diff --git a/docs/src/pages/system/basics/CssProp.tsx b/docs/src/pages/system/basics/CssProp.tsx deleted file mode 100644 index e38924331713df..00000000000000 --- a/docs/src/pages/system/basics/CssProp.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import styled, { ThemeProvider } from 'styled-components'; -import NoSsr from '@material-ui/core/NoSsr'; -import { createMuiTheme } from '@material-ui/core/styles'; -import { compose, spacing, palette, css } from '@material-ui/system'; - -const Box = styled.div` - ${css(compose(spacing, palette))} -`; - -const theme = createMuiTheme(); - -export default function CssProp() { - return ( - - - - CssProp - - - - ); -} diff --git a/docs/src/pages/system/basics/CssProp.js b/docs/src/pages/system/basics/SxProp.js similarity index 58% rename from docs/src/pages/system/basics/CssProp.js rename to docs/src/pages/system/basics/SxProp.js index e38924331713df..a8f57033d4f588 100644 --- a/docs/src/pages/system/basics/CssProp.js +++ b/docs/src/pages/system/basics/SxProp.js @@ -2,11 +2,11 @@ import React from 'react'; import styled, { ThemeProvider } from 'styled-components'; import NoSsr from '@material-ui/core/NoSsr'; import { createMuiTheme } from '@material-ui/core/styles'; -import { compose, spacing, palette, css } from '@material-ui/system'; +import { compose, spacing, palette, styleFunctionSx } from '@material-ui/system'; -const Box = styled.div` - ${css(compose(spacing, palette))} -`; +const styleFunction = styleFunctionSx(compose(spacing, palette)); + +const Box = styled.div(styleFunction); const theme = createMuiTheme(); @@ -14,7 +14,7 @@ export default function CssProp() { return ( - + CssProp diff --git a/docs/src/pages/system/basics/SxProp.tsx b/docs/src/pages/system/basics/SxProp.tsx new file mode 100644 index 00000000000000..4ae653c39e2279 --- /dev/null +++ b/docs/src/pages/system/basics/SxProp.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import styled, { ThemeProvider } from 'styled-components'; +import NoSsr from '@material-ui/core/NoSsr'; +import { createMuiTheme } from '@material-ui/core/styles'; +import { + compose, + spacing, + palette, + styleFunctionSx, + SpacingProps, + PaletteProps, +} from '@material-ui/system'; +import { CSSProperties } from '@material-ui/styles'; + +const styleFunction = styleFunctionSx(compose(spacing, palette)); + +type StyleFunctionProps = SpacingProps & PaletteProps & CSSProperties; + +type BoxProps = StyleFunctionProps & { + sx?: StyleFunctionProps; + // TODO: this should be removed once the css prop is dropped + css?: StyleFunctionProps; +}; + +const Box = styled.div(styleFunction); + +const theme = createMuiTheme(); + +export default function CssProp() { + return ( + + + + CssProp + + + + ); +} diff --git a/docs/src/pages/system/basics/basics.md b/docs/src/pages/system/basics/basics.md index 6204e3db63fa9a..b27b1174781252 100644 --- a/docs/src/pages/system/basics/basics.md +++ b/docs/src/pages/system/basics/basics.md @@ -319,12 +319,12 @@ In this example, the `variant` property supports all the keys present in `theme. {{"demo": "pages/system/basics/Variant.js", "defaultCodeOpen": true}} -## CSS property +## `sx` property -If you want to support custom CSS values, you can use the `css()` helper. -It will process the `css` property. +If you want to support custom CSS values, you can use the `styleFunctionSx()` helper. +It will process the `sx` property. -{{"demo": "pages/system/basics/CssProp.js", "defaultCodeOpen": true}} +{{"demo": "pages/system/basics/SxProp.js", "bg": true}} ## How it works diff --git a/docs/src/pages/system/flexbox/AlignContent.js b/docs/src/pages/system/flexbox/AlignContent.js index 5d23fc0ac51add..b9121eb6789d80 100644 --- a/docs/src/pages/system/flexbox/AlignContent.js +++ b/docs/src/pages/system/flexbox/AlignContent.js @@ -11,7 +11,7 @@ export default function AlignContent() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300, height: 200 }} + sx={{ maxWidth: 300, height: 200 }} > Item 1 @@ -42,7 +42,7 @@ export default function AlignContent() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300, height: 200 }} + sx={{ maxWidth: 300, height: 200 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/AlignContent.tsx b/docs/src/pages/system/flexbox/AlignContent.tsx index 5d23fc0ac51add..b9121eb6789d80 100644 --- a/docs/src/pages/system/flexbox/AlignContent.tsx +++ b/docs/src/pages/system/flexbox/AlignContent.tsx @@ -11,7 +11,7 @@ export default function AlignContent() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300, height: 200 }} + sx={{ maxWidth: 300, height: 200 }} > Item 1 @@ -42,7 +42,7 @@ export default function AlignContent() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300, height: 200 }} + sx={{ maxWidth: 300, height: 200 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/AlignItems.js b/docs/src/pages/system/flexbox/AlignItems.js index 25c34ed41ce25d..3435a243bc7465 100644 --- a/docs/src/pages/system/flexbox/AlignItems.js +++ b/docs/src/pages/system/flexbox/AlignItems.js @@ -10,7 +10,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 @@ -28,7 +28,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 @@ -46,7 +46,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/AlignItems.tsx b/docs/src/pages/system/flexbox/AlignItems.tsx index 25c34ed41ce25d..3435a243bc7465 100644 --- a/docs/src/pages/system/flexbox/AlignItems.tsx +++ b/docs/src/pages/system/flexbox/AlignItems.tsx @@ -10,7 +10,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 @@ -28,7 +28,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 @@ -46,7 +46,7 @@ export default function AlignItems() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/AlignSelf.js b/docs/src/pages/system/flexbox/AlignSelf.js index e8cb76053d0086..fb51d9a7f87277 100644 --- a/docs/src/pages/system/flexbox/AlignSelf.js +++ b/docs/src/pages/system/flexbox/AlignSelf.js @@ -10,7 +10,7 @@ export default function AlignSelf() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/AlignSelf.tsx b/docs/src/pages/system/flexbox/AlignSelf.tsx index e8cb76053d0086..fb51d9a7f87277 100644 --- a/docs/src/pages/system/flexbox/AlignSelf.tsx +++ b/docs/src/pages/system/flexbox/AlignSelf.tsx @@ -10,7 +10,7 @@ export default function AlignSelf() { p={1} m={1} bgcolor="background.paper" - css={{ height: 100 }} + sx={{ height: 100 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/FlexWrap.js b/docs/src/pages/system/flexbox/FlexWrap.js index 0d8f541f5a60e2..d774046c5b4612 100644 --- a/docs/src/pages/system/flexbox/FlexWrap.js +++ b/docs/src/pages/system/flexbox/FlexWrap.js @@ -10,7 +10,7 @@ export default function FlexWrap() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300 }} + sx={{ maxWidth: 300 }} > Item 1 @@ -37,7 +37,7 @@ export default function FlexWrap() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300 }} + sx={{ maxWidth: 300 }} > Item 1 diff --git a/docs/src/pages/system/flexbox/FlexWrap.tsx b/docs/src/pages/system/flexbox/FlexWrap.tsx index 0d8f541f5a60e2..d774046c5b4612 100644 --- a/docs/src/pages/system/flexbox/FlexWrap.tsx +++ b/docs/src/pages/system/flexbox/FlexWrap.tsx @@ -10,7 +10,7 @@ export default function FlexWrap() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300 }} + sx={{ maxWidth: 300 }} > Item 1 @@ -37,7 +37,7 @@ export default function FlexWrap() { p={1} m={1} bgcolor="background.paper" - css={{ maxWidth: 300 }} + sx={{ maxWidth: 300 }} > Item 1 diff --git a/packages/material-ui-system/src/css.js b/packages/material-ui-system/src/css.js deleted file mode 100644 index 930f128aca2519..00000000000000 --- a/packages/material-ui-system/src/css.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import merge from './merge'; - -function omit(input, fields) { - const output = {}; - - Object.keys(input).forEach((prop) => { - if (fields.indexOf(prop) === -1) { - output[prop] = input[prop]; - } - }); - - return output; -} - -function css(styleFunction) { - const newStyleFunction = (props) => { - const output = styleFunction(props); - - if (props.css) { - return { - ...merge(output, styleFunction({ theme: props.theme, ...props.css })), - ...omit(props.css, [styleFunction.filterProps]), - }; - } - - return output; - }; - - newStyleFunction.propTypes = - process.env.NODE_ENV !== 'production' - ? { - ...styleFunction.propTypes, - css: PropTypes.object, - } - : {}; - - newStyleFunction.filterProps = ['css', ...styleFunction.filterProps]; - - return newStyleFunction; -} - -export default css; diff --git a/packages/material-ui-system/src/css.test.js b/packages/material-ui-system/src/css.test.js deleted file mode 100644 index 528c22b734fe6a..00000000000000 --- a/packages/material-ui-system/src/css.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import { expect } from 'chai'; -import css from './css'; -import style from './style'; - -const textColor = style({ - prop: 'color', - themeKey: 'palette', -}); - -describe('css', () => { - it('should work', () => { - const palette = css(textColor); - - expect(palette.filterProps.length).to.equal(2); - expect( - palette({ - theme: {}, - css: { - color: 'red', - padding: 10, - }, - }), - ).to.deep.equal({ - padding: 10, - color: 'red', - }); - }); -}); diff --git a/packages/material-ui-system/src/index.d.ts b/packages/material-ui-system/src/index.d.ts index b842b8ae821ad9..17affbebca513a 100644 --- a/packages/material-ui-system/src/index.d.ts +++ b/packages/material-ui-system/src/index.d.ts @@ -53,10 +53,19 @@ export type ComposedStyleFunction>> = StyleFu >; export function compose>>(...args: T): ComposedStyleFunction; -// css.js +// styleFunctionSx.js + +/** + * @deprecated + * The css style function is deprecated. Use the styleFunctionSx instead. + */ export function css( styleFunction: StyleFunction -): StyleFunction }>; +): StyleFunction; sx?: Omit }>; + +export function styleFunctionSx( + styleFunction: StyleFunction +): StyleFunction; css?: Omit }>; export const display: SimpleStyleFunction< 'display' | 'displayPrint' | 'overflow' | 'textOverflow' | 'visibility' | 'whiteSpace' diff --git a/packages/material-ui-system/src/index.js b/packages/material-ui-system/src/index.js index c082887f574bb1..8e26584525191a 100644 --- a/packages/material-ui-system/src/index.js +++ b/packages/material-ui-system/src/index.js @@ -2,7 +2,8 @@ export { default as borders } from './borders'; export * from './borders'; export { default as breakpoints } from './breakpoints'; export { default as compose } from './compose'; -export { default as css } from './css'; +export { default as styleFunctionSx } from './styleFunctionSx'; +export * from './styleFunctionSx'; export { default as display } from './display'; export { default as flexbox } from './flexbox'; export * from './flexbox'; diff --git a/packages/material-ui-system/src/index.spec.tsx b/packages/material-ui-system/src/index.spec.tsx index 4c68b1dafe58fe..95ea9c28a1d13d 100644 --- a/packages/material-ui-system/src/index.spec.tsx +++ b/packages/material-ui-system/src/index.spec.tsx @@ -1,6 +1,6 @@ import { compose, - css, + styleFunctionSx, palette, StyleFunction, spacing, @@ -27,41 +27,41 @@ function composeTest() { styler({ color: 'test', spacing: 1 }); } -function cssTest() { +function sxTest() { function styleFunction(props: { color?: string; spacing?: number; theme?: object }) { return {}; } - const wideOrNarrowStyleFunction = css(styleFunction); + const wideOrNarrowStyleFunction = styleFunctionSx(styleFunction); // narrow - wideOrNarrowStyleFunction({ theme: {}, css: { color: 'blue', spacing: 2 } }); - // wide, undesire: `css` is required, marking it as optional breaks system/basics/#css-property - wideOrNarrowStyleFunction({ theme: {}, color: 'blue', spacing: 2, css: {} }); + wideOrNarrowStyleFunction({ theme: {}, sx: { color: 'blue', spacing: 2 } }); + // wide + wideOrNarrowStyleFunction({ theme: {}, color: 'blue', spacing: 2 }); // wide and narrow - wideOrNarrowStyleFunction({ theme: {}, css: { color: 'blue', spacing: 2 }, color: 'red' }); + wideOrNarrowStyleFunction({ theme: {}, sx: { color: 'blue', spacing: 2 }, color: 'red' }); } /** - * marking a prop as required requires it in props object and `css` object + * marking a prop as required requires it in props object and `sx` object * - * This is not equivalent to the implementation. Ideally `css` would be optional - * but that breaks system/basics/#css-property + * This is not equivalent to the implementation. Ideally `sx` would be optional + * but that breaks system/basics/#sx-property */ -function cssRequiredTest() { +function sxRequiredTest() { function styleRequiredFunction(props: { color: string }) { return {}; } - const style = css(styleRequiredFunction); + const style = styleFunctionSx(styleRequiredFunction); style({ color: 'red', // @ts-expect-error - css: {}, + sx: {}, }); // @ts-expect-error - style({ css: { color: 'red' } }); - style({ color: 'blue', css: { color: 'red' } }); + style({ sx: { color: 'red' } }); + style({ color: 'blue', sx: { color: 'red' } }); } /** diff --git a/packages/material-ui-system/src/styleFunctionSx.js b/packages/material-ui-system/src/styleFunctionSx.js new file mode 100644 index 00000000000000..2a1996a2335d4c --- /dev/null +++ b/packages/material-ui-system/src/styleFunctionSx.js @@ -0,0 +1,76 @@ +import PropTypes from 'prop-types'; +import { chainPropTypes } from '@material-ui/utils'; +import merge from './merge'; + +function omit(input, fields) { + const output = {}; + + Object.keys(input).forEach((prop) => { + if (fields.indexOf(prop) === -1) { + output[prop] = input[prop]; + } + }); + + return output; +} + +let warnedOnce = false; + +function styleFunctionSx(styleFunction) { + const newStyleFunction = (props) => { + const output = styleFunction(props); + + if (props.css) { + return { + ...merge(output, styleFunction({ theme: props.theme, ...props.css })), + ...omit(props.css, [styleFunction.filterProps]), + }; + } + + if (props.sx) { + return { + ...merge(output, styleFunction({ theme: props.theme, ...props.sx })), + ...omit(props.sx, [styleFunction.filterProps]), + }; + } + + return output; + }; + + newStyleFunction.propTypes = + process.env.NODE_ENV !== 'production' + ? { + ...styleFunction.propTypes, + css: chainPropTypes(PropTypes.object, (props) => { + if (!warnedOnce && props.css !== undefined) { + warnedOnce = true; + return new Error( + 'Material-UI: The `css` prop is deprecated, please use the `sx` prop instead.', + ); + } + return null; + }), + sx: PropTypes.object, + } + : {}; + + newStyleFunction.filterProps = ['css', 'sx', ...styleFunction.filterProps]; + + return newStyleFunction; +} + +/** + * + * @deprecated + * The css style function is deprecated. Use the `styleFunctionSx` instead. + */ +export function css(styleFunction) { + if (process.env.NODE_ENV !== 'production') { + console.warn( + 'Material-UI: The `css` function is deprecated. Use the `styleFunctionSx` instead.', + ); + } + return styleFunctionSx(styleFunction); +} + +export default styleFunctionSx; diff --git a/packages/material-ui-system/src/styleFunctionSx.test.js b/packages/material-ui-system/src/styleFunctionSx.test.js new file mode 100644 index 00000000000000..d337c16db10737 --- /dev/null +++ b/packages/material-ui-system/src/styleFunctionSx.test.js @@ -0,0 +1,46 @@ +import { expect } from 'chai'; +import { consoleWarnMock } from 'test/utils/consoleErrorMock'; +import styleFunctionSx, { css } from './styleFunctionSx'; +import style from './style'; + +const textColor = style({ + prop: 'color', + themeKey: 'palette', +}); + +describe('styleFunctionSx', () => { + beforeEach(() => { + consoleWarnMock.spy(); + }); + + afterEach(() => { + consoleWarnMock.reset(); + }); + + it('should work', () => { + const palette = styleFunctionSx(textColor); + + expect(palette.filterProps.length).to.equal(3); + expect( + palette({ + theme: {}, + sx: { + color: 'red', + padding: 10, + }, + }), + ).to.deep.equal({ + padding: 10, + color: 'red', + }); + }); + + it('should warn if deprecated css is used', () => { + css(textColor); + + expect(consoleWarnMock.callCount()).to.equal(1); + expect(consoleWarnMock.messages()[0]).to.include( + 'Material-UI: The `css` function is deprecated. Use the `styleFunctionSx` instead.', + ); + }); +}); diff --git a/packages/material-ui/src/Box/Box.d.ts b/packages/material-ui/src/Box/Box.d.ts index 2eb664df6c9d61..b9e809d18c4763 100644 --- a/packages/material-ui/src/Box/Box.d.ts +++ b/packages/material-ui/src/Box/Box.d.ts @@ -37,8 +37,13 @@ export interface BoxProps extends ElementProps, SystemProps { // styled API component?: React.ElementType; clone?: boolean; - // workaround for https://github.com/mui-org/material-ui/pull/15611 + /** + * @deprecated + * Use the `sx` prop instead + */ css?: SystemProps; + // workaround for https://github.com/mui-org/material-ui/pull/15611 + sx?: SystemProps; } declare const Box: React.ComponentType; diff --git a/packages/material-ui/src/Box/Box.js b/packages/material-ui/src/Box/Box.js index 10b85801656fa9..49cd9456844839 100644 --- a/packages/material-ui/src/Box/Box.js +++ b/packages/material-ui/src/Box/Box.js @@ -10,11 +10,11 @@ import { sizing, spacing, typography, - css, + styleFunctionSx, } from '@material-ui/system'; import styled from '../styles/styled'; -export const styleFunction = css( +export const styleFunction = styleFunctionSx( compose( borders, display, diff --git a/packages/material-ui/src/Box/Box.test.js b/packages/material-ui/src/Box/Box.test.js index 72189549bd2adf..aba6865c9f943a 100644 --- a/packages/material-ui/src/Box/Box.test.js +++ b/packages/material-ui/src/Box/Box.test.js @@ -1,6 +1,8 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; import { expect } from 'chai'; import { createClientRender } from 'test/utils/createClientRender'; +import consoleErrorMock from 'test/utils/consoleErrorMock'; import createMount from 'test/utils/createMount'; import describeConformance from '@material-ui/core/test-utils/describeConformance'; import Box from './Box'; @@ -9,6 +11,14 @@ describe('', () => { const mount = createMount(); const render = createClientRender(); + beforeEach(() => { + consoleErrorMock.spy(); + }); + + afterEach(() => { + consoleErrorMock.reset(); + }); + describeConformance(, () => ({ mount, only: ['refForwarding'], @@ -47,4 +57,13 @@ describe('', () => { expect(element.getAttribute('font-family')).to.equal(null); expect(element.getAttribute('font-size')).to.equal(null); }); + + it('warns if the css prop is used ', () => { + PropTypes.checkPropTypes(Box.propTypes, { css: { m: 1, p: 1 } }, 'props', 'MockedBox'); + + expect(consoleErrorMock.callCount()).to.equal(1); + expect(consoleErrorMock.messages()[0]).to.include( + 'Material-UI: The `css` prop is deprecated, please use the `sx` prop instead.', + ); + }); });