Repository: mui/material-ui (TypeScript)
Date: 2018-06-05 21:47:38+00:00
Task type: Feature

Issue:
[docs] Document withWidth utility function
<!--- Provide a general summary of the issue in the Title above -->
Would it be useful to have this function within `Dialog`'s import to be a more generic, utility function? Such as `isMobileScreen` or `isFullScreen` that can safely be wrapped around any component? I often find myself using it in this manner where I need a boolean telling me if I am mobile or not, where something like `<Hidden />` wouldn't be useful.

If there is a different way to acquire what breakpoint the window is currently using, I apologize for making this issue 😃
<!--
    Thank you very much for contributing to Material-UI by creating an issue! ❤️
    To avoid duplicate issues we ask you to check off the following list.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior
<!---
    If you're describing a bug, tell us what should happen.
    If you're suggesting a change/improvement, tell us how it should work.
-->
Have `withMobileDialog` be a utility function rather than under `Dialog`


Issue discussion:
@Auchindoun The implementation of `withMobileDialog` is pretty empty.
https://github.com/mui-org/material-ui/blob/1839c0804fd9671f42cf5bc09160869afc9e08c4/packages/material-ui/src/Dialog/withMobileDialog.js#L10-L22
It's a sugar wrapper on top of `withWidth`. I agree, we should be documenting it.

Solution patch:
diff --git a/docs/src/modules/components/withRoot.js b/docs/src/modules/components/withRoot.js
--- a/docs/src/modules/components/withRoot.js
+++ b/docs/src/modules/components/withRoot.js
@@ -79,8 +79,7 @@ const pages = [
         pathname: '/layout/hidden',
       },
       {
-        pathname: '/layout/css-in-js',
-        title: 'CSS in JS',
+        pathname: '/layout/breakpoints',
       },
     ],
   },
