Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[system] Add experimental_sx utility #29833

Merged
merged 11 commits into from
Nov 25, 2021
36 changes: 36 additions & 0 deletions docs/src/pages/system/styled/UsingWithSx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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')(({ theme }) =>
sx(
{
color: 'primary.contrastText',
backgroundColor: 'primary.main',
padding: 1,
borderRadius: 1,
},
theme,
),
);

export default function ThemeUsage() {
return (
<ThemeProvider theme={customTheme}>
<MyThemeComponent>Styled div with theme</MyThemeComponent>
</ThemeProvider>
);
}
36 changes: 36 additions & 0 deletions docs/src/pages/system/styled/UsingWithSx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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')(({ theme }) =>
sx(
{
color: 'primary.contrastText',
backgroundColor: 'primary.main',
padding: 1,
borderRadius: 1,
},
theme,
),
);

export default function ThemeUsage() {
return (
<ThemeProvider theme={customTheme}>
<MyThemeComponent>Styled div with theme</MyThemeComponent>
</ThemeProvider>
);
}
3 changes: 3 additions & 0 deletions docs/src/pages/system/styled/UsingWithSx.tsx.preview
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<ThemeProvider theme={customTheme}>
<MyThemeComponent>Styled div with theme</MyThemeComponent>
</ThemeProvider>
8 changes: 8 additions & 0 deletions docs/src/pages/system/styled/styled.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ const MyStyledButton = (props) => (
})
```

### How can I use the `sx` syntax with the `styled()` utility?
siriwatknp marked this conversation as resolved.
Show resolved Hide resolved

If you are one of those that prefer the `sx` syntax and want to use it in both the `sx` prop and the `styled()` utility, you can do this by using the `experimental_sx` utility from the `@mui/system`:
mnajdova marked this conversation as resolved.
Show resolved Hide resolved

{{"demo": "pages/system/styled/UsingWithSx.js", "defaultCodeOpen": true}}

> Note: You can use the `experimental_sx` outside of the `styled()` utility too, for example for defining the `variants`, or creating new `mixins` 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.
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/styles/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions packages/mui-material/src/styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
36 changes: 36 additions & 0 deletions packages/mui-material/src/styles/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { experimental_sx as sx, styled, createTheme } from '@mui/material/styles';

// Can use the experimental_sx in the styled() utility
const Test = styled('div')(({ theme }) =>
sx(
{
color: 'primary.main',
bgcolor: 'primary.light',
m: 2,
},
theme,
),
);

const defaultTheme = createTheme();

// Can use the experimental_sx in the theme's variants
const customTheme = createTheme({
components: {
MuiButton: {
variants: [
{
props: {},
style: ({ theme }) =>
sx(
{
m: 2,
p: 1,
},
defaultTheme,
),
},
],
},
},
});
2 changes: 2 additions & 0 deletions packages/mui-system/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
1 change: 1 addition & 0 deletions packages/mui-system/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions packages/mui-system/src/sx/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './sx';
1 change: 1 addition & 0 deletions packages/mui-system/src/sx/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './sx';
4 changes: 4 additions & 0 deletions packages/mui-system/src/sx/sx.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { CSSObject } from '@mui/styled-engine';
import { SxProps } from '../styleFunctionSx';

export default function sx<T extends object = {}>(styles: SxProps<T>, theme: T): CSSObject;
7 changes: 7 additions & 0 deletions packages/mui-system/src/sx/sx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import styleFunctionSx from '../styleFunctionSx';

function sx(styles, theme) {
return styleFunctionSx({ sx: styles, theme });
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try with:

const sx = (styles) => ({ theme }) => styleFunctionSx({ sx: styles, theme });

From what I check in emotion & styled-components, they support nested function.

I prefer this because I don't need to get the theme if we don't need it (mostly we don't need the theme if we want to use sx).

const MyThemeComponent = styled('div')(
  sx({
      color: 'primary.contrastText',
      backgroundColor: 'primary.main',
      padding: 1,
      borderRadius: 1,
    })
);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to create an API not tightly related to the styled(), but maybe this is the only use-case, so yeah, let me update

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. I've tested it using in the styled() utility and in the variants in side the theme. I can't think of any other use-cases at this moment.


export default sx;
13 changes: 13 additions & 0 deletions packages/mui-system/src/sx/sx.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { experimental_sx as sx, styled } from '@mui/system';

// Can be used in the styled() utility
const Test = styled('div')(({ theme }) =>
sx(
{
color: 'primary.main',
bgcolor: 'primary.light',
m: 2,
},
theme,
),
);
125 changes: 125 additions & 0 deletions packages/mui-system/src/sx/sx.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { expect } from 'chai';
import { experimental_sx as sx } from '@mui/system';

describe('sx', () => {
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 ', () => {
const result = sx(
{
color: 'primary.main',
bgcolor: 'secondary.main',
m: 2,
p: 1,
fontFamily: 'default',
fontWeight: 'light',
fontSize: 'fontSize',
maxWidth: 'sm',
displayPrint: 'block',
border: [1, 2, 3, 4, 5],
},
theme,
);

expect(result).to.deep.equal({
color: 'rgb(0, 0, 255)',
backgroundColor: 'rgb(0, 255, 0)',
margin: '20px',
padding: '10px',
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
fontWeight: 300,
fontSize: 14,
maxWidth: 600,
'@media print': {
display: 'block',
},
'@media (min-width:0px)': { border: '1px solid' },
'@media (min-width:600px)': { border: '2px solid' },
'@media (min-width:960px)': { border: '3px solid' },
'@media (min-width:1280px)': { border: '4px solid' },
'@media (min-width:1920px)': { border: '5px solid' },
});
});

it('resolves system typography', () => {
const result = sx(
{
typography: ['body2', 'body1'],
},
theme,
);

expect(result).to.deep.equal({
'@media (min-width:0px)': {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
fontSize: `${14 / 16}rem`,
letterSpacing: `${round(0.15 / 14)}em`,
fontWeight: 400,
lineHeight: 1.43,
},
'@media (min-width:600px)': {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
fontSize: '1rem',
letterSpacing: `${round(0.15 / 16)}em`,
fontWeight: 400,
lineHeight: 1.5,
},
});
});

it('allow values to be `null` or `undefined`', () => {
const result = sx({ typography: null, m: 0, p: null, transform: null }, theme);
expect(result).to.deep.equal({
margin: '0px',
});
});
});
});