diff --git a/src/components/delay_hide/delay_hide.js b/src/components/delay_hide/delay_hide.js index 1646681ad89..5ced1e47a5f 100644 --- a/src/components/delay_hide/delay_hide.js +++ b/src/components/delay_hide/delay_hide.js @@ -1,41 +1,34 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +function isComponentBecomingVisible(prevHide, nextHide) { + return prevHide === true && nextHide === false; +} + export class EuiDelayHide extends Component { static propTypes = { hide: PropTypes.bool, minimumDuration: PropTypes.number, - render: PropTypes.func.isRequired + render: PropTypes.func.isRequired, }; static defaultProps = { hide: false, - minimumDuration: 1000 + minimumDuration: 1000, }; static getDerivedStateFromProps(nextProps, prevState) { - // if the component should be visible (nextProps.hide === false) - // but we're currently suppresing it, update state.countdownExpired - if (nextProps.hide === false && prevState.countdownExpired === true) { - return { - countdownExpired: false, - }; - } - - return null; - } - - constructor(...args) { - super(...args); - - this.timeoutId = null; // track timeout so it can be referenced / cleared - - this.state = { - // start countdownExpired based on the hide prop - countdownExpired: this.props.hide, + const isBecomingVisible = isComponentBecomingVisible(prevState.hide, nextProps.hide); + return { + hide: nextProps.hide, + countdownExpired: isBecomingVisible ? false : prevState.countdownExpired }; } + state = { + countdownExpired: this.props.hide, + }; + componentDidMount() { // if the component begins visible start counting if (this.props.hide === false) { @@ -44,8 +37,8 @@ export class EuiDelayHide extends Component { } componentDidUpdate(prevProps) { - const isComponentBecomingVisible = prevProps.hide === true && this.props.hide === false; - if (isComponentBecomingVisible) { + const isBecomingVisible = isComponentBecomingVisible(prevProps.hide, this.props.hide); + if (isBecomingVisible) { this.startCountdown(); } } @@ -61,12 +54,12 @@ export class EuiDelayHide extends Component { if (this.timeoutId == null) { this.timeoutId = setTimeout(this.finishCountdown, this.props.minimumDuration); } - } + }; finishCountdown = () => { this.timeoutId = null; this.setState({ countdownExpired: true }); - } + }; render() { const shouldHideContent = this.props.hide === true && this.state.countdownExpired; diff --git a/src/components/delay_hide/delay_hide.test.js b/src/components/delay_hide/delay_hide.test.js index 778b6ef1ee2..be5fb9ba50d 100644 --- a/src/components/delay_hide/delay_hide.test.js +++ b/src/components/delay_hide/delay_hide.test.js @@ -49,6 +49,26 @@ describe('when EuiDelayHide is visible initially', () => { }); }); +describe('when EuiDelayHide parent updates', () => { + it('should still hide correctly', () => { + jest.useFakeTimers(); + const wrapper = mount( +
Hello World
} + /> + ); + + wrapper.setProps({ hide: false }); + jest.advanceTimersByTime(1100); + wrapper.setProps(); // simulate parent component re-rendering + wrapper.setProps({ hide: true }); + jest.advanceTimersByTime(1100); + + expect(wrapper.html()).toEqual(null); + }); +}); + describe('when EuiDelayHide is hidden initially', () => { let wrapper; beforeEach(() => { @@ -107,3 +127,28 @@ describe('when EuiDelayHide is visible initially and has a minimumDuration of 20 expect(wrapper.html()).toEqual(null); }); }); + +describe('when EuiDelayHide has been visible and become hidden', () => { + it('should still be visible for the minimum duration the second time', () => { + jest.useFakeTimers(); + const wrapper = mount( +
Hello World
} + /> + ); + + wrapper.setProps({ hide: false }); + jest.advanceTimersByTime(1100); + wrapper.setProps({ hide: true }); + jest.advanceTimersByTime(100); + wrapper.setProps({ hide: false }); + wrapper.setProps({ hide: true }); + + expect(wrapper.html()).toEqual('
Hello World
'); + + jest.advanceTimersByTime(1100); + + expect(wrapper.html()).toEqual(null); + }); +});