Skip to content

Commit

Permalink
[Embeddable Rebuild] Support grouping in ADD_PANEL_TRIGGER (#179872)
Browse files Browse the repository at this point in the history
## Summary

Resolves #179565

Adds support for the `grouping` property in actions assigned to the
`ADD_PANEL_TRIGGER` trigger.

In the "Add panel" context menu, groups from the embeddable factories
and `ADD_PANEL_TRIGGER` actions are merged, allowing us to group legacy
embeddables with the react ones.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
darnautov authored Apr 5, 2024
1 parent eb34ad2 commit bacad3a
Show file tree
Hide file tree
Showing 4 changed files with 376 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,40 @@ describe('getAddPanelActionMenuItems', () => {
isCompatible: () => Promise.resolve(true),
execute: jest.fn(),
},
{
id: 'TEST_ACTION_01',
type: 'TEST_ACTION_01',
getDisplayName: () => 'Action name 01',
getIconType: () => 'pencil',
getDisplayNameTooltip: () => 'Action tooltip',
isCompatible: () => Promise.resolve(true),
execute: jest.fn(),
grouping: [
{
id: 'groupedAddPanelAction',
getDisplayName: () => 'Custom group',
getIconType: () => 'logoElasticsearch',
},
],
},
{
id: 'TEST_ACTION_02',
type: 'TEST_ACTION_02',
getDisplayName: () => 'Action name',
getIconType: () => 'pencil',
getDisplayNameTooltip: () => 'Action tooltip',
isCompatible: () => Promise.resolve(true),
execute: jest.fn(),
grouping: [
{
id: 'groupedAddPanelAction',
getDisplayName: () => 'Custom group',
getIconType: () => 'logoElasticsearch',
},
],
},
];
const items = getAddPanelActionMenuItems(
const [items, grouped] = getAddPanelActionMenuItems(
getMockPresentationContainer(),
registeredActions,
jest.fn()
Expand All @@ -36,10 +68,38 @@ describe('getAddPanelActionMenuItems', () => {
toolTipContent: 'Action tooltip',
},
]);
expect(grouped).toStrictEqual({
groupedAddPanelAction: {
id: 'groupedAddPanelAction',
title: 'Custom group',
icon: 'logoElasticsearch',
items: [
{
'data-test-subj': 'create-action-Action name 01',
icon: 'pencil',
name: 'Action name 01',
onClick: expect.any(Function),
toolTipContent: 'Action tooltip',
},
{
'data-test-subj': 'create-action-Action name',
icon: 'pencil',
name: 'Action name',
onClick: expect.any(Function),
toolTipContent: 'Action tooltip',
},
],
},
});
});

it('returns empty array if no actions have been registered', async () => {
const items = getAddPanelActionMenuItems(getMockPresentationContainer(), [], jest.fn());
const [items, grouped] = getAddPanelActionMenuItems(
getMockPresentationContainer(),
[],
jest.fn()
);
expect(items).toStrictEqual([]);
expect(grouped).toStrictEqual({});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
*/
import type { ActionExecutionContext, Action } from '@kbn/ui-actions-plugin/public';
import { PresentationContainer } from '@kbn/presentation-containers';
import type {
EuiContextMenuPanelDescriptor,
EuiContextMenuPanelItemDescriptor,
} from '@elastic/eui';
import { addPanelMenuTrigger } from '../../triggers';

const onAddPanelActionClick =
Expand All @@ -26,25 +30,53 @@ const onAddPanelActionClick =
} else action.execute(context);
};

export type GroupedAddPanelActions = EuiContextMenuPanelDescriptor & {
icon?: string;
};

