From 8c0f63efe30746de33422bfb3a5b2e50f6aed743 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Thu, 25 Nov 2021 08:36:44 +0100 Subject: [PATCH] [system] Add `experimental_sx` utility (#29833) --- docs/src/pages/system/styled/UsingWithSx.js | 33 ++++ docs/src/pages/system/styled/UsingWithSx.tsx | 33 ++++ .../system/styled/UsingWithSx.tsx.preview | 3 + docs/src/pages/system/styled/styled.md | 10 ++ packages/mui-material/src/styles/index.d.ts | 1 + packages/mui-material/src/styles/index.js | 1 + .../mui-material/src/styles/index.spec.ts | 27 +++ packages/mui-system/src/index.d.ts | 2 + packages/mui-system/src/index.js | 1 + packages/mui-system/src/sx/index.d.ts | 1 + packages/mui-system/src/sx/index.js | 1 + packages/mui-system/src/sx/sx.d.ts | 4 + packages/mui-system/src/sx/sx.js | 7 + packages/mui-system/src/sx/sx.spec.ts | 10 ++ packages/mui-system/src/sx/sx.test.js | 158 ++++++++++++++++++ 15 files changed, 292 insertions(+) create mode 100644 docs/src/pages/system/styled/UsingWithSx.js create mode 100644 docs/src/pages/system/styled/UsingWithSx.tsx create mode 100644 docs/src/pages/system/styled/UsingWithSx.tsx.preview create mode 100644 packages/mui-material/src/styles/index.spec.ts create mode 100644 packages/mui-system/src/sx/index.d.ts create mode 100644 packages/mui-system/src/sx/index.js create mode 100644 packages/mui-system/src/sx/sx.d.ts create mode 100644 packages/mui-system/src/sx/sx.js create mode 100644 packages/mui-system/src/sx/sx.spec.ts create mode 100644 packages/mui-system/src/sx/sx.test.js diff --git a/docs/src/pages/system/styled/UsingWithSx.js b/docs/src/pages/system/styled/UsingWithSx.js new file mode 100644 index 00000000000000..9e50b6d44f5f71 --- /dev/null +++ b/docs/src/pages/system/styled/UsingWithSx.js @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { + styled, + createTheme, + ThemeProvider, + experimental_sx as sx, +} from '@mui/system'; + +const customTheme = createTheme({ + palette: { + primary: { + main: '#1976d2', + contrastText: 'white', + }, + }, +}); + +const MyThemeComponent = styled('div')( + sx({ + color: 'primary.contrastText', + backgroundColor: 'primary.main', + padding: 1, + borderRadius: 1, + }), +); + +export default function ThemeUsage() { + return ( + + Styled div with theme + + ); +} diff --git a/docs/src/pages/system/styled/UsingWithSx.tsx b/docs/src/pages/system/styled/UsingWithSx.tsx new file mode 100644 index 00000000000000..9e50b6d44f5f71 --- /dev/null +++ b/docs/src/pages/system/styled/UsingWithSx.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { + styled, + createTheme, + ThemeProvider, + experimental_sx as sx, +} from '@mui/system'; + +const customTheme = createTheme({ + palette: { + primary: { + main: '#1976d2', + contrastText: 'white', + }, + }, +}); + +const MyThemeComponent = styled('div')( + sx({ + color: 'primary.contrastText', + backgroundColor: 'primary.main', + padding: 1, + borderRadius: 1, + }), +); + +export default function ThemeUsage() { + return ( + + Styled div with theme + + ); +} diff --git a/docs/src/pages/system/styled/UsingWithSx.tsx.preview b/docs/src/pages/system/styled/UsingWithSx.tsx.preview new file mode 100644 index 00000000000000..bcce4bed86691a --- /dev/null +++ b/docs/src/pages/system/styled/UsingWithSx.tsx.preview @@ -0,0 +1,3 @@ + + Styled div with theme + \ No newline at end of file diff --git a/docs/src/pages/system/styled/styled.md b/docs/src/pages/system/styled/styled.md index e3a744485ac697..b05e3af7227324 100644 --- a/docs/src/pages/system/styled/styled.md +++ b/docs/src/pages/system/styled/styled.md @@ -196,6 +196,16 @@ const MyStyledButton = (props) => ( }) ``` +### How can I use the `sx` syntax with the `styled()` utility? + +If you are one of those who prefers the `sx` syntax and wants to use it in both the `sx` prop and the `styled()` utility, you can use the `experimental_sx` utility from the `@mui/system`: + +{{"demo": "pages/system/styled/UsingWithSx.js", "defaultCodeOpen": true}} + +The overhead added by using the `experimental_sx` utility is the same as if you were to use the `sx` prop on the component. + +> Note: You can use `experimental_sx` outside of the `styled()` utility, too; e.g., for defining `variants` in your custom theme. + ## How to use components selector API If you've ever used the `styled()` API of either [`emotion`](https://emotion.sh/docs/styled#targeting-another-emotion-component) or [`styled-components`](https://styled-components.com/docs/advanced#referring-to-other-components), you should have been able to use components as selectors. diff --git a/packages/mui-material/src/styles/index.d.ts b/packages/mui-material/src/styles/index.d.ts index e34edebff15e41..c72561451bded4 100644 --- a/packages/mui-material/src/styles/index.d.ts +++ b/packages/mui-material/src/styles/index.d.ts @@ -55,6 +55,7 @@ export { lighten, ColorFormat, ColorObject, + experimental_sx, } from '@mui/system'; export { default as useTheme } from './useTheme'; export { default as useThemeProps } from './useThemeProps'; diff --git a/packages/mui-material/src/styles/index.js b/packages/mui-material/src/styles/index.js index 30cf95c3056de2..5d19f3961bd5e1 100644 --- a/packages/mui-material/src/styles/index.js +++ b/packages/mui-material/src/styles/index.js @@ -13,6 +13,7 @@ export { lighten, css, keyframes, + experimental_sx, } from '@mui/system'; export { default as createTheme, createMuiTheme } from './createTheme'; export { default as unstable_createMuiStrictModeTheme } from './createMuiStrictModeTheme'; diff --git a/packages/mui-material/src/styles/index.spec.ts b/packages/mui-material/src/styles/index.spec.ts new file mode 100644 index 00000000000000..537faa39dadef5 --- /dev/null +++ b/packages/mui-material/src/styles/index.spec.ts @@ -0,0 +1,27 @@ +import { experimental_sx as sx, styled, createTheme } from '@mui/material/styles'; + +// Can use the experimental_sx in the styled() utility +const Test = styled('div')( + sx({ + color: 'primary.main', + bgcolor: 'primary.light', + m: 2, + }), +); + +// Can use the experimental_sx in the theme's variants +const customTheme = createTheme({ + components: { + MuiButton: { + variants: [ + { + props: {}, + style: sx({ + m: 2, + p: 1, + }), + }, + ], + }, + }, +}); diff --git a/packages/mui-system/src/index.d.ts b/packages/mui-system/src/index.d.ts index 2f13c2b451b315..3b902cdd7c071e 100644 --- a/packages/mui-system/src/index.d.ts +++ b/packages/mui-system/src/index.d.ts @@ -121,6 +121,8 @@ export { } from './styleFunctionSx'; export * from './styleFunctionSx'; +export { default as experimental_sx } from './sx'; + export { default as Box } from './Box'; export * from './Box'; diff --git a/packages/mui-system/src/index.js b/packages/mui-system/src/index.js index fb3f98e4f87601..2dab50a6eef8bf 100644 --- a/packages/mui-system/src/index.js +++ b/packages/mui-system/src/index.js @@ -29,6 +29,7 @@ export { default as unstable_styleFunctionSx, extendSxProp as unstable_extendSxProp, } from './styleFunctionSx'; +export { default as experimental_sx } from './sx'; export { default as unstable_getThemeValue } from './getThemeValue'; export { default as Box } from './Box'; export { default as createBox } from './createBox'; diff --git a/packages/mui-system/src/sx/index.d.ts b/packages/mui-system/src/sx/index.d.ts new file mode 100644 index 00000000000000..4232fe22d6b4bb --- /dev/null +++ b/packages/mui-system/src/sx/index.d.ts @@ -0,0 +1 @@ +export { default } from './sx'; diff --git a/packages/mui-system/src/sx/index.js b/packages/mui-system/src/sx/index.js new file mode 100644 index 00000000000000..4232fe22d6b4bb --- /dev/null +++ b/packages/mui-system/src/sx/index.js @@ -0,0 +1 @@ +export { default } from './sx'; diff --git a/packages/mui-system/src/sx/sx.d.ts b/packages/mui-system/src/sx/sx.d.ts new file mode 100644 index 00000000000000..d17929c4ee803a --- /dev/null +++ b/packages/mui-system/src/sx/sx.d.ts @@ -0,0 +1,4 @@ +import { CSSObject } from '@mui/styled-engine'; +import { SxProps } from '../styleFunctionSx'; + +export default function sx(styles: SxProps): CSSObject; diff --git a/packages/mui-system/src/sx/sx.js b/packages/mui-system/src/sx/sx.js new file mode 100644 index 00000000000000..770503fa5136e5 --- /dev/null +++ b/packages/mui-system/src/sx/sx.js @@ -0,0 +1,7 @@ +import styleFunctionSx from '../styleFunctionSx'; + +function sx(styles) { + return ({ theme }) => styleFunctionSx({ sx: styles, theme }); +} + +export default sx; diff --git a/packages/mui-system/src/sx/sx.spec.ts b/packages/mui-system/src/sx/sx.spec.ts new file mode 100644 index 00000000000000..faca7d15ad7d7a --- /dev/null +++ b/packages/mui-system/src/sx/sx.spec.ts @@ -0,0 +1,10 @@ +import { experimental_sx as sx, styled } from '@mui/system'; + +// Can be used in the styled() utility +const Test = styled('div')( + sx({ + color: 'primary.main', + bgcolor: 'primary.light', + m: 2, + }), +); diff --git a/packages/mui-system/src/sx/sx.test.js b/packages/mui-system/src/sx/sx.test.js new file mode 100644 index 00000000000000..e35c7f206ddd58 --- /dev/null +++ b/packages/mui-system/src/sx/sx.test.js @@ -0,0 +1,158 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { createRenderer } from 'test/utils'; +import { experimental_sx as sx, styled, ThemeProvider } from '@mui/system'; + +describe('sx', () => { + const { render } = createRenderer(); + const breakpointsValues = { + xs: 0, + sm: 600, + md: 960, + lg: 1280, + xl: 1920, + }; + + const round = (value) => Math.round(value * 1e5) / 1e5; + + const theme = { + spacing: (val) => `${val * 10}px`, + breakpoints: { + keys: ['xs', 'sm', 'md', 'lg', 'xl'], + values: breakpointsValues, + up: (key) => { + return `@media (min-width:${breakpointsValues[key]}px)`; + }, + }, + unit: 'px', + palette: { + primary: { + main: 'rgb(0, 0, 255)', + }, + secondary: { + main: 'rgb(0, 255, 0)', + }, + }, + typography: { + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', + fontWeightLight: 300, + fontSize: 14, + body1: { + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', + fontSize: '1rem', + letterSpacing: `${round(0.15 / 16)}em`, + fontWeight: 400, + lineHeight: 1.5, + }, + body2: { + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', + fontSize: `${14 / 16}rem`, + letterSpacing: `${round(0.15 / 14)}em`, + fontWeight: 400, + lineHeight: 1.43, + }, + }, + }; + + describe('system', () => { + it('resolves system when used inside styled()', function test() { + if (/jsdom/.test(window.navigator.userAgent)) { + this.skip(); + } + + const Test = styled('div')( + sx({ + color: 'primary.main', + bgcolor: 'secondary.main', + m: 2, + p: 1, + fontSize: 'fontSize', + maxWidth: 'sm', + }), + ); + + const { container } = render( + + + , + ); + + expect(container.firstChild).toHaveComputedStyle({ + color: 'rgb(0, 0, 255)', + backgroundColor: 'rgb(0, 255, 0)', + margin: '20px', + padding: '10px', + fontSize: '14px', + maxWidth: '600px', + }); + }); + + it('resolves system when used inside variants', function test() { + if (/jsdom/.test(window.navigator.userAgent)) { + this.skip(); + } + + const themeWithVariants = { + ...theme, + components: { + MuiTest: { + variants: [ + { + props: {}, // all props + style: sx({ + color: 'primary.main', + bgcolor: 'secondary.main', + m: 2, + p: 1, + fontSize: 'fontSize', + maxWidth: 'sm', + }), + }, + ], + }, + }, + }; + + const Test = styled('div', { name: 'MuiTest', slot: 'Root' })( + sx({ + color: 'primary.main', + bgcolor: 'secondary.main', + m: 2, + p: 1, + fontSize: 'fontSize', + maxWidth: 'sm', + }), + ); + + const { container } = render( + + + , + ); + + expect(container.firstChild).toHaveComputedStyle({ + color: 'rgb(0, 0, 255)', + backgroundColor: 'rgb(0, 255, 0)', + margin: '20px', + padding: '10px', + fontSize: '14px', + maxWidth: '600px', + }); + }); + }); + + it('does not throw if used without ThemeProvider', function test() { + const Test = styled('div')( + sx({ + color: 'primary.main', + bgcolor: 'secondary.main', + m: 2, + p: 1, + fontSize: 'fontSize', + maxWidth: 'sm', + }), + ); + + expect(() => render()).not.to.throw(); + }); +});