@@ -67,11 +67,13 @@ import {
6767 _EuiFlyoutPaddingSize ,
6868 _EuiFlyoutSide ,
6969 _EuiFlyoutType ,
70+ DEFAULT_MENU_DISPLAY_MODE ,
7071 DEFAULT_PADDING_SIZE ,
7172 DEFAULT_PUSH_MIN_BREAKPOINT ,
7273 DEFAULT_SIDE ,
7374 DEFAULT_SIZE ,
7475 DEFAULT_TYPE ,
76+ EuiFlyoutMenuDisplayMode ,
7577 EuiFlyoutSize ,
7678 isEuiFlyoutSizeNamed ,
7779} from './const' ;
@@ -84,6 +86,7 @@ import type { EuiFlyoutCloseEvent } from './types';
8486import { useEuiFlyoutZIndex } from './use_flyout_z_index' ;
8587import { EuiFlyoutParentProvider } from './flyout_parent_context' ;
8688import { useCurrentFlyoutZIndexRef } from './manager/selectors' ;
89+ import { useEuiFlyoutMenu } from './use_flyout_menu' ;
8790
8891interface _EuiFlyoutComponentProps {
8992 /**
@@ -200,9 +203,22 @@ interface _EuiFlyoutComponentProps {
200203 /**
201204 * Props for the flyout menu to have one rendered in the flyout.
202205 * If used, the close button will be automatically hidden, as the flyout menu has its own close button.
206+ *
207+ * Use `flyoutMenuDisplayMode` to control whether/when the menu is rendered. See {@link EuiFlyoutMenuDisplayMode}.
203208 */
204209 flyoutMenuProps ?: EuiFlyoutMenuProps ;
205210
211+ /**
212+ * Controls the display mode of the flyout menu:
213+ * - `'always'`: Render the menu whenever menu props are available. This may result in a close-only menu.
214+ * - `'auto'`: Render the menu whenever menu props are available and there is navigation content (back button, history, custom actions)
215+ * or a visible title.
216+ * - `'hidden'`: Never render the menu (the flyout close button will be used instead).
217+ *
218+ * @default 'always'
219+ */
220+ flyoutMenuDisplayMode ?: EuiFlyoutMenuDisplayMode ;
221+
206222 /**
207223 * Whether the flyout should be resizable.
208224 * @default false
@@ -242,6 +258,7 @@ export const EuiFlyoutComponent = forwardRef(
242258 as,
243259 hideCloseButton = false ,
244260 flyoutMenuProps : _flyoutMenuProps ,
261+ flyoutMenuDisplayMode = DEFAULT_MENU_DISPLAY_MODE ,
245262 closeButtonProps,
246263 closeButtonPosition = 'inside' ,
247264 onClose,
@@ -317,6 +334,20 @@ export const EuiFlyoutComponent = forwardRef(
317334 size : _size ,
318335 } ) ;
319336
337+ const {
338+ flyoutMenuId,
339+ flyoutMenuProps,
340+ flyoutMenuHideTitle,
341+ shouldRenderMenu,
342+ ariaLabelledBy,
343+ } = useEuiFlyoutMenu ( {
344+ flyoutMenuProps : _flyoutMenuProps ,
345+ flyoutMenuDisplayMode,
346+ flyoutId,
347+ currentSession,
348+ ariaLabelledBy : _ariaLabelledBy ,
349+ } ) ;
350+
320351 /**
321352 * Setting up the refs on the actual flyout element in order to
322353 * accommodate for the `isPushed` state by adding padding to the body equal to the width of the element
@@ -655,34 +686,6 @@ export const EuiFlyoutComponent = forwardRef(
655686 [ hasOverlayMask , descriptionId , focusTrapShards . length ]
656687 ) ;
657688
658- /*
659- * If the flyout menu is to be rendered, ensure the flyout has aria-labelledby referencing the menu's titleId
660- */
661- const generatedMenuId = useGeneratedHtmlId ( ) ;
662- const { titleId : _titleId , ...flyoutMenuProps } = _flyoutMenuProps || { } ;
663- const hasMenu = ! ! _flyoutMenuProps ;
664-
665- const flyoutMenuId = useMemo ( ( ) => {
666- if ( ! hasMenu ) return undefined ;
667- return _titleId || generatedMenuId ;
668- } , [ hasMenu , _titleId , generatedMenuId ] ) ;
669-
670- // If the flyout level is LEVEL_MAIN, the title should be hidden by default
671- const flyoutMenuHideTitle = useMemo ( ( ) => {
672- if ( ! hasMenu ) return undefined ;
673- if ( _flyoutMenuProps ?. hideTitle !== undefined ) {
674- return _flyoutMenuProps . hideTitle ;
675- }
676- return currentSession ?. mainFlyoutId === flyoutId ;
677- } , [ hasMenu , _flyoutMenuProps , currentSession , flyoutId ] ) ;
678-
679- const ariaLabelledBy = useMemo ( ( ) => {
680- if ( flyoutMenuId ) {
681- return classnames ( flyoutMenuId , _ariaLabelledBy ) ;
682- }
683- return _ariaLabelledBy ;
684- } , [ flyoutMenuId , _ariaLabelledBy ] ) ;
685-
686689 /*
687690 * Trap focus even when `ownFocus={false}`, otherwise closing
688691 * the flyout won't return focus to the originating button.
@@ -755,22 +758,22 @@ export const EuiFlyoutComponent = forwardRef(
755758 data-autofocus = { ! isPushed || undefined }
756759 onAnimationEnd = { onAnimationEnd }
757760 >
758- < EuiFlyoutParentProvider > { children } </ EuiFlyoutParentProvider >
759761 { ! isPushed && screenReaderDescription }
760- { ! _flyoutMenuProps && ! hideCloseButton && (
761- < EuiFlyoutCloseButton
762- { ...closeButtonProps }
763- onClose = { onClose }
764- closeButtonPosition = { closeButtonPosition }
765- side = { side }
766- />
767- ) }
768- { _flyoutMenuProps && (
762+ { shouldRenderMenu ? (
769763 < EuiFlyoutMenu
770764 { ...flyoutMenuProps }
771765 hideTitle = { flyoutMenuHideTitle }
772766 titleId = { flyoutMenuId }
773767 />
768+ ) : (
769+ ! hideCloseButton && (
770+ < EuiFlyoutCloseButton
771+ { ...closeButtonProps }
772+ onClose = { onClose }
773+ closeButtonPosition = { closeButtonPosition }
774+ side = { side }
775+ />
776+ )
774777 ) }
775778 { resizable && (
776779 < EuiFlyoutResizeButton
@@ -783,6 +786,7 @@ export const EuiFlyoutComponent = forwardRef(
783786 onKeyDown = { onKeyDownResizableButton }
784787 />
785788 ) }
789+ < EuiFlyoutParentProvider > { children } </ EuiFlyoutParentProvider >
786790 </ Element >
787791 </ EuiFocusTrap >
788792 </ EuiFlyoutOverlay >
0 commit comments