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

[muiStyled] Support default theme when none is available #22791

Merged
merged 40 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c2d31cc
add emotion peer dependencies
mnajdova Sep 25, 2020
5ae933f
fixed types & tests
mnajdova Sep 25, 2020
18b0668
prettier
mnajdova Sep 25, 2020
f0ef95c
Merge branch 'next' of https://github.com/mui-org/material-ui into next
mnajdova Sep 28, 2020
c7bebb8
Merge branch 'next' of https://github.com/mui-org/material-ui into next
mnajdova Sep 28, 2020
92b2d6e
Merge branch 'next' of https://github.com/mui-org/material-ui into next
mnajdova Sep 28, 2020
76256f1
wip
mnajdova Sep 28, 2020
8661306
added ThemeProvider
mnajdova Sep 28, 2020
61d7fac
exported ThemeProvider from styled-engine-sc
mnajdova Sep 28, 2020
c9fcdc1
tests wip
mnajdova Sep 28, 2020
48305e9
tests
mnajdova Sep 28, 2020
1c04d40
prettier
mnajdova Sep 28, 2020
27d9f73
Update packages/material-ui/src/styles/muiStyled.d.ts
mnajdova Sep 29, 2020
b149da5
Update packages/material-ui/src/styles/customStyled.d.ts
mnajdova Sep 29, 2020
05217a7
renamed styled -> legacy_styled, renamed customStyled -> styled
mnajdova Sep 29, 2020
3bf96aa
fixed tests
mnajdova Sep 29, 2020
551157f
added migration step, exported styled with deprecation warning
mnajdova Sep 29, 2020
a03252c
prettier
mnajdova Sep 29, 2020
179a6e2
renamed legacy_styled back to styled
mnajdova Sep 29, 2020
9878176
renamed styled to experimentalStyled
mnajdova Sep 29, 2020
17ce5d4
fix
mnajdova Sep 29, 2020
b7ccb20
specify ThemeProvider as export from emotion-theming
mnajdova Sep 29, 2020
72398c8
refactored muiStyled to be used as simple styled utility too
mnajdova Sep 29, 2020
58a1fb9
fix lint issues
mnajdova Sep 29, 2020
aac9e83
renamed muiStyled to experimentalStyled
mnajdova Sep 30, 2020
5774c19
refactored to use ThemeContext
mnajdova Sep 30, 2020
8016f92
Update packages/material-ui-styles/README.md
mnajdova Sep 30, 2020
6bc05c1
Update packages/material-ui-styles/README.md
mnajdova Sep 30, 2020
9757a00
Update packages/material-ui-styles/package.json
mnajdova Sep 30, 2020
bb880fe
Moved nesting ThemeProvider to core
mnajdova Oct 1, 2020
73d6589
Update packages/material-ui/src/styles/experimentalStyled.d.ts
mnajdova Oct 1, 2020
6e2b480
Update packages/material-ui/src/styles/experimentalStyled.d.ts
mnajdova Oct 1, 2020
0c8be58
added test
mnajdova Oct 1, 2020
a001588
fixed docs generation
mnajdova Oct 1, 2020
e676780
Update packages/material-ui/src/styles/ThemeProvider.js
mnajdova Oct 1, 2020
0298222
Update packages/material-ui/src/styles/experimentalStyled.d.ts
mnajdova Oct 1, 2020
c96062d
addressed comments
mnajdova Oct 1, 2020
3e419ac
Update packages/material-ui/src/styles/ThemeProvider.js
mnajdova Oct 2, 2020
4fccc26
Update packages/material-ui/src/styles/ThemeProvider.js
mnajdova Oct 2, 2020
dcc97fa
docs:api
mnajdova Oct 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 5 additions & 8 deletions docs/src/pages/components/slider-styled/ContinuousSlider.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import * as React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { customStyled } from '@material-ui/core/styles';
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/lab/SliderStyled';
import VolumeDown from '@material-ui/icons/VolumeDown';
import VolumeUp from '@material-ui/icons/VolumeUp';

