Skip to content

Commit c75a0ba

Browse files
clintandrewhalltsullivanweronikaolejniczak
authored andcommitted
[flyouts] Developer API + sessions (#8939)
Co-authored-by: Tim Sullivan <[email protected]> Co-authored-by: Timothy Sullivan <[email protected]> Co-authored-by: Weronika Olejniczak <[email protected]>
1 parent 9568ccc commit c75a0ba

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+7019
-4215
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ yarn-error.log*
3232
!.yarn/sdks
3333
!.yarn/versions
3434
yarn-error.log
35+
.cursorrules
36+
WARP.md

packages/eui/src/components/collapsible_nav_beta/context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import { createContext, MouseEventHandler } from 'react';
1010

11-
import { _EuiFlyoutSide } from '../flyout/flyout';
11+
import { _EuiFlyoutSide } from '../flyout/const';
1212

1313
type _EuiCollapsibleNavContext = {
1414
isCollapsed: boolean;
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# EUI Flyout System
2+
3+
## Core Flyout Components
4+
5+
### `src/components/flyout/flyout.tsx`
6+
The main flyout component that serves as the entry point for all flyout functionality. It intelligently renders different flyout types based on context:
7+
- **Session flyouts**: When `session={true}` or within an active session, renders `EuiFlyoutMain`
8+
- **Child flyouts**: When within a managed flyout context, renders `EuiFlyoutChild`
9+
- **Standard flyouts**: Default behavior renders `EuiFlyoutComponent`
10+
- **Resizable flyouts**: `EuiFlyoutResizable` component exists but is not integrated into main routing logic
11+
12+
### `src/components/flyout/flyout.component.tsx`
13+
The core flyout implementation with comprehensive functionality:
14+
- **Props**: Extensive configuration options including size, padding, positioning, focus management
15+
- **Types**: Support for `push` and `overlay` types, left/right sides, various sizes (s/m/l)
16+
- **Accessibility**: Built-in screen reader support, focus trapping, keyboard navigation with sophisticated ESC key handling
17+
- **Styling**: Dynamic width handling, responsive behavior, theme integration
18+
- **Portal/Overlay**: Conditional portal rendering and overlay mask management
19+
- **Session Logic**: Complex routing logic that determines flyout type based on session state and managed context
20+
- **Responsive Behavior**: Adaptive layout switching for managed flyouts based on viewport width and flyout size combinations
21+
22+
### `src/components/flyout/flyout.styles.ts`
23+
Contains the emotion-based styling for the flyout component, including:
24+
- Base flyout styles
25+
- Size-specific styles (s/m/l)
26+
- Padding size variations
27+
- Push vs overlay type styles
28+
- Side-specific positioning (left/right)
29+
- Animation and transition styles
30+
31+
## Flyout Management System
32+
33+
### `src/components/flyout/manager/flyout_manager.tsx`
34+
The central state management system for flyout sessions:
35+
- **Context Provider**: `EuiFlyoutManager` provides global flyout state
36+
- **Session Management**: Tracks main and child flyout relationships with complex state transitions
37+
- **State Reducer**: Handles flyout lifecycle (add, close, set active, set width)
38+
- **Hooks**: Provides utilities like `useHasActiveSession`, `useCurrentSession`, `useFlyoutWidth`
39+
- **Actions**: `addFlyout`, `closeFlyout`, `setActiveFlyout`, `setFlyoutWidth`
40+
- **Responsive Layout**: `useFlyoutLayoutMode` hook manages responsive behavior for managed flyouts with 90% viewport width rule for switching between `side-by-side` and `stacked` layouts
41+
42+
### `src/components/flyout/manager/flyout_main.tsx`
43+
Renders the primary flyout in a session. Currently a simple wrapper around `EuiManagedFlyout` with `session={true}`. TODO items include handling child flyout presence and adjusting focus/shadow behavior.
44+
45+
### `src/components/flyout/manager/flyout_child.tsx`
46+
Renders child flyouts within a session:
47+
- **Positioning**: Automatically positions relative to main flyout width
48+
- **Styling**: Supports `backgroundStyle` prop for default/shaded backgrounds
49+
- **Constraints**: Forces `type="overlay"` and `ownFocus={false}`
50+
- **Width Integration**: Uses main flyout width for positioning
51+
52+
### `src/components/flyout/manager/flyout_managed.tsx`
53+
The managed flyout wrapper that integrates with the flyout manager system, handling registration and lifecycle management. Includes size validation for managed flyouts according to business rules.
54+
55+
### `src/components/flyout/manager/flyout_validation.ts`
56+
Validation utilities for flyout size business rules:
57+
- **Named Size Validation**: Managed flyouts must use named sizes (s, m, l)
58+
- **Size Combination Rules**: Parent and child can't both be 'm', parent can't be 'l' with child
59+
- **Error Handling**: Comprehensive error messages for invalid configurations
60+
61+
### `src/components/flyout/manager/index.ts`
62+
Exports all manager-related components and utilities for easy importing.
63+
64+
## Specialized Flyout Components
65+
66+
### `src/components/flyout/flyout_resizable.tsx`
67+
A resizable flyout variant that adds drag-to-resize functionality:
68+
- **Drag Resize**: Mouse/touch drag to resize flyout width
69+
- **Keyboard Resize**: Arrow key navigation for accessibility
70+
- **Constraints**: Configurable min/max width with window bounds checking
71+
- **Callbacks**: `onResize` callback for width change notifications
72+
- **Visual Indicator**: Resize handle with border indicator
73+
- **Note**: Not yet integrated into main flyout routing logic
74+
75+
### `src/components/flyout/flyout_menu.tsx`
76+
A specialized flyout component for menu-style content:
77+
- **Layout**: Flex-based header with back button, popover, title, and close button
78+
- **Context Integration**: Uses `EuiFlyoutMenuContext` for close handling
79+
- **Accessibility**: Proper ARIA labels and screen reader support
80+
- **Styling**: Custom menu-specific styling via `flyout_menu.styles.ts`
81+
82+
### `src/components/flyout/flyout_menu_context.ts`
83+
React context for flyout menu components, providing `onClose` callback to child components.
84+
85+
## Styling and Theming
86+
87+
### `src/components/flyout/flyout.styles.ts`
88+
Core flyout styling with emotion CSS-in-JS:
89+
- Responsive design patterns
90+
- Theme variable integration
91+
- Animation and transition styles
92+
- Size and positioning utilities
93+
94+
### `src/components/flyout/flyout_menu.styles.ts`
95+
Menu-specific styling for the flyout menu component.
96+
97+
### `src/components/flyout/manager/flyout.styles.ts`
98+
Managed flyout styling, including background styles for child flyouts.
99+
100+
## Testing and Documentation
101+
102+
### `src/components/flyout/flyout.spec.tsx`
103+
Unit tests for the main flyout component functionality.
104+
105+
### `src/components/flyout/flyout.test.tsx`
106+
Additional test coverage for flyout behavior and edge cases.
107+
108+
### `src/components/flyout/flyout_menu.stories.tsx`
109+
Storybook stories demonstrating flyout menu usage and variations.
110+
111+
### `src/components/flyout/manager/flyout_manager.stories.tsx`
112+
Storybook stories for the flyout manager system and session management.
113+
114+
### `src/components/flyout/manager/flyout_child.stories.tsx`
115+
Storybook stories showcasing child flyout behavior and positioning.
116+
117+
## Integration
118+
119+
### `src/components/flyout/index.ts`
120+
Main export file that exposes all public flyout APIs:
121+
- Core components: `EuiFlyout`, `EuiFlyoutComponent`
122+
- Body/Header/Footer components
123+
- Resizable and menu variants
124+
- Animation utilities
125+
126+
### `src/components/provider/provider.tsx`
127+
The EUI provider that includes `EuiFlyoutManager` in its component tree, ensuring flyout management is available throughout the application.
128+
129+
## Key Features
130+
131+
- **Session Management**: Multi-level flyout sessions with main/child relationships
132+
- **Accessibility**: Full keyboard navigation, screen reader support, focus management with sophisticated ESC key handling
133+
- **Responsive Design**: Adaptive behavior based on screen size and breakpoints with intelligent layout switching for managed flyouts (side-by-side vs stacked) when combined flyout widths exceed 90% of viewport
134+
- **Theme Integration**: Seamless integration with EUI's theming system
135+
- **Type Safety**: Comprehensive TypeScript support with proper prop typing and validation
136+
- **Performance**: Optimized rendering with proper cleanup and memory management
137+
- **Size Validation**: Business rule enforcement for flyout size combinations and managed flyout constraints
138+
139+
## TODOs
140+
141+
### Performance Issues
142+
143+
- **Excessive Re-renders**: The flyout manager reducer creates new arrays on every action, causing unnecessary re-renders for all flyout components
144+
- **Unmemoized Style Calculations**: The `cssStyles` array in `flyout.component.tsx` is recalculated on every render without memoization
145+
- **Memory Leaks**: `document.activeElement` is stored in a ref but never cleaned up, potentially causing memory leaks
146+
- **Inefficient DOM Queries**: Focus trap selectors query the DOM on every render without caching
147+
148+
### Accessibility Issues
149+
150+
- **Focus Trap Edge Cases**: The focus trap logic with shards could fail if DOM elements are removed or changed during flyout lifecycle
151+
- **Missing Error Recovery**: No fallback behavior when focus management fails
152+
- **Inconsistent Keyboard Navigation**: Different flyout types may have different keyboard behavior patterns
153+
154+
### Architectural Concerns
155+
156+
- **Tight Coupling**: The flyout system is tightly coupled to the provider system, making it difficult to use standalone
157+
- **State Management Complexity**: The session management system has complex state transitions that could lead to inconsistent UI states
158+
- **Missing Error Boundaries**: No error handling for flyout rendering failures or state corruption
159+
- **Unclear Session Logic**: The complex session routing logic in `flyout.tsx` (lines 40-50) is difficult to understand and maintain
160+
- **Incomplete Integration**: Resizable flyout functionality exists but is not integrated into main routing logic
161+
- **Missing Cleanup**: Focus references and event listeners are not properly cleaned up
162+
163+
### Recommended Improvements
164+
165+
1. **Memoize Style Calculations**: Use `useMemo` for the `cssStyles` array to prevent unnecessary recalculations
166+
2. **Add Error Boundaries**: Wrap flyout components in error boundaries to handle rendering failures gracefully
167+
3. **Improve Type Safety**: Replace `any` types with proper type guards and add comprehensive prop validation
168+
4. **Optimize State Updates**: Use immutable update patterns that minimize re-renders in the manager
169+
5. **Add Cleanup Logic**: Properly clean up focus references and event listeners in useEffect cleanup functions
170+
6. **Simplify Session Logic**: Break down the complex session routing logic into smaller, testable functions
171+
7. **Integrate Resizable Flyouts**: Complete the integration of resizable flyout functionality into the main routing logic
172+
8. **Add Comprehensive Testing**: Add unit tests for complex state transitions and edge cases
173+
9. **Improve Documentation**: Add inline documentation for complex logic and state management patterns
174+
10. **Performance Monitoring**: Add performance monitoring for flyout rendering and state updates
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import { EuiBreakpointSize } from '../../services';
10+
11+
/** Allowed flyout render types. */
12+
export const FLYOUT_TYPES = ['push', 'overlay'] as const;
13+
/** Type representing a supported flyout render type. */
14+
export type _EuiFlyoutType = (typeof FLYOUT_TYPES)[number];
15+
16+
/** Allowed flyout attachment sides. */
17+
export const FLYOUT_SIDES = ['left', 'right'] as const;
18+
/** Type representing a supported flyout side. */
19+
export type _EuiFlyoutSide = (typeof FLYOUT_SIDES)[number];
20+
21+
/** Allowed named flyout sizes used by the manager. */
22+
export const FLYOUT_SIZES = ['s', 'm', 'l'] as const;
23+
/** Type representing a supported named flyout size. */
24+
export type EuiFlyoutSize = (typeof FLYOUT_SIZES)[number];
25+
26+
/** Allowed padding sizes for flyout content. */
27+
export const FLYOUT_PADDING_SIZES = ['none', 's', 'm', 'l'] as const;
28+
/** Type representing a supported flyout padding size. */
29+
export type _EuiFlyoutPaddingSize = (typeof FLYOUT_PADDING_SIZES)[number];
30+
31+
/** Default minimum breakpoint at which push-type flyouts begin to push content. */
32+
export const DEFAULT_PUSH_MIN_BREAKPOINT: EuiBreakpointSize = 'l';
33+
/** Default flyout type when none is provided. */
34+
export const DEFAULT_TYPE: _EuiFlyoutType = 'overlay';
35+
/** Default side where flyouts anchor when none is provided. */
36+
export const DEFAULT_SIDE: _EuiFlyoutSide = 'right';
37+
/** Default named flyout size. */
38+
export const DEFAULT_SIZE: EuiFlyoutSize = 'm';
39+
/** Default padding size inside flyouts. */
40+
export const DEFAULT_PADDING_SIZE: _EuiFlyoutPaddingSize = 'l';
41+
42+
/**
43+
* Custom type checker for named flyout sizes since the prop
44+
* `size` can also be CSSProperties['width'] (string | number)
45+
*/
46+
export function isEuiFlyoutSizeNamed(value: unknown): value is EuiFlyoutSize {
47+
return FLYOUT_SIZES.includes(value as EuiFlyoutSize);
48+
}

0 commit comments

Comments
 (0)