diff --git a/docs/src/pages/customization/css-in-js/css-in-js.md b/docs/src/pages/customization/css-in-js/css-in-js.md
--- a/docs/src/pages/customization/css-in-js/css-in-js.md
+++ b/docs/src/pages/customization/css-in-js/css-in-js.md
@@ -250,7 +250,7 @@ For instance, it can be used to defined a `getInitialProps()` static method (nex
 It will be linked to the component.
 Use the function signature if you need to have access to the theme. It's provided as the first argument.
 2. `options` (*Object* [optional]):
-  - `options.withTheme` (Boolean [optional]): Defaults to `false`. Provide the `theme` object to the component as a property.
+  - `options.withTheme` (*Boolean* [optional]): Defaults to `false`. Provide the `theme` object to the component as a property.
   - `options.name` (*String* [optional]): The name of the style sheet. Useful for debugging.
     If the value isn't provided, it will try to fallback to the name of the component.
   - `options.flip` (*Boolean* [optional]): When set to `false`, this sheet will opt-out the `rtl` transformation. When set to `true`, the styles are inversed. When set to `null`, it follows `theme.direction`.
diff --git a/docs/src/pages/layout/basics/basics.md b/docs/src/pages/layout/basics/basics.md
--- a/docs/src/pages/layout/basics/basics.md
+++ b/docs/src/pages/layout/basics/basics.md
@@ -3,26 +3,16 @@
 ## Responsive UI
 
 [Responsive layouts](https://material.io/design/layout/responsive-layout-grid.html) in Material Design adapt to any possible screen size.
+We provide the following helpers to make the UI responsive:
 
-### Breakpoints
+- [Grid](/layout/grid):
+The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs.
 
-For optimal user experience, material design interfaces need to be able to adapt their layout at various breakpoints.
-Material-UI uses a **simplified** implementation of the original [specification](https://material.io/design/layout/responsive-layout-grid.html#breakpoints).
+- [Hidden](/layout/hidden):
+The hidden component can be used to change the visibility of the elements.
 
-Each breakpoint matches with a *fixed* screen width:
-- **xs**, extra-small: 0px or larger
-- **sm**, small: 600px or larger
-- **md**, medium: 960px or larger
-- **lg**, large: 1280px or larger
-- **xl**, xlarge: 1920px or larger
-
-These values can always be customized.
-You will find them in the theme, in the [`breakpoints.values`](/customization/default-theme?expend-path=$.breakpoints.values) object.
-
-The breakpoints are used internally in various components to make them responsive,
-but you can also take advantage of them
-for controlling the layout of your application through the [Grid](/layout/grid) and
-[Hidden](/layout/hidden) components.
+- [Breakpoints](/layout/breakpoints):
+We provide a low-level API for using the breakpoints in a wide variery of context.
 
 ## z-index
 
diff --git a/docs/src/pages/layout/css-in-js/MediaQuery.js b/docs/src/pages/layout/breakpoints/MediaQuery.js
similarity index 62%
rename from docs/src/pages/layout/css-in-js/MediaQuery.js
rename to docs/src/pages/layout/breakpoints/MediaQuery.js
--- a/docs/src/pages/layout/css-in-js/MediaQuery.js
+++ b/docs/src/pages/layout/breakpoints/MediaQuery.js
@@ -1,18 +1,20 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import compose from 'recompose/compose';
 import { withStyles } from '@material-ui/core/styles';
-import withWidth from '@material-ui/core/withWidth';
 import Typography from '@material-ui/core/Typography';
+import green from '@material-ui/core/colors/green';
 
 const styles = theme => ({
   root: {
     padding: theme.spacing.unit,
+    [theme.breakpoints.down('sm')]: {
+      backgroundColor: theme.palette.secondary.main,
+    },
     [theme.breakpoints.up('md')]: {
       backgroundColor: theme.palette.primary.main,
     },
-    [theme.breakpoints.down('sm')]: {
-      backgroundColor: theme.palette.secondary.main,
+    [theme.breakpoints.up('lg')]: {
+      backgroundColor: green[500],
     },
   },
 });
@@ -22,17 +24,15 @@ function MediaQuery(props) {
 
   return (
     <div className={classes.root}>
-      <Typography variant="subheading">{`Current width: ${props.width}`}</Typography>
+      <Typography variant="subheading">{'down(sm): red'}</Typography>
+      <Typography variant="subheading">{'up(md): blue'}</Typography>
+      <Typography variant="subheading">{'up(lg): green'}</Typography>
     </div>
   );
 }
 
 MediaQuery.propTypes = {
   classes: PropTypes.object.isRequired,
-  width: PropTypes.string.isRequired,
 };
 
-export default compose(
-  withStyles(styles),
-  withWidth(),
-)(MediaQuery);
+export default withStyles(styles)(MediaQuery);
diff --git a/docs/src/pages/layout/breakpoints/WithWidth.js b/docs/src/pages/layout/breakpoints/WithWidth.js
new file mode 100644
--- /dev/null
+++ b/docs/src/pages/layout/breakpoints/WithWidth.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import withWidth from '@material-ui/core/withWidth';
+import Typography from '@material-ui/core/Typography';
+
+const components = {
+  sm: 'em',
+  md: 'u',
+  lg: 'del',
+};
+
+function WithWidth(props) {
+  const { width } = props;
+  const Component = components[width] || 'span';
+
+  return (
+    <Typography variant="subheading">
+      <Component>{`Current width: ${width}`}</Component>
+    </Typography>
+  );
+}
+
+WithWidth.propTypes = {
+  width: PropTypes.string.isRequired,
+};
+
+export default withWidth()(WithWidth);
diff --git a/docs/src/pages/layout/breakpoints/breakpoints.md b/docs/src/pages/layout/breakpoints/breakpoints.md
new file mode 100644
--- /dev/null
+++ b/docs/src/pages/layout/breakpoints/breakpoints.md
@@ -0,0 +1,201 @@
+# Breakpoints
+
+For optimal user experience, material design interfaces need to be able to adapt their layout at various breakpoints.
+Material-UI uses a **simplified** implementation of the original [specification](https://material.io/design/layout/responsive-layout-grid.html#breakpoints).
+
+Each breakpoint matches with a *fixed* screen width:
+- **xs**, extra-small: 0px or larger
+- **sm**, small: 600px or larger
+- **md**, medium: 960px or larger
+- **lg**, large: 1280px or larger
+- **xl**, xlarge: 1920px or larger
+
+These values can always be customized.
+You will find them in the theme, in the [`breakpoints.values`](/customization/default-theme?expend-path=$.breakpoints.values) object.
+
+The breakpoints are used internally in various components to make them responsive,
+but you can also take advantage of them
+for controlling the layout of your application through the [Grid](/layout/grid) and
+[Hidden](/layout/hidden) components.
+
+## Media Queries
+
+CSS media queries is the idiomatic approach to make your UI responsive.
+We provide some [CSS-in-JS](/customization/css-in-js) helpers to do so.
+
+In the following demo, we change the background color (red, blue & green) based on the screen width.
+
+{{"demo": "pages/layout/breakpoints/MediaQuery.js"}}
+
+## withWidth()
+
+Sometimes, using CSS isn't enough.
+You might want to change the React rendering tree based on the breakpoint value, in JavaScript.
+We provide a `withWidth()` higher-order component for this use case.
+
+In the following demo, we change the rendered DOM element (*em*, <u>u</u>, ~~del~~ & span) based on the screen width.
+
+{{"demo": "pages/layout/breakpoints/WithWidth.js"}}
+
+⚠️ `withWidth()` server-side rendering support is limited.
+
+## API
+
+### `withWidth([options]) => higher-order component`
+
+Inject a `width` property.
+It does not modify the component passed to it; instead, it returns a new component.
+This `width` breakpoint property match the current screen width.
+It can be one of the following breakpoints:
+
+```ts
+type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+```
+
+Some implementation details that might be interesting to being aware of:
+- It forwards *non React static* properties so this HOC is more "transparent".
+For instance, it can be used to defined a `getInitialProps()` static method (next.js).
+
+#### Arguments
+
+1. `options` (*Object* [optional]):
+  - `options.withTheme` (*Boolean* [optional]): Defaults to `false`. Provide the `theme` object to the component as a property.
+  - `options.noSSR` (*Boolean* [optional]): Defaults to `false`.
+  In order to perform the server-side rendering reconciliation, we need to render twice.
+  A first time with nothing and a second time with the children.
+  This double pass rendering cycle comes with a drawback. The UI might blink.
+  You can set this flag to `true` if you are not doing server-side rendering.
+  - `options.initialWidth` (*Breakpoint* [optional]):
+  As `window.innerWidth` is unavailable on the server,
+  we default to rendering an empty component during the first mount.
+  In some situation, you might want to use an heuristic to approximate
+  the screen width of the client browser screen width.
+  For instance, you could be using the user-agent or the client-hints.
+  http://caniuse.com/#search=client%20hint
+  - `options.resizeInterval` (*Number* [optional]): Defaults to 166, corresponds to 10 frames at 60 Hz. Number of milliseconds to wait before responding to a screen resize event.
+
+#### Returns
+
+`higher-order component`: Should be used to wrap a component.
+
+#### Examples
+
+```jsx
+import withWidth, { isWidthUp } from '@material-ui/core/withWidth';
+
+class MyComponent extends React.Component {
+  render () {
+    if (isWidthUp('sm', this.props.width)) {
+      return <span />
+    }
+
+    return <div />;
+  }
+}
+
+export default withWidth()(MyComponent);
+```
+
+### `theme.breakpoints.up(key) => media query`
+
+#### Arguments
+
+1. `key` (*String* | *Number*): A breakpoint key (`xs`, `sm`, etc.) or a screen width number in pixels.
+
+#### Returns
+
+`media query`: A media query string ready to be used with JSS.
+
+#### Examples
+
+```js
+const styles = theme => ({
+  root: {
+    backgroundColor: 'blue',
+    // Match [md, ∞[
+    //       [960px, ∞[
+    [theme.breakpoints.up('md')]: {
+      backgroundColor: 'red',
+    },
+  },
+});
+```
+
+### `theme.breakpoints.down(key) => media query`
+
+#### Arguments
+
+1. `key` (*String* | *Number*): A breakpoint key (`xs`, `sm`, etc.) or a screen width number in pixels.
+
+#### Returns
+
+`media query`: A media query string ready to be used with JSS.
+
+#### Examples
+
+```js
+const styles = theme => ({
+  root: {
+    backgroundColor: 'blue',
+    // Match [0, md + 1[
+    //       [0, lg[
+    //       [0, 1280px[
+    [theme.breakpoints.down('md')]: {
+      backgroundColor: 'red',
+    },
+  },
+});
+```
+
+### `theme.breakpoints.only(key) => media query`
+
+#### Arguments
+
+1. `key` (*String*): A breakpoint key (`xs`, `sm`, etc.).
+
+#### Returns
+
+`media query`: A media query string ready to be used with JSS.
+
+#### Examples
+
+```js
+const styles = theme => ({
+  root: {
+    backgroundColor: 'blue',
+    // Match [md, md + 1[
+    //       [md, lg[
+    //       [960px, 1280px[
+    [theme.breakpoints.only('md')]: {
+      backgroundColor: 'red',
+    },
+  },
+});
+```
+
+### `theme.breakpoints.between(start, end) => media query`
+
+#### Arguments
+
+1. `start` (*String*): A breakpoint key (`xs`, `sm`, etc.).
+2. `end` (*String*): A breakpoint key (`xs`, `sm`, etc.).
+
+#### Returns
+
+`media query`: A media query string ready to be used with JSS.
+
+#### Examples
+
+```js
+const styles = theme => ({
+  root: {
+    backgroundColor: 'blue',
+    // Match [sm, md + 1[
+    //       [sm, lg[
+    //       [600px, 1280px[
+    [theme.breakpoints.between('sm', 'md')]: {
+      backgroundColor: 'red',
+    },
+  },
+});
+```
diff --git a/docs/src/pages/layout/css-in-js/css-in-js.md b/docs/src/pages/layout/css-in-js/css-in-js.md
deleted file mode 100644
--- a/docs/src/pages/layout/css-in-js/css-in-js.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# CSS in JS
-
-## Responsive breakpoints
-
-Sometimes, wrapping things inside a layout component doesn't make much sense when
-everything you need can be handled by a small CSS change. By using the
-[`breakpoints`](/customization/default-theme?expend-path=$.breakpoints) attribute of the theme, you can utilise the same breakpoints used
-for the [Grid](/layout/grid) and [Hidden](/layout/hidden) components directly in your component.
-
-This can be accomplished using [CSS-in-JS](/customization/css-in-js).
-
-{{"demo": "pages/layout/css-in-js/MediaQuery.js"}}
-
-## API
-
-### `theme.breakpoints.up(key) => media query`
-
-#### Arguments
-
-1. `key` (*String* | *Number*): A breakpoint key (`xs`, `sm`, etc.) or a screen width number in pixels.
-
-#### Returns
-
-`media query`: A media query string ready to be used with JSS.
-
-#### Examples
-
-```js
-const styles = theme => ({
-  root: {
-    backgroundColor: 'blue',
-    // Match [md, ∞[
-    //       [960px, ∞[
-    [theme.breakpoints.up('md')]: {
-      backgroundColor: 'red',
-    },
-  },
-});
-```
-
-### `theme.breakpoints.down(key) => media query`
-
-#### Arguments
-
-1. `key` (*String* | *Number*): A breakpoint key (`xs`, `sm`, etc.) or a screen width number in pixels.
-
-#### Returns
-
-`media query`: A media query string ready to be used with JSS.
-
-#### Examples
-
-```js
-const styles = theme => ({
-  root: {
-    backgroundColor: 'blue',
-    // Match [0, md + 1[
-    //       [0, lg[
-    //       [0, 1280px[
-    [theme.breakpoints.down('md')]: {
-      backgroundColor: 'red',
-    },
-  },
-});
-```
-
-### `theme.breakpoints.only(key) => media query`
-
-#### Arguments
-
-1. `key` (*String*): A breakpoint key (`xs`, `sm`, etc.).
-
-#### Returns
-
-`media query`: A media query string ready to be used with JSS.
-
-#### Examples
-
-```js
-const styles = theme => ({
-  root: {
-    backgroundColor: 'blue',
-    // Match [md, md + 1[
-    //       [md, lg[
-    //       [960px, 1280px[
-    [theme.breakpoints.only('md')]: {
-      backgroundColor: 'red',
-    },
-  },
-});
-```
-
-### `theme.breakpoints.between(start, end) => media query`
-
-#### Arguments
-
-1. `start` (*String*): A breakpoint key (`xs`, `sm`, etc.).
-2. `end` (*String*): A breakpoint key (`xs`, `sm`, etc.).
-
-#### Returns
-
-`media query`: A media query string ready to be used with JSS.
-
-#### Examples
-
-```js
-const styles = theme => ({
-  root: {
-    backgroundColor: 'blue',
-    // Match [sm, md + 1[
-    //       [sm, lg[
-    //       [600px, 1280px[
-    [theme.breakpoints.between('sm', 'md')]: {
-      backgroundColor: 'red',
-    },
-  },
-});
-```
diff --git a/docs/src/pages/layout/grid/grid.md b/docs/src/pages/layout/grid/grid.md
--- a/docs/src/pages/layout/grid/grid.md
+++ b/docs/src/pages/layout/grid/grid.md
@@ -5,7 +5,7 @@ components: Grid
 # Grid
 
 Material Design’s responsive UI is based on a 12-column grid layout.
-This grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs.
+The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs.
 
 ## How it works
 
diff --git a/docs/src/pages/layout/hidden/hidden.md b/docs/src/pages/layout/hidden/hidden.md
--- a/docs/src/pages/layout/hidden/hidden.md
+++ b/docs/src/pages/layout/hidden/hidden.md
@@ -29,7 +29,7 @@ mdDown      |                     hide | show
 
 ### js
 
-By default, the `js` implementation is used, responsively hiding content based on using the `withWidth()` higher-order component that watches screen size.
+By default, the `js` implementation is used, responsively hiding content based on using the [`withWidth()`](/layout/breakpoints#withwidth-) higher-order component that watches screen size.
 This has the benefit of not rendering any content at all unless the breakpoint is met.
 
 ### css
diff --git a/packages/material-ui/src/withWidth/withWidth.js b/packages/material-ui/src/withWidth/withWidth.js
--- a/packages/material-ui/src/withWidth/withWidth.js
+++ b/packages/material-ui/src/withWidth/withWidth.js
@@ -1,3 +1,5 @@
+/* eslint-disable react/no-did-mount-set-state */
+
 import React from 'react';
 import PropTypes from 'prop-types';
 import EventListener from 'react-event-listener';
@@ -25,28 +27,39 @@ export const isWidthDown = (breakpoint, width, inclusive = true) => {
 
 const withWidth = (options = {}) => Component => {
   const {
-    resizeInterval = 166, // Corresponds to 10 frames at 60 Hz.
     withTheme: withThemeOption = false,
+    noSSR = false,
+    initialWidth: initialWidthOption,
+    resizeInterval = 166, // Corresponds to 10 frames at 60 Hz.
   } = options;
 
   class WithWidth extends React.Component {
+    constructor(props) {
+      super(props);
+
+      if (noSSR) {
+        this.state.width = this.getWidth();
+      }
+    }
+
     state = {
       width: undefined,
     };
 
     componentDidMount() {
-      this.updateWidth(window.innerWidth);
+      const width = this.getWidth();
+      if (width !== this.state.width) {
+        this.setState({
+          width,
+        });
+      }
     }
 
     componentWillUnmount() {
       this.handleResize.clear();
     }
 
-    handleResize = debounce(() => {
-      this.updateWidth(window.innerWidth);
-    }, resizeInterval);
-
-    updateWidth(innerWidth) {
+    getWidth(innerWidth = window.innerWidth) {
       const breakpoints = this.props.theme.breakpoints;
       let width = null;
 
@@ -71,18 +84,22 @@ const withWidth = (options = {}) => Component => {
       }
 
       width = width || 'xl';
+      return width;
+    }
 
+    handleResize = debounce(() => {
+      const width = this.getWidth();
       if (width !== this.state.width) {
         this.setState({
           width,
         });
       }
-    }
+    }, resizeInterval);
 
     render() {
       const { initialWidth, theme, width, ...other } = this.props;
       const props = {
-        width: width || this.state.width || initialWidth,
+        width: width || this.state.width || initialWidth || initialWidthOption,
         ...other,
       };
       const more = {};
@@ -112,8 +129,8 @@ const withWidth = (options = {}) => Component => {
   WithWidth.propTypes = {
     /**
      * As `window.innerWidth` is unavailable on the server,
-     * we default to rendering an empty componenent during the first mount.
-     * In some situation you might want to use an heristic to approximate
+     * we default to rendering an empty component during the first mount.
+     * In some situation, you might want to use an heuristic to approximate
      * the screen width of the client browser screen width.
      *
      * For instance, you could be using the user-agent or the client-hints.
diff --git a/pages/layout/breakpoints.js b/pages/layout/breakpoints.js
new file mode 100644
--- /dev/null
+++ b/pages/layout/breakpoints.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import withRoot from 'docs/src/modules/components/withRoot';
+import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
+import markdown from 'docs/src/pages/layout/breakpoints/breakpoints.md';
+
+function Page() {
+  return (
+    <MarkdownDocs
+      markdown={markdown}
+      demos={{
+        'pages/layout/breakpoints/MediaQuery.js': {
+          js: require('docs/src/pages/layout/breakpoints/MediaQuery').default,
+          raw: preval`
+module.exports = require('fs')
+  .readFileSync(require.resolve('docs/src/pages/layout/breakpoints/MediaQuery'), 'utf8')
+`,
+        },
+        'pages/layout/breakpoints/WithWidth.js': {
+          js: require('docs/src/pages/layout/breakpoints/WithWidth').default,
+          raw: preval`
+module.exports = require('fs')
+  .readFileSync(require.resolve('docs/src/pages/layout/breakpoints/WithWidth'), 'utf8')
+`,
+        },
+      }}
+    />
+  );
+}
+
+export default withRoot(Page);
diff --git a/pages/layout/css-in-js.js b/pages/layout/css-in-js.js
deleted file mode 100644
--- a/pages/layout/css-in-js.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import withRoot from 'docs/src/modules/components/withRoot';
-import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
-import markdown from 'docs/src/pages/layout/css-in-js/css-in-js.md';
-
-function Page() {
-  return (
-    <MarkdownDocs
-      markdown={markdown}
-      demos={{
-        'pages/layout/css-in-js/MediaQuery.js': {
-          js: require('docs/src/pages/layout/css-in-js/MediaQuery').default,
-          raw: preval`
-module.exports = require('fs')
-  .readFileSync(require.resolve('docs/src/pages/layout/css-in-js/MediaQuery'), 'utf8')
-`,
-        },
-      }}
-    />
-  );
-}
-
-export default withRoot(Page);


Test patch:
diff --git a/packages/material-ui/src/withWidth/withWidth.test.js b/packages/material-ui/src/withWidth/withWidth.test.js
--- a/packages/material-ui/src/withWidth/withWidth.test.js
+++ b/packages/material-ui/src/withWidth/withWidth.test.js
@@ -35,7 +35,6 @@ describe('withWidth', () => {
   describe('prop: width', () => {
     it('should be able to override it', () => {
       const wrapper = mount(<EmptyWithWidth width="xl" />);
-
       assert.strictEqual(wrapper.find(Empty).props().width, 'xl');
     });
   });
@@ -43,7 +42,6 @@ describe('withWidth', () => {
   describe('browser', () => {
     it('should provide the right width to the child element', () => {
       const wrapper = mount(<EmptyWithWidth />);
-
       assert.strictEqual(wrapper.find(Empty).props().width, TEST_ENV_WIDTH);
     });
   });
@@ -78,11 +76,14 @@ describe('withWidth', () => {
     it('should work as expected', () => {
       const wrapper = shallow(<EmptyWithWidth />);
       const instance = wrapper.instance();
-      const updateWidth = instance.updateWidth.bind(instance);
+      const getWidth = instance.getWidth.bind(instance);
 
       breakpoints.keys.forEach(key => {
-        updateWidth(breakpoints.values[key]);
-        assert.strictEqual(wrapper.state().width, key, 'should return the matching width');
+        assert.strictEqual(
+          getWidth(breakpoints.values[key]),
+          key,
+          'should return the matching width',
+        );
       });
     });
   });
@@ -114,11 +115,25 @@ describe('withWidth', () => {
       // First mount on the server
       const wrapper1 = shallow(element);
       assert.strictEqual(wrapper1.find(Empty).props().width, 'lg');
+
+      // Second mount on the client
       const wrapper2 = mount(element);
+      assert.strictEqual(wrapper2.find(Empty).props().width, TEST_ENV_WIDTH);
+    });
+  });
+
+  describe('option: initialWidth', () => {
+    it('should work as expected', () => {
+      const EmptyWithWidth2 = withWidth({ initialWidth: 'lg' })(Empty);
+      const element = <EmptyWithWidth2 />;
+
+      // First mount on the server
+      const wrapper1 = shallow(element);
+      assert.strictEqual(wrapper1.find(Empty).props().width, 'lg');
 
       // Second mount on the client
+      const wrapper2 = mount(element);
       assert.strictEqual(wrapper2.find(Empty).props().width, TEST_ENV_WIDTH);
-      assert.strictEqual(TEST_ENV_WIDTH !== 'lg', true);
     });
   });
 
@@ -136,4 +151,12 @@ describe('withWidth', () => {
       assert.strictEqual(wrapper.find(Empty).props().theme, theme);
     });
   });
+
+  describe('option: noSSR', () => {
+    it('should work as expected', () => {
+      const EmptyWithWidth2 = withWidth({ noSSR: true })(Empty);
+      const wrapper = shallow(<EmptyWithWidth2 />);
+      assert.strictEqual(wrapper.find(Empty).props().width, TEST_ENV_WIDTH);
+    });
+  });
 });