Skip to content

Commit 1d8186a

Browse files
feat: refactor active state management and test
1 parent 0ab0c2b commit 1d8186a

27 files changed

+3767
-480
lines changed

src/core/packages/chrome/navigation/README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,6 @@ An adaptive side navigation system built with [Elastic UI](https://eui.elastic.c
66
| ----------------------------- | ------------------------------ |
77
| ![image](./expanded-mode.png) | ![image](./collapsed-mode.png) |
88

9-
## Features
10-
11-
- **Responsive design** - automatically adapts between collapsed and expanded states based on screen size.
12-
- **Smart menu system** - dynamic "More" menu that consolidates overflow items during window resize.
13-
- **Nested navigation** - multi-level menu support with nested popover panels in collapsed mode.
14-
- **Accessibility-first** - WCAG-compliant with proper ARIA labels, keyboard navigation, and screen reader support.
15-
- **Modular architecture** - composable components with clean separation of concerns. Exported as a self-contained widget.
16-
- **Dark mode** and **High contrast mode support**
17-
189
## Usage
1910

2011
### Basic setup
@@ -66,6 +57,12 @@ const navigationItems = {
6657

6758
function App() {
6859
const [isCollapsed, setIsCollapsed] = useState(false);
60+
const [activeItemId, setActiveItemId] = useState('dashboard');
61+
62+
const handleItemClick = (item: MenuItem | SecondaryMenuItem | SideNavLogo) => {
63+
setActiveItemId(item.id);
64+
trackAnalytics(item.id);
65+
};
6966

7067
return (
7168
<div className="app">
@@ -80,6 +77,7 @@ function App() {
8077
iconType: 'observabilityApp',
8178
href: '/observability',
8279
}}
80+
onItemClick={handleItemClick}
8381
setWidth={setNavigationWidth}
8482
/>
8583
<main className="app-content">{/* Your application content */}</main>
@@ -101,6 +99,7 @@ export const navigationItems = {
10199
label: 'Overview',
102100
iconType: 'info',
103101
href: '/overview',
102+
badgeType: 'techPreview', // for tech preview items
104103
},
105104
// Menu item with nested sections
106105
{
@@ -111,7 +110,7 @@ export const navigationItems = {
111110
sections: [
112111
{
113112
id: 'reports-section',
114-
label: 'Reports', // or null for unlabeled sections
113+
label: 'Reports', // omit for unlabeled sections
115114
items: [
116115
{
117116
id: 'analytics', // has the same `id` as the parent item
@@ -122,6 +121,7 @@ export const navigationItems = {
122121
id: 'sales-report',
123122
label: 'Sales report',
124123
href: '/analytics/sales',
124+
badgeType: 'beta', // for beta items
125125
},
126126
{
127127
id: 'traffic-report',

src/core/packages/chrome/navigation/src/__stories__/navigation.stories.tsx

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export const Default: StoryObj<PropsAndArgs> = {
9191
);
9292
},
9393
],
94+
render: (args) => <ControlledNavigation {...args} />,
9495
};
9596

9697
export const Collapsed: StoryObj<PropsAndArgs> = {
@@ -108,6 +109,7 @@ export const Collapsed: StoryObj<PropsAndArgs> = {
108109
args: {
109110
isCollapsed: true,
110111
},
112+
render: (args) => <ControlledNavigation {...args} />,
111113
};
112114

113115
export const WithMinimalItems: StoryObj<PropsAndArgs> = {
@@ -128,6 +130,7 @@ export const WithMinimalItems: StoryObj<PropsAndArgs> = {
128130
footerItems: PRIMARY_MENU_FOOTER_ITEMS.slice(0, 2),
129131
},
130132
},
133+
render: (args) => <ControlledNavigation {...args} />,
131134
};
132135

133136
export const WithManyItems: StoryObj<PropsAndArgs> = {
@@ -168,6 +171,24 @@ export const WithManyItems: StoryObj<PropsAndArgs> = {
168171
footerItems: PRIMARY_MENU_FOOTER_ITEMS,
169172
},
170173
},
174+
render: (args) => <ControlledNavigation {...args} />,
175+
};
176+
177+
export const WithinLayout: StoryObj<PropsAndArgs> = {
178+
name: 'Navigation within Layout',
179+
render: (args) => <Layout {...args} />,
180+
};
181+
182+
const ControlledNavigation = ({ ...props }: PropsAndArgs) => {
183+
const [activeItemId, setActiveItemId] = useState(props.activeItemId || PRIMARY_MENU_ITEMS[0].id);
184+
185+
return (
186+
<Navigation
187+
{...props}
188+
activeItemId={activeItemId}
189+
onItemClick={(item) => setActiveItemId(item.id)}
190+
/>
191+
);
171192
};
172193

173194
const Layout = ({ ...props }: PropsAndArgs) => {
@@ -212,15 +233,7 @@ const Layout = ({ ...props }: PropsAndArgs) => {
212233
backgroundColor={euiTheme.colors.backgroundFilledText}
213234
/>
214235
}
215-
navigation={
216-
<Navigation
217-
activeItemId={props.activeItemId}
218-
isCollapsed={props.isCollapsed}
219-
items={props.items}
220-
logo={props.logo}
221-
setWidth={setNavigationWidth}
222-
/>
223-
}
236+
navigation={<ControlledNavigation {...props} setWidth={setNavigationWidth} />}
224237
sidebar={
225238
<Box
226239
label="Global Sidebar"
@@ -260,8 +273,3 @@ const Layout = ({ ...props }: PropsAndArgs) => {
260273
</>
261274
);
262275
};
263-
264-
export const WithinLayout: StoryObj<PropsAndArgs> = {
265-
name: 'Navigation within Layout',
266-
render: (args) => <Layout {...args} />,
267-
};

0 commit comments

Comments
 (0)