From f8996dc30a3494aed75f2993283ff380d6458762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Paiva?= Date: Mon, 30 Oct 2017 14:44:04 +0100 Subject: [PATCH 1/2] Create the property holdPlaceholder to keep the placeholder for some time --- README.md | 6 ++- src/ReactPlaceholder.js | 31 +++++++++--- src/index.d.ts | 3 +- tests/ReactPlaceholder.test.js | 47 +++++++++++++++++++ .../ReactPlaceholder.test.js.snap | 16 +++++++ 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 37048a3..7c7244c 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ color: PropTypes.string, customPlaceholder: PropTypes.oneOfType([ PropTypes.node, PropTypes.element - ]) + ]), +holdPlaceholder: PropTypes.number, ``` ### Customization @@ -79,6 +80,9 @@ You can pass an optional `delay` prop which specifies the time (in milliseconds) Note that this delay will __not__ affect the initial render, only subsequent "ready" state changes. Setting the `firsLaunchOnly` prop to `true` will also disable this feature. +### Hold Placeholder +You can pass an optional `holdPlaceholder` prop which specifies the time (in milliseconds) `react-placeholder` should keep displaying the placeholder before displaying the content element even if the `ready` prop is `true`. This is useful if you want to keep displaying the placeholder for slower connections while avoiding a brief "flash" on faster connections. + ### Animation `react-placeholder` already comes with one default pulse animation to better tell the user that the page is loading. The animation is defined in a separate CSS file so, in order to enable it, you should import that style in your project like this: diff --git a/src/ReactPlaceholder.js b/src/ReactPlaceholder.js index 76a0456..d52b90d 100644 --- a/src/ReactPlaceholder.js +++ b/src/ReactPlaceholder.js @@ -19,22 +19,25 @@ export default class ReactPlaceholder extends React.Component { customPlaceholder: PropTypes.oneOfType([ PropTypes.node, PropTypes.element - ]) + ]), + holdPlaceholder: PropTypes.number } static defaultProps = { delay: 0, type: 'text', - color: '#CDCDCD' + color: '#CDCDCD', + holdPlaceholder: 0 } state = { - ready: this.props.ready + ready: this.props.ready, + holdPlaceholder: this.props.holdPlaceholder > 0 } getFiller = () => { const { - firstLaunchOnly, children, ready, className, // eslint-disable-line no-unused-vars + firstLaunchOnly, children, ready, className, holdPlaceholder, // eslint-disable-line no-unused-vars type, customPlaceholder, showLoadingAnimation, ...rest } = this.props; @@ -80,14 +83,28 @@ export default class ReactPlaceholder extends React.Component { } render() { - return this.state.ready ? this.props.children : this.getFiller(); + const { ready, holdPlaceholder } = this.state; + return ready && !holdPlaceholder ? this.props.children : this.getFiller(); } componentWillReceiveProps(nextProps) { - if (!this.props.firstLaunchOnly && this.state.ready && !nextProps.ready) { - this.setNotReady(); + if (!this.props.firstLaunchOnly && !nextProps.ready) { + if (this.state.ready) { + this.setNotReady(); + } + if (this.props.holdPlaceholder) { + clearTimeout(this.holdPlaceholderTimeout); + this.setState({ holdPlaceholder: true }); + this.holdPlaceholderTimeout = setTimeout(() => this.setState({ holdPlaceholder: false }), nextProps.holdPlaceholder); + } } else if (nextProps.ready) { this.setReady(); } } + + componentWillMount() { + if (this.props.holdPlaceholder > 0) { + this.holdPlaceholderTimeout = setTimeout(() => this.setState({ holdPlaceholder: false }), this.props.holdPlaceholder); + } + } } diff --git a/src/index.d.ts b/src/index.d.ts index 2e2745f..6b911f1 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -10,7 +10,8 @@ interface ReactPlaceholderProps { rows?: number, color?: string, showLoadingAnimation?: boolean, - customPlaceholder?: ReactNode | ReactElement + customPlaceholder?: ReactNode | ReactElement, + holdPlaceholder?: number, } interface ReactPlaceholderState { diff --git a/tests/ReactPlaceholder.test.js b/tests/ReactPlaceholder.test.js index 7b57500..29d0cd3 100644 --- a/tests/ReactPlaceholder.test.js +++ b/tests/ReactPlaceholder.test.js @@ -53,4 +53,51 @@ describe('ReactPlaceholder', () => { expect(tree.getElements()).toMatchSnapshot(); }); + it('renders the placeholder for a while even if the content is ready', () => { + const content =
Some ready content
; + const tree = shallow( + + {content} + + ); + expect(tree.contains(content)).toBe(false); + jest.runAllTimers(); + tree.update(); + expect(tree.contains(content)).toBe(true); + expect(tree.getElements()).toMatchSnapshot(); + }); + + it('renders the placeholder again for a while even if the content is ready', () => { + const content =
Some ready content
; + const tree = shallow( + + {content} + + ); + expect(tree.contains(content)).toBe(false); + jest.runAllTimers(); + tree.update(); + expect(tree.contains(content)).toBe(true); + tree.setProps({ ready: false }); + tree.update(); + expect(tree.contains(content)).toBe(false); + tree.setProps({ ready: true }); + tree.update(); + expect(tree.contains(content)).toBe(false); + jest.runAllTimers(); + tree.update(); + expect(tree.contains(content)).toBe(true); + expect(tree.getElements()).toMatchSnapshot(); + }); + }); diff --git a/tests/__snapshots__/ReactPlaceholder.test.js.snap b/tests/__snapshots__/ReactPlaceholder.test.js.snap index ac5f173..284e644 100644 --- a/tests/__snapshots__/ReactPlaceholder.test.js.snap +++ b/tests/__snapshots__/ReactPlaceholder.test.js.snap @@ -8,6 +8,22 @@ Array [ ] `; +exports[`ReactPlaceholder renders the placeholder again for a while even if the content is ready 1`] = ` +Array [ +
+ Some ready content +
, +] +`; + +exports[`ReactPlaceholder renders the placeholder for a while even if the content is ready 1`] = ` +Array [ +
+ Some ready content +
, +] +`; + exports[`ReactPlaceholder renders the placeholder only after the specified delay 1`] = ` Array [ Date: Mon, 30 Oct 2017 15:07:03 +0100 Subject: [PATCH 2/2] Provide an example using holdPlaceholder time --- examples/examples.js | 31 ++++++++++++++++++++++++++++++- src/ReactPlaceholder.js | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/examples/examples.js b/examples/examples.js index 0dff43e..90e5c7a 100644 --- a/examples/examples.js +++ b/examples/examples.js @@ -19,7 +19,8 @@ class Example extends React.Component { readyCustom: false, readyFirstLaunch: false, textBlockRows: 6, - mediaBlockRows: 4 + mediaBlockRows: 4, + holdPlaceholder: 2000 }; onChange = (key) => ({ target: { value } }) => { @@ -176,6 +177,34 @@ class Example extends React.Component { +

Using ReactPlaceholder with a hold placeholder time

+ +

Will show the content only after specified time and it's ready

+
+ + holdPlaceholder (ms): + + +
+
+ + {realComponent} + +
+

Using ReactPlaceholder with a delay