Skip to content

Commit

Permalink
[withWidth] Add a initalWidth property (#7343)
Browse files Browse the repository at this point in the history
Some side though on this new property.
People using it should have some mechanism so that the property stay the same
between the server side rendering and the client side rendering.

Closes #6068
  • Loading branch information
oliviertassinari committed Jul 5, 2017
1 parent d27a227 commit c5b7cbb
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 16 deletions.
45 changes: 31 additions & 14 deletions src/utils/withWidth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React, { Component } from 'react';
import EventListener from 'react-event-listener';
import PropTypes from 'prop-types';
import createEagerFactory from 'recompose/createEagerFactory';
import wrapDisplayName from 'recompose/wrapDisplayName';
import customPropTypes from '../utils/customPropTypes';
Expand Down Expand Up @@ -42,12 +43,8 @@ function withWidth(options = {}) {
const factory = createEagerFactory(BaseComponent);

class Width extends Component {
static contextTypes = {
styleManager: customPropTypes.muiRequired,
};

state = {
width: null,
width: undefined,
};

componentDidMount() {
Expand Down Expand Up @@ -101,22 +98,21 @@ function withWidth(options = {}) {
}

render() {
const { initalWidth, width, ...other } = this.props;
const props = {
width: this.state.width,
...this.props,
width: width || this.state.width || initalWidth,
...other,
};

/**
* When rendering the component on the server,
* we have no idea about the screen width.
* In order to prevent blinks and help the reconciliation
* we are not rendering the component.
* we have no idea about the client browser screen width.
* In order to prevent blinks and help the reconciliation of the React tree
* we are not rendering the child component.
*
* A better alternative would be to send client hints.
* But the browser support of this API is low:
* http://caniuse.com/#search=client%20hint
* An alternative is to use the `initialWidth` property.
*/
if (props.width === null) {
if (props.width === undefined) {
return null;
}

Expand All @@ -128,6 +124,27 @@ function withWidth(options = {}) {
}
}

Width.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
* 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
*/
initalWidth: PropTypes.oneOf(keys),
/**
* Bypass the width calculation logic.
*/
width: PropTypes.oneOf(keys),
};

Width.contextTypes = {
styleManager: customPropTypes.muiRequired,
};

if (process.env.NODE_ENV !== 'production') {
Width.displayName = wrapDisplayName(BaseComponent, 'withWidth');
}
Expand Down
20 changes: 18 additions & 2 deletions src/utils/withWidth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ describe('withWidth', () => {

describe('prop: width', () => {
it('should be able to override it', () => {
const wrapper = mount(<EmptyWithWidth width="foo" />);
const wrapper = mount(<EmptyWithWidth width="xl" />);

assert.strictEqual(wrapper.find(Empty).props().width, 'foo');
assert.strictEqual(wrapper.find(Empty).props().width, 'xl');
});
});

Expand Down Expand Up @@ -102,9 +102,25 @@ describe('withWidth', () => {

it('should handle resize event', () => {
const wrapper = shallow(<EmptyWithWidth width="sm" />);
assert.strictEqual(wrapper.state().width, undefined);
wrapper.simulate('resize');
clock.tick(166);
assert.strictEqual(wrapper.state().width, TEST_ENV_WIDTH);
});
});

describe('props: initalWidth', () => {
it('should work as expected', () => {
const element = <EmptyWithWidth initalWidth="lg" />;

// First mount on the server
const wrapper1 = shallow(element);
assert.strictEqual(wrapper1.find(Empty).props().width, 'lg');
const wrapper2 = mount(element);

// Second mount on the client
assert.strictEqual(wrapper2.find(Empty).props().width, TEST_ENV_WIDTH);
assert.strictEqual(TEST_ENV_WIDTH !== 'lg', true);
});
});
});

0 comments on commit c5b7cbb

Please sign in to comment.