@shopify/polaris
Version:
Shopify’s product component library
80 lines (79 loc) • 3.16 kB
JavaScript
import React from 'react';
import { createUniqueIDFactory } from '@shopify/javascript-utilities/other';
import { findFirstFocusableNode } from '@shopify/javascript-utilities/focus';
import { Portal } from '../Portal';
import { TooltipOverlay } from './components';
import styles from './Tooltip.scss';
const getUniqueID = createUniqueIDFactory('TooltipContent');
export class Tooltip extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {
active: Boolean(this.props.active),
activatorNode: null,
};
this.id = getUniqueID();
this.mouseEntered = false;
this.setActivator = (node) => {
if (node == null) {
this.activatorContainer = null;
this.setState({ activatorNode: null });
return;
}
this.setState({ activatorNode: node.firstElementChild });
this.activatorContainer = node;
};
this.handleFocus = () => {
this.setState({ active: true });
};
this.handleBlur = () => {
this.setState({ active: false });
};
this.handleMouseEnter = () => {
this.mouseEntered = true;
this.setState({ active: true });
};
this.handleMouseLeave = () => {
this.mouseEntered = false;
this.setState({ active: false });
};
// https://github.com/facebook/react/issues/10109
// Mouseenter event not triggered when cursor moves from disabled button
this.handleMouseEnterFix = () => {
!this.mouseEntered && this.handleMouseEnter();
};
}
componentDidMount() {
this.setAccessibilityAttributes();
}
componentDidUpdate() {
this.setAccessibilityAttributes();
}
render() {
const { id } = this;
const { children, content, light, preferredPosition = 'below', activatorWrapper: WrapperComponent = 'span', } = this.props;
const { active, activatorNode } = this.state;
const portal = activatorNode ? (<Portal idPrefix="tooltip">
<TooltipOverlay id={id} preferredPosition={preferredPosition} activator={activatorNode} active={active} onClose={noop} light={light}>
<div className={styles.Label} testID="TooltipOverlayLabel">
{content}
</div>
</TooltipOverlay>
</Portal>) : null;
return (<WrapperComponent testID="WrapperComponent" onFocus={this.handleFocus} onBlur={this.handleBlur} onMouseLeave={this.handleMouseLeave} onMouseOver={this.handleMouseEnterFix} ref={this.setActivator}>
{children}
{portal}
</WrapperComponent>);
}
setAccessibilityAttributes() {
const { activatorContainer, id } = this;
if (activatorContainer == null) {
return;
}
const firstFocusable = findFirstFocusableNode(activatorContainer);
const accessibilityNode = firstFocusable || activatorContainer;
accessibilityNode.tabIndex = 0;
accessibilityNode.setAttribute('aria-describedby', id);
}
}
function noop() { }