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
33 changes: 33 additions & 0 deletions docs/src/pages/system/styled/UsingWithSx.js
Original file line number Diff line number Diff line change
@@ -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 (
<ThemeProvider theme={customTheme}>
<MyThemeComponent>Styled div with theme</MyThemeComponent>
</ThemeProvider>
);
}
33 changes: 33 additions & 0 deletions docs/src/pages/system/styled/UsingWithSx.tsx
Original file line number Diff line number Diff line change
@@ -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,
}),
Comment on lines +19 to +24
Copy link
Member

Choose a reason for hiding this comment

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

One question, does sx work without ThemeProvider?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it's just that I could not really test that the values are applied :) I will add test for it too, just to ensure it does not throw

);

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>
10 changes: 10 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,16 @@ 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}}

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 the `experimental_sx` outside of the `styled()` utility too, for example for defining the `variants` in your custom theme.
mnajdova marked this conversation as resolved.
Show resolved Hide resolved

## 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
27 changes: 27 additions & 0 deletions packages/mui-material/src/styles/index.spec.ts
Original file line number Diff line number Diff line change
@@ -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,
}),
},
],
},
},
});
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>): 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) {
return ({ theme }) => styleFunctionSx({ sx: styles, theme });
}

export default sx;
10 changes: 10 additions & 0 deletions packages/mui-system/src/sx/sx.spec.ts
Original file line number Diff line number Diff line change
@@ -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,
}),
);
143 changes: 143 additions & 0 deletions packages/mui-system/src/sx/sx.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
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(
<ThemeProvider theme={theme}>
<Test />
</ThemeProvider>,
);

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(
<ThemeProvider theme={themeWithVariants}>
<Test />
</ThemeProvider>,
);

expect(container.firstChild).toHaveComputedStyle({
color: 'rgb(0, 0, 255)',
backgroundColor: 'rgb(0, 255, 0)',
margin: '20px',
padding: '10px',
fontSize: '14px',
maxWidth: '600px',
});
});
});
});