Skip to content

Commit

Permalink
[styles] Make StyleRules backwards compatible (#16200)
Browse files Browse the repository at this point in the history
* Refactor needed types to @material-ui/types

* Revert to old types if there are no props

* Add typescript version of DynamicInlineStyle

* docs:typescript:formatted
  • Loading branch information
merceyz authored and eps1lon committed Jun 17, 2019
1 parent 4909bd8 commit c0e4103
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 68 deletions.
60 changes: 27 additions & 33 deletions docs/src/pages/customization/components/DynamicInlineStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,34 @@ const styles = {
},
};

class DynamicInlineStyle extends React.Component {
state = {
color: 'default',
};
export default function DynamicInlineStyle() {
const [color, setColor] = React.useState('default');

handleChange = event => {
this.setState({ color: event.target.checked ? 'blue' : 'default' });
const handleChange = event => {
setColor(event.target.checked ? 'blue' : 'default');
};

render() {
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={this.state.color === 'blue'}
onChange={this.handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button
style={{
...styles.button,
...(this.state.color === 'blue' ? styles.buttonBlue : {}),
}}
>
{'dynamic inline-style'}
</Button>
</React.Fragment>
);
}
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button
style={{
...styles.button,
...(color === 'blue' ? styles.buttonBlue : {}),
}}
>
{'dynamic inline-style'}
</Button>
</React.Fragment>
);
}

export default DynamicInlineStyle;
53 changes: 53 additions & 0 deletions docs/src/pages/customization/components/DynamicInlineStyle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import Button from '@material-ui/core/Button';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import { createStyles } from '@material-ui/core/styles';

const styles = createStyles({
button: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
},
buttonBlue: {
background: 'linear-gradient(45deg, #2196f3 30%, #21cbf3 90%)',
boxShadow: '0 3px 5px 2px rgba(33, 203, 243, .30)',
},
});

export default function DynamicInlineStyle() {
const [color, setColor] = React.useState('default');

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setColor(event.target.checked ? 'blue' : 'default');
};

return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button
style={{
...styles.button,
...(color === 'blue' ? styles.buttonBlue : {}),
}}
>
{'dynamic inline-style'}
</Button>
</React.Fragment>
);
}
33 changes: 1 addition & 32 deletions packages/material-ui-styles/src/makeStyles/makeStyles.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,7 @@ import {
Styles,
WithStylesOptions,
} from '@material-ui/styles/withStyles';
import { IsAny, Omit } from '@material-ui/types';

export type Or<A, B, C = false> = A extends true
? true
: B extends true
? true
: C extends true
? true
: false;
export type And<A, B, C = true> = A extends true
? B extends true
? C extends true
? true
: false
: false
: false;

/**
* @internal
*
* check if a type is `{}`
*
* 1. false if the given type has any members
* 2. false if the type is `object` which is the only other type with no members
* {} is a top type so e.g. `string extends {}` but not `string extends object`
* 3. false if the given type is `unknown`
*/
export type IsEmptyInterface<T> = And<
keyof T extends never ? true : false,
string extends T ? true : false,
unknown extends T ? false : true
>;
import { Omit, IsAny, Or, IsEmptyInterface } from '@material-ui/types';

/**
* @internal
Expand Down
6 changes: 4 additions & 2 deletions packages/material-ui-styles/src/withStyles/withStyles.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { PropInjector, CoerceEmptyInterface } from '@material-ui/types';
import { PropInjector, CoerceEmptyInterface, IsEmptyInterface } from '@material-ui/types';
import * as CSS from 'csstype';
import * as JSS from 'jss';

Expand Down Expand Up @@ -32,7 +32,9 @@ export interface CreateCSSProperties<Props extends object = {}>
*/
export type StyleRules<Props extends object = {}, ClassKey extends string = string> = Record<
ClassKey,
CreateCSSProperties<Props> | ((props: Props) => CreateCSSProperties<Props>)
IsEmptyInterface<Props> extends true
? CSSProperties | (() => CSSProperties)
: CreateCSSProperties<Props> | ((props: Props) => CreateCSSProperties<Props>)
>;

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui-styles/test/IsEmptyInterface.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* unit testing for IsEmptyInterface utility type
*/
import { IsEmptyInterface } from '@material-ui/styles/makeStyles';
import { IsEmptyInterface } from '@material-ui/types';

// $ExpectType true
type EmptyInterfaceIsValid = IsEmptyInterface<{}>;
Expand Down
30 changes: 30 additions & 0 deletions packages/material-ui-styles/test/styles.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,33 @@ function forwardRefTest() {
// $ExpectType string
const root2 = styles.root2;
}

{
// If there are no props, use the definition that doesn't accept them
// https://github.com/mui-org/material-ui/issues/16198

// $ExpectType Record<"root", CSSProperties | (() => CSSProperties)>
const styles = createStyles({
root: {
width: 1,
},
});

// $ExpectType Record<"root", CSSProperties | (() => CSSProperties)>
const styles2 = createStyles({
root: () => ({
width: 1,
}),
});

interface testProps {
foo: boolean;
}

// $ExpectType Record<"root", CreateCSSProperties<testProps> | ((props: testProps) => CreateCSSProperties<testProps>)>
const styles3 = createStyles({
root: (props: testProps) => ({
width: 1,
}),
});
}
32 changes: 32 additions & 0 deletions packages/material-ui-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,35 @@ export type IsAny<T> = 0 extends (1 & T) ? true : false;
* Returns an empty object type if T is any, otherwise returns T
*/
export type CoerceEmptyInterface<T> = IsAny<T> extends true ? {} : T;

export type Or<A, B, C = false> = A extends true
? true
: B extends true
? true
: C extends true
? true
: false;

export type And<A, B, C = true> = A extends true
? B extends true
? C extends true
? true
: false
: false
: false;

/**
* @internal
*
* check if a type is `{}`
*
* 1. false if the given type has any members
* 2. false if the type is `object` which is the only other type with no members
* {} is a top type so e.g. `string extends {}` but not `string extends object`
* 3. false if the given type is `unknown`
*/
export type IsEmptyInterface<T> = And<
keyof T extends never ? true : false,
string extends T ? true : false,
unknown extends T ? false : true
>;

0 comments on commit c0e4103

Please sign in to comment.