Skip to content

Commit

Permalink
[Popover] Support anchorEl as a function (#10420)
Browse files Browse the repository at this point in the history
* Added functional support for Popover (resolves #10411).
Fixed nested ternary operators.

* Replaced anchorEl component-wide with getter. (re #10420)

* Fix for `anchorEl` prop on a DOM element

* Attempt at resolving pages.

* isolation
  • Loading branch information
quisido authored and oliviertassinari committed Feb 23, 2018
1 parent 8555fda commit 4412572
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 21 deletions.
2 changes: 1 addition & 1 deletion pages/api/popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ filename: /src/Popover/Popover.js
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">action</span> | <span class="prop-type">func | | This is callback property. It's called by the component on mount. This is useful when you want to trigger an action programmatically. It currently only supports updatePosition() action.<br><br>**Signature:**<br>`function(actions: object) => void`<br>*actions:* This object contains all posible actions that can be triggered programmatically. |
| <span class="prop-name">anchorEl</span> | <span class="prop-type">object | | This is the DOM element that may be used to set the position of the popover. |
| <span class="prop-name">anchorEl</span> | <span class="prop-type">union:&nbsp;object&nbsp;&#124;<br>&nbsp;func<br> | | This is the DOM element, or a function that returns the DOM element, that may be used to set the position of the popover. |
| <span class="prop-name">anchorOrigin</span> | <span class="prop-type">{horizontal?: union:&nbsp;number&nbsp;&#124;<br>&nbsp;enum:&nbsp;'left'&nbsp;&#124;<br>&nbsp;'center'&nbsp;&#124;<br>&nbsp;'right'<br><br>, vertical?: union:&nbsp;number&nbsp;&#124;<br>&nbsp;enum:&nbsp;'top'&nbsp;&#124;<br>&nbsp;'center'&nbsp;&#124;<br>&nbsp;'bottom'<br><br>} | <span class="prop-default">{ vertical: 'top', horizontal: 'left',}</span> | This is the point on the anchor where the popover's `anchorEl` will attach to. This is not used when the anchorReference is 'anchorPosition'.<br>Options: vertical: [top, center, bottom]; horizontal: [left, center, right]. |
| <span class="prop-name">anchorPosition</span> | <span class="prop-type">{top?: number, left?: number} | | This is the position that may be used to set the position of the popover. The coordinates are relative to the application's client area. |
| <span class="prop-name">anchorReference</span> | <span class="prop-type">enum:&nbsp;'anchorEl'&nbsp;&#124;<br>&nbsp;'anchorPosition'<br> | <span class="prop-default">'anchorEl'</span> | |
Expand Down
20 changes: 13 additions & 7 deletions src/Popover/Popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ function getScrollParent(parent, child) {
return scrollTop;
}

function getAnchorEl(anchorEl) {
return typeof anchorEl === 'function' ? anchorEl() : anchorEl;
}

export const styles = {
paper: {
position: 'absolute',
Expand Down Expand Up @@ -123,7 +127,7 @@ class Popover extends React.Component {
const right = left + elemRect.width;

// Use the parent window of the anchorEl if provided
const containerWindow = ownerWindow(anchorEl);
const containerWindow = ownerWindow(getAnchorEl(anchorEl));

// Window thresholds taking required margin into account
const heightThreshold = containerWindow.innerHeight - marginThreshold;
Expand Down Expand Up @@ -177,7 +181,8 @@ class Popover extends React.Component {
}

// If an anchor element wasn't provided, just use the parent body element of this Popover
const anchorElement = anchorEl || ownerDocument(ReactDOM.findDOMNode(this.transitionEl)).body;
const anchorElement =
getAnchorEl(anchorEl) || ownerDocument(ReactDOM.findDOMNode(this.transitionEl)).body;
const anchorRect = anchorElement.getBoundingClientRect();
const anchorVertical = contentAnchorOffset === 0 ? anchorOrigin.vertical : 'center';

Expand Down Expand Up @@ -247,6 +252,7 @@ class Popover extends React.Component {

render() {
const {
action,
anchorEl,
anchorOrigin,
anchorPosition,
Expand All @@ -269,14 +275,14 @@ class Popover extends React.Component {
transformOrigin,
transition: TransitionProp,
transitionDuration,
action,
...other
} = this.props;

// If the container prop is provided, use that
// If the anchorEl prop is provided, use its parent body element as the container
// If neither are provided let the Modal take care of choosing the container
const container = containerProp || (anchorEl ? ownerDocument(anchorEl).body : undefined);
const container =
containerProp || (anchorEl ? ownerDocument(getAnchorEl(anchorEl)).body : undefined);

const transitionProps = {};
// The provided transition might not support the auto timeout value.
Expand Down Expand Up @@ -327,10 +333,10 @@ Popover.propTypes = {
*/
action: PropTypes.func,
/**
* This is the DOM element that may be used
* to set the position of the popover.
* This is the DOM element, or a function that returns the DOM element,
* that may be used to set the position of the popover.
*/
anchorEl: PropTypes.object,
anchorEl: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
/**
* This is the point on the anchor where the popover's
* `anchorEl` will attach to. This is not used when the
Expand Down
38 changes: 25 additions & 13 deletions src/Popover/Popover.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,18 @@ describe('<Popover />', () => {
});
});

describe('prop: anchorEl', () => {
it('should accept a function', () => {
const anchorElSpy = spy();
shallow(
<Popover {...defaultProps} anchorEl={anchorElSpy}>
<div />
</Popover>,
);
assert.strictEqual(anchorElSpy.callCount, 1);
});
});

describe('positioning on an anchor', () => {
let anchorEl;
let wrapper;
Expand All @@ -298,20 +310,20 @@ describe('<Popover />', () => {

before(() => {
openPopover = (anchorOrigin, renderShallow) => {
return new Promise(resolve => {
if (!anchorEl) {
anchorEl = window.document.createElement('div');
}

css(anchorEl, {
width: '50px',
height: '50px',
position: 'absolute',
top: '100px',
left: '100px',
});
window.document.body.appendChild(anchorEl);
if (!anchorEl) {
anchorEl = window.document.createElement('div');
}

css(anchorEl, {
width: '50px',
height: '50px',
position: 'absolute',
top: '100px',
left: '100px',
});
window.document.body.appendChild(anchorEl);

return new Promise(resolve => {
const component = (
<Popover
{...defaultProps}
Expand Down

0 comments on commit 4412572

Please sign in to comment.