diff --git a/docs/pages/guides/routing.js b/docs/pages/guides/routing.js new file mode 100644 index 00000000000000..04b21f34057898 --- /dev/null +++ b/docs/pages/guides/routing.js @@ -0,0 +1,20 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import { prepareMarkdown } from 'docs/src/modules/utils/parseMarkdown'; + +const pageFilename = 'guides/routing'; +const requireDemo = require.context('docs/src/pages/guides/routing', false, /\.(js|tsx)$/); +const requireRaw = require.context( + '!raw-loader!../../src/pages/guides/routing', + false, + /\.(js|md|tsx)$/, +); + +export default function Page({ demos, docs }) { + return ; +} + +Page.getInitialProps = () => { + const { demos, docs } = prepareMarkdown({ pageFilename, requireRaw }); + return { demos, docs }; +}; diff --git a/docs/src/pages.ts b/docs/src/pages.ts index c7cafad7ffe82c..1d254181bb74fd 100644 --- a/docs/src/pages.ts +++ b/docs/src/pages.ts @@ -254,6 +254,7 @@ const pages: MuiPage[] = [ { pathname: '/guides/interoperability', title: 'Style Library Interoperability' }, { pathname: '/guides/minimizing-bundle-size' }, { pathname: '/guides/composition' }, + { pathname: '/guides/routing' }, { pathname: '/guides/server-rendering' }, { pathname: '/guides/responsive-ui', title: 'Responsive UI' }, { pathname: '/guides/pickers-migration', title: 'Migration from @material-ui/pickers' }, diff --git a/docs/src/pages/components/buttons/buttons.md b/docs/src/pages/components/buttons/buttons.md index 67ab58eb3e62bb..90d4b244d4f6cb 100644 --- a/docs/src/pages/components/buttons/buttons.md +++ b/docs/src/pages/components/buttons/buttons.md @@ -132,7 +132,7 @@ component forwards this ref to the underlying DOM node. Given that many of the interactive components rely on `ButtonBase`, you should be able to take advantage of it everywhere. -Here is an [integration example with react-router](/guides/composition/#button). +Here is an [integration example with react-router](/guides/routing/#button). ## Limitations diff --git a/docs/src/pages/components/links/Links.js b/docs/src/pages/components/links/Links.js index 719afc4361ff55..701739ab1f6de6 100644 --- a/docs/src/pages/components/links/Links.js +++ b/docs/src/pages/components/links/Links.js @@ -3,9 +3,9 @@ import * as React from 'react'; import Box from '@material-ui/core/Box'; import Link from '@material-ui/core/Link'; -export default function Links() { - const preventDefault = (event) => event.preventDefault(); +const preventDefault = (event) => event.preventDefault(); +export default function Links() { return ( event.preventDefault(); +const preventDefault = (event: React.SyntheticEvent) => event.preventDefault(); +export default function Links() { return ( event.preventDefault(); +const preventDefault = (event) => event.preventDefault(); +export default function UnderlineLink() { return ( event.preventDefault(); +const preventDefault = (event: React.SyntheticEvent) => event.preventDefault(); +export default function UnderlineLink() { return ( ; ``` -You can find a [demo with React Router following this section](/guides/composition/#react-router) of the documentation. +You can find a [demo with React Router following this section](/guides/routing/#list) of the documentation. ## Nested List diff --git a/docs/src/pages/getting-started/faq/faq.md b/docs/src/pages/getting-started/faq/faq.md index c2631c03df10db..600f8faee1d9b6 100644 --- a/docs/src/pages/getting-started/faq/faq.md +++ b/docs/src/pages/getting-started/faq/faq.md @@ -127,7 +127,7 @@ or are already familiar with a different API, and don't want to learn a new one? [Style Library Interoperability](/guides/interoperability/) section, where we show how simple it is to restyle Material-UI components with alternative style libraries. -## When should I use inline-style vs CSS? +## When should I use inline-style vs. CSS? As a rule of thumb, only use inline-styles for dynamic style properties. The CSS alternative provides more advantages, such as: @@ -139,7 +139,7 @@ The CSS alternative provides more advantages, such as: ## How do I use react-router? -We detail the [integration with third-party routing libraries](/guides/composition/#routing-libraries) like react-router, Gatsby or Next.js in our guide. +We detail the [integration with third-party routing libraries](/guides/routing/) like react-router, Gatsby or Next.js in our guide. ## How can I access the DOM element? diff --git a/docs/src/pages/guides/composition/LinkRouterWithTheme.js b/docs/src/pages/guides/composition/LinkRouterWithTheme.js deleted file mode 100644 index b9f0c16325cb18..00000000000000 --- a/docs/src/pages/guides/composition/LinkRouterWithTheme.js +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { MemoryRouter as Router } from 'react-router'; -import { Link as RouterLink } from 'react-router-dom'; -import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles'; -import Button from '@material-ui/core/Button'; - -const LinkBehavior = React.forwardRef(({ href, ...props }, ref) => ( - -)); - -LinkBehavior.propTypes = { - href: PropTypes.string, -}; - -const theme = createMuiTheme({ - components: { - MuiButtonBase: { - defaultProps: { - LinkComponent: LinkBehavior, - }, - }, - MuiButton: { - styleOverrides: { - root: { - fontSize: '1rem', - }, - }, - }, - }, -}); - -export default function GlobalThemeOverride() { - return ( - - - - - - ); -} diff --git a/docs/src/pages/guides/composition/composition.md b/docs/src/pages/guides/composition/composition.md index b52b73cdf5aecd..bc2e9c159f88b7 100644 --- a/docs/src/pages/guides/composition/composition.md +++ b/docs/src/pages/guides/composition/composition.md @@ -6,7 +6,7 @@ In order to provide the maximum flexibility and performance, we need a way to know the nature of the child elements a component receives. -To solve this problem we tag some of the components +To solve this problem, we tag some of the components with a `muiName` static property when needed. You may, however, need to wrap a component in order to enhance it, @@ -128,29 +128,6 @@ The component providing the `component` prop (e.g. ListItem) might not forward a You can find the details in the [TypeScript guide](/guides/typescript/#usage-of-component-prop). -## Routing libraries - -The integration with third-party routing libraries is achieved with the `component` prop. -The behavior is identical to the description of the prop above. -Here are a few demos with [react-router-dom](https://github.com/ReactTraining/react-router). -They cover the Button, Link, and List components. You can apply the same strategy with all the components (BottomNavigation, Card, etc.). - -### Button - -{{"demo": "pages/guides/composition/ButtonRouter.js"}} - -### Link - -{{"demo": "pages/guides/composition/LinkRouter.js"}} - -### Link configuration using theme - -{{"demo": "pages/guides/composition/LinkRouterWithTheme.js"}} - -### List - -{{"demo": "pages/guides/composition/ListRouter.js"}} - ## Caveat with refs This section covers caveats when using a custom component as `children` or for the diff --git a/docs/src/pages/guides/routing/ButtonDemo.js b/docs/src/pages/guides/routing/ButtonDemo.js new file mode 100644 index 00000000000000..1fc380f3638f1b --- /dev/null +++ b/docs/src/pages/guides/routing/ButtonDemo.js @@ -0,0 +1,15 @@ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; + +const preventDefault = (event) => event.preventDefault(); + +export default function ButtonDemo() { + return ( + + + + ); +} diff --git a/docs/src/pages/guides/routing/ButtonDemo.tsx b/docs/src/pages/guides/routing/ButtonDemo.tsx new file mode 100644 index 00000000000000..eee2bfa849af0f --- /dev/null +++ b/docs/src/pages/guides/routing/ButtonDemo.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; +import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; + +const preventDefault = (event: React.SyntheticEvent) => event.preventDefault(); + +export default function ButtonDemo() { + return ( + + + + ); +} diff --git a/docs/src/pages/guides/composition/ButtonRouter.js b/docs/src/pages/guides/routing/ButtonRouter.js similarity index 91% rename from docs/src/pages/guides/composition/ButtonRouter.js rename to docs/src/pages/guides/routing/ButtonRouter.js index c6865f1734736b..da65000ddd285e 100644 --- a/docs/src/pages/guides/composition/ButtonRouter.js +++ b/docs/src/pages/guides/routing/ButtonRouter.js @@ -9,14 +9,14 @@ const LinkBehavior = React.forwardRef((props, ref) => ( export default function ButtonRouter() { return ( - -
+
+
-
- + +
); } diff --git a/docs/src/pages/guides/composition/ButtonRouter.tsx b/docs/src/pages/guides/routing/ButtonRouter.tsx similarity index 92% rename from docs/src/pages/guides/composition/ButtonRouter.tsx rename to docs/src/pages/guides/routing/ButtonRouter.tsx index 4d468adaf6a337..0cbcb17272d082 100644 --- a/docs/src/pages/guides/composition/ButtonRouter.tsx +++ b/docs/src/pages/guides/routing/ButtonRouter.tsx @@ -11,14 +11,14 @@ const LinkBehavior = React.forwardRef>( export default function ButtonRouter() { return ( - -
+
+
-
- + +
); } diff --git a/docs/src/pages/guides/routing/LinkDemo.js b/docs/src/pages/guides/routing/LinkDemo.js new file mode 100644 index 00000000000000..fec5638e0ab1de --- /dev/null +++ b/docs/src/pages/guides/routing/LinkDemo.js @@ -0,0 +1,13 @@ +import * as React from 'react'; +import Link from '@material-ui/core/Link'; +import Box from '@material-ui/core/Box'; + +const preventDefault = (event) => event.preventDefault(); + +export default function LinkDemo() { + return ( + + Link + + ); +} diff --git a/docs/src/pages/guides/routing/LinkDemo.tsx b/docs/src/pages/guides/routing/LinkDemo.tsx new file mode 100644 index 00000000000000..5fba35c5a8e814 --- /dev/null +++ b/docs/src/pages/guides/routing/LinkDemo.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; +import Link from '@material-ui/core/Link'; +import Box from '@material-ui/core/Box'; + +const preventDefault = (event: React.SyntheticEvent) => event.preventDefault(); + +export default function LinkDemo() { + return ( + + Link + + ); +} diff --git a/docs/src/pages/guides/composition/LinkRouter.js b/docs/src/pages/guides/routing/LinkRouter.js similarity index 83% rename from docs/src/pages/guides/composition/LinkRouter.js rename to docs/src/pages/guides/routing/LinkRouter.js index 0e4ac26cb4a1b9..9a8f2bfcf2dd90 100644 --- a/docs/src/pages/guides/composition/LinkRouter.js +++ b/docs/src/pages/guides/routing/LinkRouter.js @@ -3,6 +3,7 @@ import * as React from 'react'; import { MemoryRouter as Router } from 'react-router'; import { Link as RouterLink } from 'react-router-dom'; import Link from '@material-ui/core/Link'; +import Box from '@material-ui/core/Box'; const LinkBehavior = React.forwardRef((props, ref) => ( @@ -10,14 +11,14 @@ const LinkBehavior = React.forwardRef((props, ref) => ( export default function LinkRouter() { return ( - -
+ + With prop forwarding
Without prop forwarding -
-
+
+
); } diff --git a/docs/src/pages/guides/composition/LinkRouter.tsx b/docs/src/pages/guides/routing/LinkRouter.tsx similarity index 84% rename from docs/src/pages/guides/composition/LinkRouter.tsx rename to docs/src/pages/guides/routing/LinkRouter.tsx index 5ebbc09aa534da..ae512955c5ce8f 100644 --- a/docs/src/pages/guides/composition/LinkRouter.tsx +++ b/docs/src/pages/guides/routing/LinkRouter.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { MemoryRouter as Router } from 'react-router'; import { Link as RouterLink, LinkProps as RouterLinkProps } from 'react-router-dom'; import Link from '@material-ui/core/Link'; +import Box from '@material-ui/core/Box'; const LinkBehavior = React.forwardRef>( (props, ref) => ( @@ -12,14 +13,14 @@ const LinkBehavior = React.forwardRef>( export default function LinkRouter() { return ( - -
+ + With prop forwarding
Without prop forwarding -
-
+ +
); } diff --git a/docs/src/pages/guides/routing/LinkRouterWithTheme.js b/docs/src/pages/guides/routing/LinkRouterWithTheme.js new file mode 100644 index 00000000000000..d0190a9926f8fd --- /dev/null +++ b/docs/src/pages/guides/routing/LinkRouterWithTheme.js @@ -0,0 +1,57 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { MemoryRouter as Router } from 'react-router'; +import { Link as RouterLink } from 'react-router-dom'; +import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Box from '@material-ui/core/Box'; +import Link from '@material-ui/core/Link'; + +const LinkBehavior = React.forwardRef((props, ref) => { + const { href, ...other } = props; + return ; +}); + +LinkBehavior.propTypes = { + href: PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.string]) + .isRequired, +}; + +const theme = createMuiTheme({ + components: { + MuiLink: { + defaultProps: { + // @ts-ignore + component: LinkBehavior, + }, + }, + MuiButtonBase: { + defaultProps: { + LinkComponent: LinkBehavior, + }, + }, + }, +}); + +export default function LinkRouterWithTheme() { + return ( + :not(style) + :not(style)': { mt: 1 }, + }} + > + + + Link + + + + + ); +} diff --git a/docs/src/pages/guides/routing/LinkRouterWithTheme.tsx b/docs/src/pages/guides/routing/LinkRouterWithTheme.tsx new file mode 100644 index 00000000000000..6f3d79a5004210 --- /dev/null +++ b/docs/src/pages/guides/routing/LinkRouterWithTheme.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { MemoryRouter as Router } from 'react-router'; +import { Link as RouterLink, LinkProps as RouterLinkProps } from 'react-router-dom'; +import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Box from '@material-ui/core/Box'; +import Link from '@material-ui/core/Link'; + +const LinkBehavior = React.forwardRef< + any, + Omit & { href: RouterLinkProps['to'] } +>((props, ref) => { + const { href, ...other } = props; + return ; +}); + +const theme = createMuiTheme({ + components: { + MuiLink: { + defaultProps: { + // @ts-ignore + component: LinkBehavior, + }, + }, + MuiButtonBase: { + defaultProps: { + LinkComponent: LinkBehavior, + }, + }, + }, +}); + +export default function LinkRouterWithTheme() { + return ( + :not(style) + :not(style)': { mt: 1 }, + }} + > + + + Link + + + + + ); +} diff --git a/docs/src/pages/guides/composition/ListRouter.js b/docs/src/pages/guides/routing/ListRouter.js similarity index 100% rename from docs/src/pages/guides/composition/ListRouter.js rename to docs/src/pages/guides/routing/ListRouter.js diff --git a/docs/src/pages/guides/composition/ListRouter.tsx b/docs/src/pages/guides/routing/ListRouter.tsx similarity index 100% rename from docs/src/pages/guides/composition/ListRouter.tsx rename to docs/src/pages/guides/routing/ListRouter.tsx diff --git a/docs/src/pages/guides/routing/routing.md b/docs/src/pages/guides/routing/routing.md new file mode 100644 index 00000000000000..2d5c20f965efc7 --- /dev/null +++ b/docs/src/pages/guides/routing/routing.md @@ -0,0 +1,47 @@ +# Routing libraries + +

By default, the navigation is performed with a native <a> element. You can customize it to use your own router. For instance, using Next.js's Link or react-router.

+ +## Navigation components + +There are two main components available to perform navigations. +The most common one is the [`Link`](/components/link/) as its name might suggest. +It renders a native `` element and applies the `href` as an attribute. + +{{"demo": "pages/guides/routing/LinkDemo.js"}} + +You can also make a button perform navigation actions. +As long as your component is extending [`ButtonBase`](/components/buttons/#complex-buttons), the `href` prop will enable the link mode. + +{{"demo": "pages/guides/routing/ButtonDemo.js"}} + +## Global theme Link + +In real-life applications, using a native `` element is very rarely enough. +The UX can be improved by using an enhanced Link component systematically. +The theme of Material-UI allows to configure this component once: + +{{"demo": "pages/guides/routing/LinkRouterWithTheme.js"}} + +> ⚠️ This approach has limitations with TypeScript. The `href` prop only accepts a string. +> In the event you need to provide a richer structure, see the next section. + +## `component` prop + +The integration with third-party routing libraries can be achieved with the `component` prop. +You can learn more about this prop in the [composition guide](/guides/composition/#component-prop). + +Here are a few demos with [react-router-dom](https://github.com/ReactTraining/react-router). +You can apply the same strategy with all the components (BottomNavigation, Card, etc.). + +### Link + +{{"demo": "pages/guides/routing/LinkRouter.js"}} + +### Button + +{{"demo": "pages/guides/routing/ButtonRouter.js"}} + +### List + +{{"demo": "pages/guides/routing/ListRouter.js"}} diff --git a/docs/src/pages/guides/typescript/typescript.md b/docs/src/pages/guides/typescript/typescript.md index 41e5045a60a254..764e5aa1a5db64 100644 --- a/docs/src/pages/guides/typescript/typescript.md +++ b/docs/src/pages/guides/typescript/typescript.md @@ -287,7 +287,7 @@ const theme = createMyTheme({ Many Material-UI components allow you to replace their root node via a `component` prop, this will be detailed in the component's API documentation. For example, a Button's root node can be replaced with a React Router's Link, and any additional props that are passed to Button, such as `to`, will be spread to the Link component. -For a code example concerning Button and react-router-dom checkout [these demos](/guides/composition/#routing-libraries). +For a code example concerning Button and react-router-dom checkout [these demos](/guides/routing/#component-prop). To be able to use props of such a Material-UI component on their own, props should be used with type arguments. Otherwise, the `component` prop will not be present in the props of the Material-UI component.