diff --git a/CHANGELOG.md b/CHANGELOG.md index b2bc4f10e4d..de32fefe811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Added `spacesApp` logo to `EuiIcon` set ([#1065](https://github.com/elastic/eui/pull/1065)) - Added `!default` to border SASS props ([#1079](https://github.com/elastic/eui/pull/1079)) +- Added `repositionOnScroll` prop to `EuiPopover` which enables repositioning the popover when the window is scrolled. ([#1064](https://github.com/elastic/eui/pull/1064)) **Bug fixes** diff --git a/src-docs/src/views/popover/popover_example.js b/src-docs/src/views/popover/popover_example.js index 4b86b9d15ed..ac41ed3dedb 100644 --- a/src-docs/src/views/popover/popover_example.js +++ b/src-docs/src/views/popover/popover_example.js @@ -44,6 +44,9 @@ import PopoverContainer from './popover_container'; const popoverContainerSource = require('!!raw-loader!./popover_container'); const popoverContainerHtml = renderToHtml(PopoverContainer); +import PopoverFixed from './popover_fixed'; +const popoverFixedSource = require('!!raw-loader!./popover_fixed'); +const popoverFixedHtml = renderToHtml(PopoverFixed); export const PopoverExample = { title: 'Popover', @@ -204,5 +207,22 @@ export const PopoverExample = { ), demo: , + }, { + title: 'Popover on a fixed element', + source: [{ + type: GuideSectionTypes.JS, + code: popoverFixedSource, + }, { + type: GuideSectionTypes.HTML, + code: popoverFixedHtml, + }], + text: ( +
+

+ Popover content even works on position: fixed; elements. +

+
+ ), + demo: , }], }; diff --git a/src-docs/src/views/popover/popover_fixed.js b/src-docs/src/views/popover/popover_fixed.js new file mode 100644 index 00000000000..f543a513353 --- /dev/null +++ b/src-docs/src/views/popover/popover_fixed.js @@ -0,0 +1,67 @@ +import React, { + Component, +} from 'react'; + +import { + EuiButton, + EuiPopover, +} from '../../../../src/components'; + +export default class PopoverContainer extends Component { + constructor(props) { + super(props); + + this.state = { + isExampleShown: false, + isPopoverOpen: false, + }; + } + + toggleExample = () => this.setState(({ isExampleShown }) => ({ isExampleShown: !isExampleShown })) + + onButtonClick = () => { + this.setState({ + isPopoverOpen: !this.state.isPopoverOpen, + }); + } + + closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + } + + setPanelRef = node => this.panel = node; + + render() { + const button = ( + + Show fixed popover + + ); + + return ( + + Toggle Example + {this.state.isExampleShown && ( + +
+ This popover scrolls with the button element! +
+
+ )} +
+ ); + } +} diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 719b1013b73..8a0db98038c 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -153,6 +153,10 @@ export class EuiPopover extends Component { this.setState({ suppressingPopover: false, isOpening: true }); // eslint-disable-line react/no-did-mount-set-state } + if (this.props.repositionOnScroll) { + window.addEventListener('scroll', this.positionPopover); + } + this.updateFocus(); } @@ -169,6 +173,15 @@ export class EuiPopover extends Component { }); } + // update scroll listener + if (prevProps.repositionOnScroll !== this.props.repositionOnScroll) { + if (this.props.repositionOnScroll) { + window.addEventListener('scroll', this.positionPopover); + } else { + window.removeEventListener('scroll', this.positionPopover); + } + } + // The popover is being closed. if (prevProps.isOpen && !this.props.isOpen) { // If the user has just closed the popover, queue up the removal of the content after the @@ -184,6 +197,7 @@ export class EuiPopover extends Component { } componentWillUnmount() { + window.removeEventListener('scroll', this.positionPopover); clearTimeout(this.closingTransitionTimeout); } @@ -420,6 +434,8 @@ EuiPopover.propTypes = { PropTypes.node, PropTypes.instanceOf(HTMLElement) ]), + /** When `true`, the popover's position is re-calculated when the user scrolls, this supports having fixed-position popover anchors. */ + repositionOnScroll: PropTypes.bool, }; EuiPopover.defaultProps = {