Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 60 additions & 36 deletions src/components/flyout/flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import React, {
useEffect,
useRef,
useMemo,
useCallback,
useState,
forwardRef,
ComponentPropsWithRef,
Expand Down Expand Up @@ -187,7 +189,7 @@ export const EuiFlyout = forwardRef(
outsideClickCloses,
pushMinBreakpoint = 'l',
pushAnimation = false,
focusTrapProps: _focusTrapProps = {},
focusTrapProps: _focusTrapProps,
includeFixedHeadersInFocusTrap = true,
'aria-describedby': _ariaDescribedBy,
...rest
Expand Down Expand Up @@ -246,23 +248,31 @@ export const EuiFlyout = forwardRef(
/**
* ESC key closes flyout (always?)
*/
const onKeyDown = (event: KeyboardEvent) => {
if (!isPushed && event.key === keys.ESCAPE) {
event.preventDefault();
onClose(event);
}
};
const onKeyDown = useCallback(
(event: KeyboardEvent) => {
if (!isPushed && event.key === keys.ESCAPE) {
event.preventDefault();
onClose(event);
}
},
[onClose, isPushed]
);

/**
* Set inline styles
*/
let newStyle = style;
if (typeof maxWidth !== 'boolean') {
newStyle = { ...newStyle, ...logicalStyle('max-width', maxWidth) };
}
if (!isEuiFlyoutSizeNamed(size)) {
newStyle = { ...newStyle, ...logicalStyle('width', size) };
}
const inlineStyles = useMemo(() => {
const widthStyle =
!isEuiFlyoutSizeNamed(size) && logicalStyle('width', size);
const maxWidthStyle =
typeof maxWidth !== 'boolean' && logicalStyle('max-width', maxWidth);

return {
...style,
...widthStyle,
...maxWidthStyle,
};
}, [style, maxWidth, size]);

const euiTheme = useEuiTheme();
const styles = euiFlyoutStyles(euiTheme);
Expand All @@ -280,8 +290,9 @@ export const EuiFlyout = forwardRef(

const classes = classnames('euiFlyout', className);

let closeButton;
if (onClose && !hideCloseButton) {
const closeButton = useMemo(() => {
if (hideCloseButton || !onClose) return null;

const closeButtonClasses = classnames(
'euiFlyout__closeButton',
closeButtonProps?.className
Expand All @@ -296,7 +307,7 @@ export const EuiFlyout = forwardRef(
closeButtonProps?.css,
];

closeButton = (
return (
<EuiI18n token="euiFlyout.closeAriaLabel" default="Close this dialog">
{(closeAriaLabel: string) => (
<EuiButtonIcon
Expand All @@ -316,7 +327,14 @@ export const EuiFlyout = forwardRef(
)}
</EuiI18n>
);
}
}, [
onClose,
hideCloseButton,
closeButtonPosition,
closeButtonProps,
side,
euiTheme,
]);

/*
* If not disabled, automatically add fixed EuiHeaders as shards
Expand Down Expand Up @@ -345,10 +363,13 @@ export const EuiFlyout = forwardRef(
}
}, [includeFixedHeadersInFocusTrap, resizeRef]);

const focusTrapProps: EuiFlyoutProps['focusTrapProps'] = {
..._focusTrapProps,
shards: [...fixedHeaders, ...(_focusTrapProps.shards || [])],
};
const focusTrapProps: EuiFlyoutProps['focusTrapProps'] = useMemo(
() => ({
..._focusTrapProps,
shards: [...fixedHeaders, ...(_focusTrapProps?.shards || [])],
}),
[fixedHeaders, _focusTrapProps]
);

/*
* Provide meaningful screen reader instructions/details
Expand Down Expand Up @@ -393,19 +414,22 @@ export const EuiFlyout = forwardRef(
* or if `outsideClickCloses={true}` to close on clicks that target
* (both mousedown and mouseup) the overlay mask.
*/
const onClickOutside = (event: MouseEvent | TouchEvent) => {
// Do not close the flyout for any external click
if (outsideClickCloses === false) return undefined;
if (hasOverlayMask) {
// The overlay mask is present, so only clicks on the mask should close the flyout, regardless of outsideClickCloses
if (event.target === maskRef.current) return onClose(event);
} else {
// No overlay mask is present, so any outside clicks should close the flyout
if (outsideClickCloses === true) return onClose(event);
}
// Otherwise if ownFocus is false and outsideClickCloses is undefined, outside clicks should not close the flyout
return undefined;
};
const onClickOutside = useCallback(
(event: MouseEvent | TouchEvent) => {
// Do not close the flyout for any external click
if (outsideClickCloses === false) return undefined;
if (hasOverlayMask) {
// The overlay mask is present, so only clicks on the mask should close the flyout, regardless of outsideClickCloses
if (event.target === maskRef.current) return onClose(event);
} else {
// No overlay mask is present, so any outside clicks should close the flyout
if (outsideClickCloses === true) return onClose(event);
}
// Otherwise if ownFocus is false and outsideClickCloses is undefined, outside clicks should not close the flyout
return undefined;
},
[onClose, hasOverlayMask, outsideClickCloses]
);

let flyout = (
<EuiFocusTrap
Expand All @@ -418,7 +442,7 @@ export const EuiFlyout = forwardRef(
<Element
className={classes}
css={cssStyles}
style={newStyle}
style={inlineStyles}
ref={setRef}
{...(rest as ComponentPropsWithRef<T>)}
role={!isPushed ? 'dialog' : rest.role}
Expand Down
3 changes: 3 additions & 0 deletions upcoming_changelogs/7259.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Bug fixes**

- Fixed focus trap rerender issues in `EuiFlyout` with memoization