From 00da27d612ae4e2268a2a11a681fe95e8c1e427f Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 18 Apr 2019 10:24:39 +0200 Subject: [PATCH 1/4] [docs] Add Button + react-router TypeScript demo (#15382) Component prop example was mainly concerned with react-router + button so we just added a proper demo for it. Closes #13218 --- docs/src/pages/demos/buttons/ButtonRouter.js | 24 +++++++ docs/src/pages/demos/buttons/ButtonRouter.tsx | 29 +++++++++ docs/src/pages/demos/buttons/buttons.md | 31 +-------- .../src/pages/guides/typescript/typescript.md | 63 ++----------------- .../material-ui/src/Button/Button.spec.tsx | 1 - 5 files changed, 61 insertions(+), 87 deletions(-) create mode 100644 docs/src/pages/demos/buttons/ButtonRouter.js create mode 100644 docs/src/pages/demos/buttons/ButtonRouter.tsx diff --git a/docs/src/pages/demos/buttons/ButtonRouter.js b/docs/src/pages/demos/buttons/ButtonRouter.js new file mode 100644 index 00000000000000..ae073a68eb4f03 --- /dev/null +++ b/docs/src/pages/demos/buttons/ButtonRouter.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { MemoryRouter as Router } from 'react-router'; +import { Link } from 'react-router-dom'; +import Button from '@material-ui/core/Button'; + +// required for react-router-dom < 6.0.0 +// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678 +const AdapterLink = React.forwardRef((props, ref) => ); + +const HomeLink = React.forwardRef((props, ref) => ); + +function App() { + return ( + + + {/* Avoids property collision */} + + + ); +} + +export default App; diff --git a/docs/src/pages/demos/buttons/ButtonRouter.tsx b/docs/src/pages/demos/buttons/ButtonRouter.tsx new file mode 100644 index 00000000000000..43b363cb39a2bd --- /dev/null +++ b/docs/src/pages/demos/buttons/ButtonRouter.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { MemoryRouter as Router } from 'react-router'; +import { Link, LinkProps } from 'react-router-dom'; +import Button from '@material-ui/core/Button'; + +// required for react-router-dom < 6.0.0 +// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678 +const AdapterLink = React.forwardRef((props, ref) => ( + +)); + +type Omit = Pick>; +const HomeLink = React.forwardRef>( + (props, ref) => , +); + +function App() { + return ( + + + {/* Avoids property collision */} + + + ); +} + +export default App; diff --git a/docs/src/pages/demos/buttons/buttons.md b/docs/src/pages/demos/buttons/buttons.md index 8afccee12d8026..f403f9f2688549 100644 --- a/docs/src/pages/demos/buttons/buttons.md +++ b/docs/src/pages/demos/buttons/buttons.md @@ -123,32 +123,7 @@ component forwards this ref to the underlying DOM node. Given that a lot of our interactive components rely on `ButtonBase`, you should be able to take advantage of it everywhere: -```jsx -import React from 'react'; -import { Link as RouterLink } from 'react-router-dom' -import Button from '@material-ui/core/Button'; +{{"demo": "pages/demos/buttons/ButtonRouter.js", "defaultCodeOpen": true}} -// required for react-router-dom < 5.0.0 -// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678 -const Link = React.forwardRef((props, ref) => ) - - -``` - -or if you want to avoid properties collision: - -```jsx -import { Link } from 'react-router-dom' -import Button from '@material-ui/core/Button'; - -// use `ref` instead of `innerRef` with react-router-dom@^5.0.0 -const MyLink = React.forwardRef((props, ref) => ); - - -``` - -*Note: Creating `MyLink` is necessary to prevent unexpected unmounting. You can read more about it in our [component property guide](/guides/composition/#component-property).* +_Note: Creating the Button components is necessary to prevent unexpected unmounting. +You can read more about it in our [component property guide](/guides/composition/#component-property)._ diff --git a/docs/src/pages/guides/typescript/typescript.md b/docs/src/pages/guides/typescript/typescript.md index 325246689199d6..8f481d62ae2ee1 100644 --- a/docs/src/pages/guides/typescript/typescript.md +++ b/docs/src/pages/guides/typescript/typescript.md @@ -249,63 +249,10 @@ const theme = createMyTheme({ appDrawer: { breakpoint: 'md' }}); ``` ## Usage of `component` property - Material-UI allows you to replace a component's root node via a `component` property. -For example, a `Button`'s root node can be replaced with a React Router `Link`, and any additional props that are passed to `Button`, such as `to`, will be spread to the `Link` component, meaning you can do this: -```jsx -import { Link } from 'react-router-dom'; - - -``` - -However, TypeScript will complain about it, because `to` is not part of the `ButtonProps` interface, and with the current type declarations it has no way of inferring what props can be passed to `component`. - -The current workaround is to cast Link to `any`: - -```tsx -import { Link } from 'react-router-dom'; -import Button, { ButtonProps } from '@material-ui/core/Button'; - -interface LinkButtonProps extends ButtonProps { - to: string; - replace?: boolean; -} - -const LinkButton = (props: LinkButtonProps) => ( - -``` +Not every component fully supports any component type you pass in. If you encounter a +component that rejects its `component` props in TypeScript please open an issue. +There is an ongoing effort to fix this by making component props generic. \ No newline at end of file diff --git a/packages/material-ui/src/Button/Button.spec.tsx b/packages/material-ui/src/Button/Button.spec.tsx index 043a159839cf78..8abb53237e751b 100644 --- a/packages/material-ui/src/Button/Button.spec.tsx +++ b/packages/material-ui/src/Button/Button.spec.tsx @@ -1,7 +1,6 @@ import React from 'react'; import Button, { ButtonProps } from '@material-ui/core/Button'; import { Link as ReactRouterLink, LinkProps } from 'react-router-dom'; -import { type } from 'os'; const log = console.log; From 522c8f7dbac34394f383c81243cb296d4544a341 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 18 Apr 2019 11:18:42 +0200 Subject: [PATCH 2/4] [styles] Add type test for withStyles + ref (#15383) Closes #15326 --- .../material-ui-styles/test/styles.spec.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/material-ui-styles/test/styles.spec.tsx b/packages/material-ui-styles/test/styles.spec.tsx index 00e4becc83c8f1..237a8565447154 100644 --- a/packages/material-ui-styles/test/styles.spec.tsx +++ b/packages/material-ui-styles/test/styles.spec.tsx @@ -385,3 +385,37 @@ withStyles(theme => color: number; } } + +function forwardRefTest() { + const styles = createStyles({ + root: { color: 'red' }, + }); + + function Anchor(props: WithStyles) { + const { classes } = props; + return ; + } + const StyledAnchor = withStyles(styles)(Anchor); + + const anchorRef = React.useRef(null); + // forwarded to function components which can't hold refs + // property 'ref' does not exists + ; // $ExpectError + ; + + const RefableAnchor = React.forwardRef>( + (props, ref) => { + const { classes } = props; + return ; + }, + ); + const StyledRefableAnchor = withStyles(styles)(RefableAnchor); + + ; + const buttonRef = React.createRef(); + // HTMLButtonElement is missing properties + ; // $ExpectError + // undesired: `innerRef` is currently typed as any but for backwards compat we're keeping it + // especially since `innerRef` will be removed in v5 and is equivalent to `ref` + ; +} From a494652e35fb8456eca157e7705a8f5e7cf09960 Mon Sep 17 00:00:00 2001 From: Doug Miller Date: Thu, 18 Apr 2019 11:08:22 -0400 Subject: [PATCH 3/4] [TextField] Update labelWidth for outline variant if required is updated (#15386) --- packages/material-ui/src/TextField/TextField.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/material-ui/src/TextField/TextField.js b/packages/material-ui/src/TextField/TextField.js index c8e853ddb2591e..dad3bf7735e70a 100644 --- a/packages/material-ui/src/TextField/TextField.js +++ b/packages/material-ui/src/TextField/TextField.js @@ -100,7 +100,7 @@ const TextField = React.forwardRef(function TextField(props, ref) { const labelNode = ReactDOM.findDOMNode(labelRef.current); setLabelWidth(labelNode != null ? labelNode.offsetWidth : 0); } - }, [variant]); + }, [variant, required]); warning( !select || Boolean(children), From 3044cb48e9d03a10c91e9a8fde880ea603f4cdac Mon Sep 17 00:00:00 2001 From: Mark <42415859+JarkEMones@users.noreply.github.com> Date: Thu, 18 Apr 2019 20:47:13 +0100 Subject: [PATCH 4/4] [docs] Add switches typescript demo (#15384) Adding TypeScript example for switches demo. Issue #14897 --- .../demos/selection-controls/Switches.tsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/src/pages/demos/selection-controls/Switches.tsx diff --git a/docs/src/pages/demos/selection-controls/Switches.tsx b/docs/src/pages/demos/selection-controls/Switches.tsx new file mode 100644 index 00000000000000..c875ab191b92d9 --- /dev/null +++ b/docs/src/pages/demos/selection-controls/Switches.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import Switch from '@material-ui/core/Switch'; + +function Switches() { + const [state, setState] = React.useState({ + checkedA: true, + checkedB: true, + }); + + const handleChange = (name: string) => (event: React.ChangeEvent) => { + setState({ ...state, [name]: event.target.checked }); + }; + + return ( +
+ + + + + + +
+ ); +} + +export default Switches;