const useStyles = makeStyles({
root: {
width: 200,
},
const Root = customStyled('div')({
width: 200,
});

export default function ContinuousSlider() {
const classes = useStyles();
const [value, setValue] = React.useState(30);

const handleChange = (event, newValue) => {
setValue(newValue);
};

return (
<div className={classes.root}>
<Root>
<Typography id="continuous-slider" gutterBottom>
Volume
</Typography>
Expand All @@ -44,6 +41,6 @@ export default function ContinuousSlider() {
Disabled slider
</Typography>
<Slider disabled defaultValue={30} aria-labelledby="disabled-slider" />
</div>
</Root>
);
}
13 changes: 5 additions & 8 deletions docs/src/pages/components/slider-styled/ContinuousSlider.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import * as React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { customStyled } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import Slider from '@material-ui/lab/SliderStyled';
import VolumeDown from '@material-ui/icons/VolumeDown';
import VolumeUp from '@material-ui/icons/VolumeUp';

const useStyles = makeStyles({
root: {
width: 200,
},
const Root = customStyled('div')({
width: 200,
});

export default function ContinuousSlider() {
const classes = useStyles();
const [value, setValue] = React.useState<number>(30);

const handleChange = (
Expand All @@ -24,7 +21,7 @@ export default function ContinuousSlider() {
};

return (
<div className={classes.root}>
<Root>
<Typography id="continuous-slider" gutterBottom>
Volume
</Typography>
Expand All @@ -47,6 +44,6 @@ export default function ContinuousSlider() {
Disabled slider
</Typography>
<Slider disabled defaultValue={30} aria-labelledby="disabled-slider" />
</div>
</Root>
);
}
4 changes: 2 additions & 2 deletions docs/src/pages/getting-started/installation/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ To install and save in your `package.json` dependencies, run:

```sh
// with npm
npm install @material-ui/core@next @emotion/core @emotion/styled
npm install @material-ui/core@next @emotion/core @emotion/styled emotion-theming

// with yarn
yarn add @material-ui/core@next @emotion/core @emotion/styled
yarn add @material-ui/core@next @emotion/core @emotion/styled emotion-theming
```

Please note that [react](https://www.npmjs.com/package/react) >= 16.8.0 and [react-dom](https://www.npmjs.com/package/react-dom) >= 16.8.0 are peer dependencies.
Expand Down
2 changes: 2 additions & 0 deletions packages/material-ui-styled-engine-sc/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export default function styled(tag, options) {

return scStyled(tag);
}

export { ThemeProvider } from 'styled-components';
3 changes: 2 additions & 1 deletion packages/material-ui-styled-engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"dependencies": {},
"peerDependencies": {
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27"
"@emotion/styled": "^10.0.27",
"emotion-theming": "^10.0.27"
},
"sideEffects": false,
"publishConfig": {
Expand Down
1 change: 1 addition & 0 deletions packages/material-ui-styled-engine/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from '@emotion/styled';
export { default } from '@emotion/styled';
export * from 'emotion-theming';
1 change: 1 addition & 0 deletions packages/material-ui-styled-engine/src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default } from '@emotion/styled';
export * from 'emotion-theming';
Copy link
Member

Choose a reason for hiding this comment

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

We should specify what we need in case emotion-theming adds new modules that we don't need.

Copy link
Member

Choose a reason for hiding this comment

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

What problem does emotion-theming solve?

Copy link
Member Author

Choose a reason for hiding this comment

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

By adding the emotion's/styled-components' ThemeProvider in our ThemeProvider component, developers no longer need to specify ThemeProvider from emotion/styled component together with ours. Also, in the styled utility when custom theme is used, it will automatically be available on props.theme. In addition to this, when no ThemeProvider is used, the styled utility uses the defaultTheme

Copy link
Member

Choose a reason for hiding this comment

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

What if they already use emotion-theming?

Copy link
Member

Choose a reason for hiding this comment

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

I think that using a different ThemeProvider (enforcing the usage of two) makes incremental adoption easier. Developers don't have to worry about conflicts of theme structure with existing usage of emotion/sc.

However, for using the css prop, it seems required https://emotion.sh/docs/theming#css-prop.

It seems that theme-ui imports the ThemeContext of emotion directly (without emotion-theming), https://github.com/system-ui/theme-ui/blob/af57d4ce97c0e8a3289115f33cef1d17bc65adad/packages/core/src/index.ts#L3. They also have emotion as a direct dependency.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just a clarification, for our components, like Slider, Button, muiStyled + our ThemeProvider is working, it won't work for the scenarios of using muiStyled + div, span or other primitive elements as we do not have access to change the behavior there.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, I see, thanks for the clarification. +1 for including the styled-engine theme provider in Material-UI's ThemeProvider component. It's simpler. For the theme structure conflict, developers can workaround the issue by nesting <MuiThemeProvider> > <Emotion ThemeProvider>.

The only point I would suggest to explore is: Using the ThemeContext exported from @emotion/core vs. emotion-theming.

Copy link
Member Author

Choose a reason for hiding this comment

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

The only point I would suggest to explore is: Using the ThemeContext exported from @emotion/core vs. emotion-theming.

Yeah, let me spend some time on this, just to make sure I am not missing something 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Refactored to use only the ThemeContext (both emotion & styled-components expose this). Tested with both engines, tests are passing, so I think everything is covered. No new dependencies added with this 👍

Copy link
Member

Choose a reason for hiding this comment

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

No new dependencies added with this

Nice :).

4 changes: 2 additions & 2 deletions packages/material-ui-styles/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ Install the package in your project directory with:

```sh
// with npm
npm install @material-ui/styles
npm install @material-ui/styles @emotion/core @emotion/styled emotion-theming
mnajdova marked this conversation as resolved.
Show resolved Hide resolved

// with yarn
yarn add @material-ui/styles
yarn add @material-ui/styles @emotion/core @emotion/styled emotion-theming
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
```

## Documentation
Expand Down
4 changes: 4 additions & 0 deletions packages/material-ui-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@
"typescript": "tslint -p tsconfig.json \"{src,test}/**/*.{spec,d}.{ts,tsx}\" && tsc -p tsconfig.json"
},
"peerDependencies": {
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27",
"@types/react": "^16.8.6",
"emotion-theming": "^10.0.27",
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
Expand All @@ -49,6 +52,7 @@
"dependencies": {
"@babel/runtime": "^7.4.4",
"@emotion/hash": "^0.8.0",
"@material-ui/styled-engine": "^5.0.0-alpha.11",
"@material-ui/types": "^5.1.0",
"@material-ui/utils": "^5.0.0-alpha.8",
"clsx": "^1.0.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { exactProp } from '@material-ui/utils';
import { ThemeProvider as StyledEngineThemeProvider } from '@material-ui/styled-engine';
import ThemeContext from '../useTheme/ThemeContext';
import useTheme from '../useTheme';
import nested from './nested';
Expand Down Expand Up @@ -61,7 +62,13 @@ function ThemeProvider(props) {
return output;
}, [localTheme, outerTheme]);

return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
return (
<ThemeContext.Provider value={theme}>
<StyledEngineThemeProvider theme={typeof theme === 'object' ? theme : {}}>
{children}
</StyledEngineThemeProvider>
</ThemeContext.Provider>
);
}

ThemeProvider.propTypes = {
Expand Down
1 change: 1 addition & 0 deletions packages/material-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@emotion/core": "^10.0.27",
"@emotion/styled": "^10.0.27",
"@types/react": "^16.8.6",
"emotion-theming": "^10.0.27",
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
Expand Down
29 changes: 29 additions & 0 deletions packages/material-ui/src/styles/customStyled.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
StyledOptions,
JSXInEl,
CreateStyledComponentIntrinsic,
CreateStyledComponentExtrinsic,
} from './muiStyled';

export interface CreateStyled<Theme extends object = any> {
<Tag extends React.ComponentType<any>, ExtraProps = {}>(
tag: Tag,
options?: StyledOptions
): CreateStyledComponentExtrinsic<Tag, ExtraProps, Theme>;

<Tag extends keyof JSXInEl, ExtraProps = {}>(
tag: Tag,
options?: StyledOptions
): CreateStyledComponentIntrinsic<Tag, ExtraProps, Theme>;
}

/**
* Cutom styled utility that has a default mui theme.
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
*
* @param tag HTML tag or component that should serve as base.
* @param options Styled options for the created component.
* @returns React component that has styles attached to it.
*/
declare const styled: CreateStyled;

export default styled;
31 changes: 31 additions & 0 deletions packages/material-ui/src/styles/customStyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import seStyled from '@material-ui/styled-engine';
import defaultTheme from './defaultTheme';

function isEmpty(obj) {
let result = true;

Object.keys(obj).forEach((key) => {
if (obj.hasOwnProperty(key)) {
result = false;
}
});

return result;
}

export default function styled(tag, options) {
const defaultStyledResolver = seStyled(tag, options);

const customStyledResolver = (...styles) => {
const stylesWithDefaultTheme = styles.map((stylesArg) => {
return typeof stylesArg === 'function'
? ({ theme: themeInput, ...rest }) =>
stylesArg({ theme: isEmpty(themeInput) ? defaultTheme : themeInput, ...rest })
: stylesArg;
});

return defaultStyledResolver(...stylesWithDefaultTheme);
};

return customStyledResolver;
}
54 changes: 54 additions & 0 deletions packages/material-ui/src/styles/customStyled.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ThemeProvider } from '@material-ui/styles';
import React from 'react';
import { expect } from 'chai';
import { createClientRender, screen } from 'test/utils';
import createMuiTheme from './createMuiTheme';
import styled from './customStyled';

describe('customStyled', () => {
const render = createClientRender();
it('should work', () => {
const Div = styled('div')({
width: '200px',
});

render(<Div data-testid="component">Test</Div>);

const style = window.getComputedStyle(screen.getByTestId('component'));
expect(style.getPropertyValue('width')).to.equal('200px');
});

it('should use defaultTheme if no theme is provided', () => {
const Div = styled('div')((props) => ({
backgroundColor: props.theme.palette.background.paper,
}));

render(<Div data-testid="component">Test</Div>);

const style = window.getComputedStyle(screen.getByTestId('component'));
expect(style.getPropertyValue('background-color')).to.equal('rgb(255, 255, 255)'); // white
});

it('should use theme from context if available', () => {
const Div = styled('div')((props) => ({
backgroundColor: props.theme.palette.background.paper,
}));

const theme = createMuiTheme({
palette: {
background: {
paper: 'rgb(255, 255, 0)',
},
},
});

render(
<ThemeProvider theme={theme}>
<Div data-testid="component">Test</Div>
</ThemeProvider>,
);

const style = window.getComputedStyle(screen.getByTestId('component'));
expect(style.getPropertyValue('background-color')).to.equal('rgb(255, 255, 0)');
});
});
1 change: 1 addition & 0 deletions packages/material-ui/src/styles/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export {
} from './withStyles';
export { default as withTheme, WithTheme } from './withTheme';
export { default as muiStyled } from './muiStyled';
export { default as customStyled } from './customStyled';
export { default as styled, ComponentCreator, StyledProps } from './styled';
export {
createGenerateClassName,
Expand Down
1 change: 1 addition & 0 deletions packages/material-ui/src/styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export { default as useThemeProps } from './useThemeProps';
export { default as withStyles } from './withStyles';
export { default as withTheme } from './withTheme';
export { default as muiStyled } from './muiStyled';
export { default as customStyled } from './customStyled';
export {
createGenerateClassName,
jssPreset,
Expand Down
10 changes: 6 additions & 4 deletions packages/material-ui/src/styles/muiStyled.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export type PropsOf<
export type Omit<T, U> = T extends any ? Pick<T, Exclude<keyof T, U>> : never;
export type Overwrapped<T, U> = Pick<T, Extract<keyof T, keyof U>>;

type JSXInEl = JSX.IntrinsicElements;
export type JSXInEl = JSX.IntrinsicElements;
type ReactClassPropKeys = keyof React.ClassAttributes<any>;

export type WithTheme<P, T> = P extends { theme: infer Theme }
Expand Down Expand Up @@ -198,10 +198,12 @@ export interface CreateStyled<Theme extends object = any> {
}

/**
* Cutom styled functionality that support mui specific config.
* Cutom styled utility that has a default mui theme.
mbrookes marked this conversation as resolved.
Show resolved Hide resolved
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
*
* @param options Takes an incomplete theme object and adds the missing parts.
* @returns A complete, ready to use theme object.
* @param tag HTML tag or component that should serve as base.
* @param options Styled options for the created component.
* @muiOptions Material-UI specific style options, consiting of overrides resolver.
* @returns React component that has styles attached to it.
*/
declare const muiStyled: CreateStyled;

Expand Down