in packages/eui/src/components/popover/popover.tsx [610:799]
render() {
const {
anchorPosition,
button,
insert,
isOpen,
ownFocus,
children,
className,
closePopover,
panelClassName,
panelPaddingSize,
panelProps,
panelRef,
panelStyle,
popoverScreenReaderText,
popoverRef,
hasArrow,
arrowChildren,
repositionOnScroll,
repositionToCrossAxis,
zIndex,
attachToAnchor,
display,
offset,
onPositionChange,
buffer,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
'aria-live': ariaLiveProp,
container,
focusTrapProps,
initialFocus: initialFocusProp,
tabIndex: _tabIndexProp,
...rest
} = this.props;
const tabIndexProp = panelProps?.tabIndex ?? _tabIndexProp;
const styles = euiPopoverStyles();
const popoverStyles = [styles.euiPopover, { display, label: display }];
const classes = classNames(
'euiPopover',
{
'euiPopover-isOpen': this.state.isOpening,
},
className
);
const showArrow = hasArrow && !attachToAnchor;
let panel;
if (!this.state.suppressingPopover && (isOpen || this.state.isClosing)) {
let tabIndex = tabIndexProp;
let initialFocus = initialFocusProp;
let ariaDescribedby;
let ariaLive: HTMLAttributes<any>['aria-live'];
const panelAriaModal = panelProps?.hasOwnProperty('aria-modal')
? panelProps['aria-modal']
: 'true';
const panelRole = panelProps?.hasOwnProperty('role')
? panelProps.role
: 'dialog';
if (ownFocus || panelAriaModal !== 'true') {
tabIndex = tabIndexProp ?? 0;
ariaLive = 'off';
if (!initialFocus) {
initialFocus = () => this.panel!;
}
} else {
ariaLive = ariaLiveProp ?? 'assertive';
}
let focusTrapScreenReaderText;
if (ownFocus || popoverScreenReaderText) {
ariaDescribedby = this.descriptionId;
focusTrapScreenReaderText = (
<EuiScreenReaderOnly>
<p id={this.descriptionId}>
{ownFocus && (
<EuiI18n
token="euiPopover.screenReaderAnnouncement"
default="You are in a dialog. Press Escape, or tap/click outside the dialog to close."
/>
)}
{popoverScreenReaderText}
</p>
</EuiScreenReaderOnly>
);
}
const returnFocus = this.state.isOpenStable ? returnFocusConfig : false;
panel = (
<EuiPortal {...(insert && { insert })}>
<EuiFocusTrap
clickOutsideDisables={true}
onClickOutside={this.onClickOutside}
returnFocus={returnFocus} // Ignore temporary state of indecisive focus
initialFocus={initialFocus}
onEscapeKey={this.onEscapeKey}
disabled={
!ownFocus || !this.state.isOpenStable || this.state.isClosing
}
{...focusTrapProps}
>
<EuiPopoverPanel
{...(panelProps as EuiPopoverPanelProps)}
panelRef={this.panelRef}
isOpen={this.state.isOpening}
position={this.state.arrowPosition}
isAttached={attachToAnchor}
className={classNames(panelClassName, panelProps?.className)}
hasShadow={false}
paddingSize={panelPaddingSize}
tabIndex={tabIndex}
aria-live={ariaLive}
role={panelRole}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
aria-modal={panelAriaModal}
aria-describedby={ariaDescribedby}
style={{
...this.state.popoverStyles,
// Adding `will-change` to reduce risk of a blurry animation in Chrome 86+
willChange: !this.state.isOpenStable
? 'transform, opacity'
: undefined,
}}
>
{showArrow && this.state.arrowPosition && (
<EuiPopoverArrow
position={this.state.arrowPosition}
style={this.state.arrowStyles}
>
{arrowChildren}
</EuiPopoverArrow>
)}
{focusTrapScreenReaderText}
<EuiMutationObserver
observerOptions={{
attributes: true, // element attribute changes
childList: true, // added/removed elements
characterData: true, // text changes
subtree: true, // watch all child elements
}}
onMutation={this.onMutation}
>
{(mutationRef) => <div ref={mutationRef}>{children}</div>}
</EuiMutationObserver>
</EuiPopoverPanel>
</EuiFocusTrap>
</EuiPortal>
);
}
// react-focus-on and related do not register outside click detection
// when disabled, so we still need to conditionally check for that ourselves
if (ownFocus) {
return (
<div
css={popoverStyles}
className={classes}
ref={this.popoverRef}
{...rest}
>
{button instanceof HTMLElement ? null : button}
{panel}
</div>
);
} else {
return (
<EuiOutsideClickDetector onOutsideClick={this.closePopover}>
<div
css={popoverStyles}
className={classes}
ref={this.popoverRef}
onKeyDown={this.onKeyDown}
{...rest}
>
{button instanceof HTMLElement ? null : button}
{panel}
</div>
</EuiOutsideClickDetector>
);
}
}