export const getAddPanelActionMenuItems = (
api: PresentationContainer,
actions: Array<Action<object>> | undefined,
closePopover: () => void
) => {
return (
actions?.map((item) => {
const context = {
embeddable: api,
trigger: addPanelMenuTrigger,
};
const actionName = item.getDisplayName(context);
return {
name: actionName,
icon: item.getIconType(context),
onClick: onAddPanelActionClick(item, context, closePopover),
'data-test-subj': `create-action-${actionName}`,
toolTipContent: item?.getDisplayNameTooltip?.(context),
};
}) ?? []
);
): [EuiContextMenuPanelItemDescriptor[], Record<string, GroupedAddPanelActions>] => {
const ungrouped: EuiContextMenuPanelItemDescriptor[] = [];
const grouped: Record<string, GroupedAddPanelActions> = {};

const context = {
embeddable: api,
trigger: addPanelMenuTrigger,
};

const getMenuItem = (item: Action<object>) => {
const actionName = item.getDisplayName(context);

return {
name: actionName,
icon: item.getIconType(context),
onClick: onAddPanelActionClick(item, context, closePopover),
'data-test-subj': `create-action-${actionName}`,
toolTipContent: item?.getDisplayNameTooltip?.(context),
};
};

actions?.forEach((item) => {
if (Array.isArray(item.grouping)) {
item.grouping.forEach((group) => {
if (!grouped[group.id]) {
grouped[group.id] = {
id: group.id,
icon: group.getIconType ? group.getIconType(context) : undefined,
title: group.getDisplayName ? group.getDisplayName(context) : undefined,
items: [],
};
}

grouped[group.id]!.items!.push(getMenuItem(item));
});
} else {
ungrouped.push(getMenuItem(item));
}
});

return [ungrouped, grouped];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EmbeddableFactory } from '@kbn/embeddable-plugin/public';
import { PresentationContainer } from '@kbn/presentation-containers';
import { GroupedAddPanelActions } from './add_panel_action_menu_items';
import {
FactoryGroup,
mergeGroupedItemsProvider,
getEmbeddableFactoryMenuItemProvider,
} from './editor_menu';

describe('mergeGroupedItemsProvider', () => {
const mockApi = { addNewPanel: jest.fn() } as unknown as jest.Mocked<PresentationContainer>;
const closePopoverSpy = jest.fn();

const getEmbeddableFactoryMenuItem = getEmbeddableFactoryMenuItemProvider(
mockApi,
closePopoverSpy
);

const mockFactory = {
id: 'factory1',
type: 'mockFactory',
getDisplayName: () => 'Factory 1',
getDescription: () => 'Factory 1 description',
getIconType: () => 'icon1',
} as unknown as EmbeddableFactory;

const factoryGroupMap = {
group1: {
panelId: 'panel1',
appName: 'App 1',
icon: 'icon1',
factories: [mockFactory],
},
} as unknown as Record<string, FactoryGroup>;

const groupedAddPanelAction = {
group1: {
id: 'panel2',
title: 'Panel 2',
icon: 'icon2',
items: [
{
id: 'addPanelActionId',
},
],
},
} as unknown as Record<string, GroupedAddPanelActions>;

it('should merge factoryGroupMap and groupedAddPanelAction correctly', () => {
const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider(
getEmbeddableFactoryMenuItem
)(factoryGroupMap, groupedAddPanelAction);

expect(initialPanelGroups).toEqual([
{
'data-test-subj': 'dashboardEditorMenu-group1Group',
name: 'App 1',
icon: 'icon1',
panel: 'panel1',
},
]);

expect(additionalPanels).toEqual([
{
id: 'panel1',
title: 'App 1',
items: [
{
icon: 'icon1',
name: 'Factory 1',
toolTipContent: 'Factory 1 description',
'data-test-subj': 'createNew-mockFactory',
onClick: expect.any(Function),
},
{
id: 'addPanelActionId',
},
],
},
]);
});

it('should handle missing factoryGroup correctly', () => {
const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider(
getEmbeddableFactoryMenuItem
)({}, groupedAddPanelAction);

expect(initialPanelGroups).toEqual([
{
'data-test-subj': 'dashboardEditorMenu-group1Group',
name: 'Panel 2',
icon: 'icon2',
panel: 'panel2',
},
]);

expect(additionalPanels).toEqual([
{
id: 'panel2',
title: 'Panel 2',
items: [
{
id: 'addPanelActionId',
},
],
},
]);
});

it('should handle missing groupedAddPanelAction correctly', () => {
const [initialPanelGroups, additionalPanels] = mergeGroupedItemsProvider(
getEmbeddableFactoryMenuItem
)(factoryGroupMap, {});

expect(initialPanelGroups).toEqual([
{
'data-test-subj': 'dashboardEditorMenu-group1Group',
name: 'App 1',
icon: 'icon1',
panel: 'panel1',
},
]);

expect(additionalPanels).toEqual([
{
id: 'panel1',
title: 'App 1',
items: [
{
icon: 'icon1',
name: 'Factory 1',
toolTipContent: 'Factory 1 description',
'data-test-subj': 'createNew-mockFactory',
onClick: expect.any(Function),
},
],
},
]);
});
});
Loading

0 comments on commit bacad3a

Please sign in to comment.