From d544024ca2fcabb679a4e78337a269824360b606 Mon Sep 17 00:00:00 2001 From: quiaro Date: Tue, 18 Jul 2017 19:26:36 -0600 Subject: [PATCH] [Popover] Doesn't reposition with anchorEl Closes #5937 --- src/internal/Popover.js | 31 ++++++++++++++++++++++++++----- src/internal/Popover.spec.js | 29 ++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/internal/Popover.js b/src/internal/Popover.js index 6e170fe1f76af2..fe68cb89aeb759 100644 --- a/src/internal/Popover.js +++ b/src/internal/Popover.js @@ -2,9 +2,12 @@ import React, { Component } from 'react'; import type { Element } from 'react'; +import ReactDOM from 'react-dom'; import classNames from 'classnames'; import { createStyleSheet } from 'jss-theme-reactor'; import contains from 'dom-helpers/query/contains'; +import debounce from 'lodash/debounce'; +import EventListener from 'react-event-listener'; import withStyles from '../styles/withStyles'; import customPropTypes from '../utils/customPropTypes'; import Modal from './Modal'; @@ -211,7 +214,22 @@ class Popover extends Component { return `scale(${value}, ${value ** 2})`; } + componentWillUnmount = () => { + this.handleResize.cancel(); + }; + autoTransitionDuration = undefined; + transitionEl = undefined; + + setPositioningStyles = (element: HTMLElement) => { + if (element && element.style) { + const positioning = this.getPositioningStyle(element); + + element.style.top = positioning.top; + element.style.left = positioning.left; + element.style.transformOrigin = positioning.transformOrigin; + } + }; handleEnter = (element: HTMLElement) => { element.style.opacity = '0'; @@ -221,11 +239,7 @@ class Popover extends Component { this.props.onEnter(element); } - const positioning = this.getPositioningStyle(element); - - element.style.top = positioning.top; - element.style.left = positioning.left; - element.style.transformOrigin = positioning.transformOrigin; + this.setPositioningStyles(element); let { transitionDuration } = this.props; const { transitions } = this.context.styleManager.theme; @@ -281,6 +295,11 @@ class Popover extends Component { } }; + handleResize = debounce(() => { + const element: any = ReactDOM.findDOMNode(this.transitionEl); + this.setPositioningStyles(element); + }, 166); + handleRequestTimeout = () => { if (this.props.transitionDuration === 'auto') { return (this.autoTransitionDuration || 0) + 20; @@ -437,6 +456,7 @@ class Popover extends Component { role={role} onRequestTimeout={this.handleRequestTimeout} transitionAppear + ref={node => (this.transitionEl = node)} > { elevation={elevation} {...other} > + {children} diff --git a/src/internal/Popover.spec.js b/src/internal/Popover.spec.js index 95530767429fb0..cfc993908cb83a 100644 --- a/src/internal/Popover.spec.js +++ b/src/internal/Popover.spec.js @@ -2,7 +2,7 @@ import React from 'react'; import { assert } from 'chai'; -import { spy, stub } from 'sinon'; +import { spy, stub, useFakeTimers } from 'sinon'; import css from 'dom-helpers/style'; import { createShallow, createMount } from '../test-utils'; import Popover, { styleSheet } from './Popover'; @@ -416,6 +416,33 @@ describe('', () => { }); }); + describe('on window resize', () => { + let clock; + + before(() => { + clock = useFakeTimers(); + }); + + after(() => { + clock.restore(); + }); + + it('should recalculate position if the popover is open', () => { + const wrapper = shallow(); + const instance = wrapper.instance(); + + stub(instance, 'setPositioningStyles'); + wrapper.find('EventListener').at(0).simulate('resize'); + clock.tick(166); + assert.isTrue(instance.setPositioningStyles.called, 'position styles recalculated'); + }); + + it('should not recalculate position if the popover is closed', () => { + const wrapper = mount(); + assert.isNotTrue(wrapper.contains('EventListener'), 'no component listening on resize'); + }); + }); + describe('getPositioningStyle(element)', () => { let instance; let element;