Skip to content

Commit b1ee2f5

Browse files
[Chrome Next] AppMenu changes for DnD (#259949)
## Summary This PR updates app menu to match the first milestone of the new design from [Project Vibranium (Chrome Next)](elastic/kibana-team#3115). ### Changes: - removal of `secondaryActionItem` from `AppMenuConfig` interface - the number of items visible at once has been reduced to `3`: - `primaryActionItem` is now treated separately and doesn't contribute to the limit - if the number of `items` is less or equal than `3` then all the items are visible - if the number of `items` is more than `3` then the overflow button will appear **consuming one of the available slots**, resulting in maximum of `2` items being visible alongside the overflow button The changes affect both classic and solution views. The order of the items has been changed to match design decisions. ### Visuals: | App | Before | After | |--------|------------|------------| | Dashboards view mode | <img width="596" height="141" alt="Screenshot 2026-03-30 at 23 43 49" src="https://github.com/user-attachments/assets/1b75e843-d088-47f1-b221-f61e8d721655" /> | <img width="353" height="210" alt="Screenshot 2026-03-30 at 23 51 35" src="https://github.com/user-attachments/assets/2bd09d71-4d99-49d8-b2f7-5b21b09e03e2" /> | | Dashboards edit mode | <img width="592" height="122" alt="Screenshot 2026-03-30 at 23 43 42" src="https://github.com/user-attachments/assets/62fecc1c-09cc-4bd5-b1a3-c33a198e3dcb" /> | <img width="421" height="208" alt="Screenshot 2026-03-30 at 23 52 25" src="https://github.com/user-attachments/assets/bba87abe-141c-4206-a7e3-79b5e827275e" /> | | Discover KQL mode | <img width="540" height="250" alt="Screenshot 2026-03-30 at 23 45 12" src="https://github.com/user-attachments/assets/4497ccb9-4856-4278-ae16-5c9cd0e85dd1" /> | <img width="360" height="328" alt="Screenshot 2026-03-30 at 23 53 01" src="https://github.com/user-attachments/assets/638c6a23-52b9-41ef-971f-e0ad75997a53" /> | | Discover ES\|QL mode | <img width="530" height="208" alt="Screenshot 2026-03-30 at 23 45 26" src="https://github.com/user-attachments/assets/fa7a97b9-1d2a-42d3-a26b-cff154d7dd1d" /> | <img width="355" height="287" alt="Screenshot 2026-03-30 at 23 53 38" src="https://github.com/user-attachments/assets/ce9a9dd4-ef43-4c12-a2ef-7c164d244fa0" /> | Implements: #259991
1 parent 4d8f631 commit b1ee2f5

56 files changed

Lines changed: 500 additions & 671 deletions

File tree

Some content is hidden

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

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,7 @@ x-pack/platform/test/plugin_api_integration/test_suites/platform/ @elastic/kiban
21922192
/src/platform/test/node_roles_functional @elastic/kibana-core
21932193
/src/platform/test/functional/page_objects/newsfeed_page.ts @elastic/kibana-core # assigned per https://github.com/elastic/kibana/pull/160210
21942194
/src/platform/test/functional/page_objects/home_page.ts @elastic/appex-sharedux
2195+
/src/platform/test/functional/page_objects/app_menu.ts @elastic/appex-sharedux
21952196
/src/platform/test/functional/page_objects/export_page.ts @elastic/appex-sharedux
21962197
/src/platform/test/functional/page_objects/solution_navigation.ts @elastic/appex-sharedux
21972198
/src/platform/test/functional/fixtures/es_archiver/deprecations_service @elastic/kibana-core

src/core/packages/chrome/app-menu/core-chrome-app-menu-components/app_menu.stories.tsx

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,86 @@ type Story = StoryObj<AppMenuWrapperProps>;
5454

5555
const dashboardEditModeConfig: AppMenuConfig = {
5656
items: [
57+
{
58+
id: 'add',
59+
order: 0,
60+
label: 'add',
61+
testId: 'addButton',
62+
iconType: 'plus',
63+
popoverWidth: 200,
64+
items: [
65+
{
66+
run: () => action('create-visualization-clicked'),
67+
order: 1,
68+
id: 'createVisualization',
69+
label: 'Visualization',
70+
iconType: 'lensApp',
71+
testId: 'createNewVisButton',
72+
},
73+
{
74+
run: () => action('new-panel-clicked'),
75+
id: 'newPanel',
76+
order: 2,
77+
label: 'New panel',
78+
iconType: 'plusCircle',
79+
testId: 'openAddPanelFlyoutButton',
80+
},
81+
{
82+
run: () => action('add-section-clicked'),
83+
id: 'collapsibleSection',
84+
order: 3,
85+
label: 'Collapsible section',
86+
iconType: 'section',
87+
testId: 'addCollapsibleSectionButton',
88+
},
89+
{
90+
id: 'controls',
91+
order: 4,
92+
label: 'Controls',
93+
iconType: 'controls',
94+
testId: 'controls-menu-button',
95+
items: [
96+
{
97+
run: () => action('control-clicked'),
98+
id: 'control',
99+
order: 1,
100+
label: 'control',
101+
testId: 'controlButton',
102+
},
103+
{
104+
run: () => action('variable-control-clicked'),
105+
id: 'variableControl',
106+
order: 2,
107+
label: 'variable control',
108+
testId: 'variableControlButton',
109+
},
110+
{
111+
run: () => action('time-slider-control-clicked'),
112+
id: 'timeSliderControl',
113+
order: 3,
114+
label: 'time slider control',
115+
testId: 'timeSliderControlButton',
116+
},
117+
{
118+
run: () => action('setting-clicked'),
119+
id: 'settings',
120+
order: 4,
121+
label: 'settings',
122+
testId: 'settingButton',
123+
separator: 'above',
124+
},
125+
],
126+
},
127+
{
128+
run: () => action('add-from-library-clicked'),
129+
id: 'fromLibrary',
130+
order: 5,
131+
label: 'From library',
132+
iconType: 'folderOpen',
133+
testId: 'addFromLibraryButton',
134+
},
135+
],
136+
},
57137
{
58138
run: action('exit-edit-clicked'),
59139
id: 'exitEdit',
@@ -121,87 +201,6 @@ const dashboardEditModeConfig: AppMenuConfig = {
121201
iconType: 'backgroundTask',
122202
},
123203
],
124-
secondaryActionItem: {
125-
id: 'add',
126-
label: 'add',
127-
testId: 'addButton',
128-
iconType: 'plusCircle',
129-
color: 'success',
130-
minWidth: false,
131-
popoverWidth: 200,
132-
items: [
133-
{
134-
run: () => action('create-visualization-clicked'),
135-
order: 1,
136-
id: 'createVisualization',
137-
label: 'Visualization',
138-
iconType: 'lensApp',
139-
testId: 'createNewVisButton',
140-
},
141-
{
142-
run: () => action('new-panel-clicked'),
143-
id: 'newPanel',
144-
order: 2,
145-
label: 'New panel',
146-
iconType: 'plusCircle',
147-
testId: 'openAddPanelFlyoutButton',
148-
},
149-
{
150-
run: () => action('add-section-clicked'),
151-
id: 'collapsibleSection',
152-
order: 3,
153-
label: 'Collapsible section',
154-
iconType: 'section',
155-
testId: 'addCollapsibleSectionButton',
156-
},
157-
{
158-
id: 'controls',
159-
order: 4,
160-
label: 'Controls',
161-
iconType: 'controls',
162-
testId: 'controls-menu-button',
163-
items: [
164-
{
165-
run: () => action('control-clicked'),
166-
id: 'control',
167-
order: 1,
168-
label: 'control',
169-
testId: 'controlButton',
170-
},
171-
{
172-
run: () => action('variable-control-clicked'),
173-
id: 'variableControl',
174-
order: 2,
175-
label: 'variable control',
176-
testId: 'variableControlButton',
177-
},
178-
{
179-
run: () => action('time-slider-control-clicked'),
180-
id: 'timeSliderControl',
181-
order: 3,
182-
label: 'time slider control',
183-
testId: 'timeSliderControlButton',
184-
},
185-
{
186-
run: () => action('setting-clicked'),
187-
id: 'settings',
188-
order: 4,
189-
label: 'settings',
190-
testId: 'settingButton',
191-
separator: 'above',
192-
},
193-
],
194-
},
195-
{
196-
run: () => action('add-from-library-clicked'),
197-
id: 'fromLibrary',
198-
order: 5,
199-
label: 'From library',
200-
iconType: 'folderOpen',
201-
testId: 'addFromLibraryButton',
202-
},
203-
],
204-
},
205204
primaryActionItem: {
206205
run: action('save-clicked'),
207206
id: 'save',

src/core/packages/chrome/app-menu/core-chrome-app-menu-components/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ export type {
1919
AppMenuRunActionParams,
2020
AppMenuConfig,
2121
AppMenuItemType,
22-
AppMenuSecondaryActionItem,
2322
AppMenuPrimaryActionItem,
2423
AppMenuPopoverItem,
2524
AppMenuSplitButtonProps,

src/core/packages/chrome/app-menu/core-chrome-app-menu-components/src/components/app_menu.test.tsx

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -89,43 +89,6 @@ describe('AppMenu', () => {
8989

9090
expect(screen.getByText('Save')).toBeInTheDocument();
9191
});
92-
93-
it('should render secondary action item', () => {
94-
const configWithSecondary: AppMenuConfig = {
95-
secondaryActionItem: {
96-
id: 'cancel',
97-
label: 'Cancel',
98-
run: jest.fn(),
99-
iconType: 'cross',
100-
},
101-
};
102-
103-
render(<AppMenuComponent config={configWithSecondary} />);
104-
105-
expect(screen.getByText('Cancel')).toBeInTheDocument();
106-
});
107-
108-
it('should render both primary and secondary action items', () => {
109-
const configWithBoth: AppMenuConfig = {
110-
primaryActionItem: {
111-
id: 'save',
112-
label: 'Save',
113-
run: jest.fn(),
114-
iconType: 'save',
115-
},
116-
secondaryActionItem: {
117-
id: 'cancel',
118-
label: 'Cancel',
119-
run: jest.fn(),
120-
iconType: 'cross',
121-
},
122-
};
123-
124-
render(<AppMenuComponent config={configWithBoth} />);
125-
126-
expect(screen.getByText('Save')).toBeInTheDocument();
127-
expect(screen.getByText('Cancel')).toBeInTheDocument();
128-
});
12992
});
13093

13194
describe('responsive behavior', () => {

src/core/packages/chrome/app-menu/core-chrome-app-menu-components/src/components/app_menu.tsx

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ export interface AppMenuItemsProps {
2626
isCollapsed?: boolean;
2727
}
2828

29-
const hasNoItems = (config: AppMenuConfig) =>
30-
!config.items?.length && !config?.primaryActionItem && !config?.secondaryActionItem;
29+
const hasNoItems = (config: AppMenuConfig) => !config.items?.length && !config?.primaryActionItem;
3130

3231
export const AppMenuComponent = ({
3332
config,
@@ -43,7 +42,6 @@ export const AppMenuComponent = ({
4342
}
4443

4544
const primaryActionItem = config?.primaryActionItem;
46-
const secondaryActionItem = config?.secondaryActionItem;
4745
const showMoreButtonId = 'show-more';
4846

4947
const headerLinksProps = {
@@ -76,22 +74,10 @@ export const AppMenuComponent = ({
7674
/>
7775
) : undefined;
7876

79-
const secondaryActionComponent = secondaryActionItem ? (
80-
<AppMenuActionButton
81-
{...secondaryActionItem}
82-
isPopoverOpen={openPopoverId === secondaryActionItem.id}
83-
onPopoverToggle={() => {
84-
handlePopoverToggle(secondaryActionItem.id);
85-
}}
86-
onPopoverClose={handleOnPopoverClose}
87-
/>
88-
) : undefined;
89-
9077
const collapsedComponent = (
9178
<AppMenuOverflowButton
9279
items={[...displayedItems, ...overflowItems]}
9380
isPopoverOpen={openPopoverId === showMoreButtonId}
94-
secondaryActionItem={secondaryActionItem}
9581
primaryActionItem={primaryActionItem}
9682
onPopoverToggle={() => handlePopoverToggle(showMoreButtonId)}
9783
onPopoverClose={handleOnPopoverClose}
@@ -111,7 +97,6 @@ export const AppMenuComponent = ({
11197
onPopoverToggle={() => handlePopoverToggle(showMoreButtonId)}
11298
onPopoverClose={handleOnPopoverClose}
11399
/>
114-
{secondaryActionComponent}
115100
{primaryActionComponent}
116101
</EuiHeaderLinks>
117102
);
@@ -138,7 +123,6 @@ export const AppMenuComponent = ({
138123
onPopoverClose={handleOnPopoverClose}
139124
/>
140125
)}
141-
{secondaryActionComponent}
142126
{primaryActionComponent}
143127
</EuiHeaderLinks>
144128
);

src/core/packages/chrome/app-menu/core-chrome-app-menu-components/src/components/app_menu_action_button.test.tsx

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -110,28 +110,6 @@ describe('AppMenuActionButton', () => {
110110
expect(defaultProps.run).not.toHaveBeenCalled();
111111
});
112112

113-
it('should toggle popover when button with items is clicked', async () => {
114-
const user = userEvent.setup();
115-
const secondaryActionProps = {
116-
label: 'options',
117-
id: 'optionsButton',
118-
iconType: 'gear',
119-
isPopoverOpen: false,
120-
onPopoverToggle: jest.fn(),
121-
onPopoverClose: jest.fn(),
122-
items: [
123-
{ id: 'item1', label: 'Item 1', run: jest.fn(), order: 1 },
124-
{ id: 'item2', label: 'Item 2', run: jest.fn(), order: 2 },
125-
],
126-
};
127-
128-
render(<AppMenuActionButton {...secondaryActionProps} testId="test-action-button" />);
129-
130-
await user.click(screen.getByTestId('test-action-button'));
131-
132-
expect(secondaryActionProps.onPopoverToggle).toHaveBeenCalledTimes(1);
133-
});
134-
135113
it('should toggle popover when split button with items is clicked', async () => {
136114
const user = userEvent.setup();
137115
const splitButtonPropsWithItems = {

0 commit comments

Comments
 (0)