@shopify/polaris
Version:
Shopify’s product component library
77 lines (76 loc) • 3.26 kB
JavaScript
import * as tslib_1 from "tslib";
import React from 'react';
import { createUniqueIDFactory } from '@shopify/javascript-utilities/other';
import { focusFirstFocusableNode, findFirstFocusableNode, } from '@shopify/javascript-utilities/focus';
import { Portal } from '../Portal';
import { CloseSource, Pane, PopoverOverlay, Section } from './components';
export { CloseSource };
const getUniqueID = createUniqueIDFactory('Popover');
export class Popover extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {
activatorNode: null,
};
this.activatorContainer = null;
this.id = getUniqueID();
this.handleClose = (source) => {
this.props.onClose(source);
if (this.activatorContainer == null) {
return;
}
if (source === CloseSource.FocusOut ||
source === CloseSource.EscapeKeypress) {
focusFirstFocusableNode(this.activatorContainer, false);
}
};
this.setActivator = (node) => {
if (node == null) {
this.activatorContainer = null;
this.setState({ activatorNode: null });
return;
}
this.setState({ activatorNode: node.firstElementChild });
this.activatorContainer = node;
};
}
componentDidMount() {
this.setAccessibilityAttributes();
}
componentDidUpdate() {
if (this.activatorContainer &&
this.state.activatorNode &&
!this.activatorContainer.contains(this.state.activatorNode)) {
this.setActivator(this.activatorContainer);
}
this.setAccessibilityAttributes();
}
render() {
const _a = this.props, { activatorWrapper: WrapperComponent = 'div', children, onClose, activator, active, fixed } = _a, rest = tslib_1.__rest(_a, ["activatorWrapper", "children", "onClose", "activator", "active", "fixed"]);
const { activatorNode } = this.state;
const portal = activatorNode ? (<Portal idPrefix="popover" testID="portal">
<PopoverOverlay testID="popoverOverlay" id={this.id} activator={activatorNode} onClose={this.handleClose} active={active} fixed={fixed} {...rest}>
{children}
</PopoverOverlay>
</Portal>) : null;
return (<WrapperComponent testID="wrapper-component" ref={this.setActivator}>
{React.Children.only(this.props.activator)}
{portal}
</WrapperComponent>);
}
setAccessibilityAttributes() {
const { id, activatorContainer } = this;
if (activatorContainer == null) {
return;
}
const firstFocusable = findFirstFocusableNode(activatorContainer);
const focusableActivator = firstFocusable || activatorContainer;
focusableActivator.tabIndex = focusableActivator.tabIndex || 0;
focusableActivator.setAttribute('aria-controls', id);
focusableActivator.setAttribute('aria-owns', id);
focusableActivator.setAttribute('aria-haspopup', 'true');
focusableActivator.setAttribute('aria-expanded', String(this.props.active));
}
}
Popover.Pane = Pane;
Popover.Section = Section;