diff --git a/docs/src/pages/components/portal/portal.md b/docs/src/pages/components/portal/portal.md index fe79e32f7ba4d5..4b9eab5d93f175 100644 --- a/docs/src/pages/components/portal/portal.md +++ b/docs/src/pages/components/portal/portal.md @@ -13,7 +13,7 @@ The children of the portal component will be appended to the `container` specifi The component is used internally by the [`Modal`](/components/modal/) and [`Popper`](/components/popper/) components. On the server, the content won't be rendered. -You have to wait for the client side hydration to see the children. +You have to wait for the client-side hydration to see the children. ## Simple Portal diff --git a/docs/src/pages/components/use-media-query/ServerSide.js b/docs/src/pages/components/use-media-query/ServerSide.js index b55fd899542aac..de67bacbaf18bb 100644 --- a/docs/src/pages/components/use-media-query/ServerSide.js +++ b/docs/src/pages/components/use-media-query/ServerSide.js @@ -1,28 +1,39 @@ import React from 'react'; import mediaQuery from 'css-mediaquery'; import { ThemeProvider } from '@material-ui/styles'; -import useMediaQueryTheme from '@material-ui/core/useMediaQuery'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; function MyComponent() { - const matches = useMediaQueryTheme('@media (min-width:600px)'); + const matches = useMediaQuery('(min-width:600px)'); - return {`@media (min-width:600px) matches: ${matches}`}; + return {`(min-width:600px) matches: ${matches}`}; } export default function ServerSide() { - // Use https://github.com/ericf/css-mediaquery as ponyfill. const ssrMatchMedia = query => ({ + // Use https://github.com/ericf/css-mediaquery as ponyfill. matches: mediaQuery.match(query, { // The estimated CSS width of the browser. // For the sake of this demo, we are using a fixed value. - // In production, you can look into client-hint https://caniuse.com/#search=client%20hint - // or user-agent resolution. + // + // In production, you can leverage: + // + // - Client hints. You can ask the client to send your server its width. + // Be aware that this feature is not supported everywhere: https://caniuse.com/#search=client%20hint. + // - User-agent. You can parse the user agent of the client, then convert the data to a + // is mobile or is desktop variable, and finally, guess the most likely screen width of the client. width: 800, }), }); return ( - + ); diff --git a/docs/src/pages/components/use-media-query/SimpleMediaQuery.tsx b/docs/src/pages/components/use-media-query/SimpleMediaQuery.tsx new file mode 100644 index 00000000000000..4c3bf0e13fec7a --- /dev/null +++ b/docs/src/pages/components/use-media-query/SimpleMediaQuery.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +export default function SimpleMediaQuery() { + const matches = useMediaQuery('(min-width:600px)'); + + return {`(min-width:600px) matches: ${matches}`}; +} diff --git a/docs/src/pages/components/use-media-query/use-media-query.md b/docs/src/pages/components/use-media-query/use-media-query.md index 14eb4744d7a57d..fbfac9090a0cb8 100644 --- a/docs/src/pages/components/use-media-query/use-media-query.md +++ b/docs/src/pages/components/use-media-query/use-media-query.md @@ -11,8 +11,7 @@ Some of the key features: - ⚛️ It has an idiomatic React API. - 🚀 It's performant, it observes the document to detect when its media queries change, instead of polling the values periodically. - 📦 [1 kB gzipped](/size-snapshot). -- 💄 It's an alternative to react-responsive and react-media that aims for simplicity. -- 🤖 It supports Server-side rendering. +- 🤖 It supports server-side rendering. ## Simple media query @@ -39,19 +38,68 @@ function MyComponent() { {{"demo": "pages/components/use-media-query/ThemeHelper.js"}} +Alternatively, you can use a callback function, accepting the theme as a first argument: + +```jsx +import useMediaQuery from '@material-ui/core/useMediaQuery'; + +function MyComponent() { + const matches = useMediaQuery(theme => theme.breakpoints.up('sm')); + + return {`theme.breakpoints.up('sm') matches: ${matches}`}; +} +``` + +⚠️ There is **no default** theme support, you have to inject it in a parent theme provider. + ## Using JavaScript syntax -[json2mq](https://github.com/akiran/json2mq) is used to generate media query string from a JavaScript object. +You can use [json2mq](https://github.com/akiran/json2mq) to generate media query string from a JavaScript object. {{"demo": "pages/components/use-media-query/JavaScriptMedia.js", "defaultCodeOpen": true}} ## Server-side rendering -An implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) is required on the server, we recommend using [css-mediaquery](https://github.com/ericf/css-mediaquery). -We also encourage the usage of the `useMediaQueryTheme` version of the hook that fetches properties from the theme. This way, you can provide a `ssrMatchMedia` option once for all your React tree. +An implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) is required on the server. +We recommend using [css-mediaquery](https://github.com/ericf/css-mediaquery) to emulate it. {{"demo": "pages/components/use-media-query/ServerSide.js"}} +⚠️ Server-side rendering and client-side media queries are fundamentally at odds. +Be aware of the tradeoff. The support can only be partial. + +Try relying on client-side CSS media queries first. +For instance, you could use: + +- [``](/system/display/#hiding-elements) +- [``](/components/hidden/#css) +- or [`themes.breakpoints.up(x)`](/customization/breakpoints/#css-media-queries) + +## Testing + +Similar to the server-side case, you need an implementation of [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) in your test environment. + +For instance, [jsdom doesn't support it yet](https://github.com/jsdom/jsdom/blob/master/test/web-platform-tests/to-upstream/html/browsers/the-window-object/window-properties-dont-upstream.html). You should polyfill it. +We recommend using [css-mediaquery](https://github.com/ericf/css-mediaquery) to emulate it. + +```js +import mediaQuery from 'css-mediaquery'; + +function createMatchMedia(width) { + return query => ({ + matches: mediaQuery.match(query, { width }), + addListener: () => {}, + removeListener: () => {}, + }); +} + +describe('MyTests', () => { + beforeAll(() => { + window.matchMedia = createMatchMedia(window.innerWidth); + }); +}); +``` + ## Migrating from `withWidth()` The `withWidth()` higher-order component injects the screen width of the page. @@ -84,7 +132,7 @@ function useWidth() { #### Arguments -1. `query` (*String*): A string representing the media query to handle. +1. `query` (*String* | *Function*): A string representing the media query to handle or a callback function accepting the theme (in the context) that returns a string. 2. `options` (*Object* [optional]): - `options.defaultMatches` (*Boolean* [optional]): As `window.matchMedia()` is unavailable on the server, @@ -97,7 +145,7 @@ function useWidth() { - `options.ssrMatchMedia` (*Function* [optional]) You might want to use an heuristic to approximate the screen of the client browser. For instance, you could be using the user-agent or the client-hint https://caniuse.com/#search=client%20hint. - You can provide a global ponyfill using [`custom properties`](/customization/globals/#default-props) on the theme. Check the [server-side rendering example](#server-side-rendering). + You can provide a global ponyfill using [`custom props`](/customization/globals/#default-props) on the theme. Check the [server-side rendering example](#server-side-rendering). #### Returns diff --git a/docs/src/pages/styles/advanced/advanced.md b/docs/src/pages/styles/advanced/advanced.md index 8439d9622aa3f6..ff966d7639b3bf 100644 --- a/docs/src/pages/styles/advanced/advanced.md +++ b/docs/src/pages/styles/advanced/advanced.md @@ -585,7 +585,7 @@ If you are using Server-Side Rendering (SSR), you should pass the nonce in the ` ``` Then, you must pass this nonce to JSS so it can add it to subsequent `