Skip to content

Commit

Permalink
Context menu (#76497)
Browse files Browse the repository at this point in the history
* feat: 🎸 add grouping to presentable interface

* feat: 🎸 add group to "Explore underlying" data action

* refactor: 💡 return panel list and simplify context creation

* refactor: 💡 simplify context menu builder code

* refactor: 💡 further simplify context menu builder code

* feat: 🎸 add grouping to context menu builder

* feat: 🎸 add icon to drilldowns group

* fix: 🐛 sort in the other order

* feat: 🎸 group drilldown actions in edit mode

* fix: 🐛 fix TypeScript error

* feat: 🎸 wrap long context menu list into a submenu

* feat: 🎸 improve context menu long list wrapping

* feat: 🎸 display drilldowns panel at the bottom of main panel

* feat: 🎸 add separator line for context menu

* test: 💍 add basic context menu builder unit tests

* feat: 🎸 remove meta decoratiosn from generated menu

* test: 💍 add test subject attribute to "More" menu item

* chore: 🤖 remove separator line and add comment about EUI

* test: 💍 update Jest snapshots

* chore: 🤖 revert back change of showing both drilldown options

* test: 💍 add context menu samples to example plugin

* feat: 🎸 collapse long groups into a sub-panel

* test: 💍 add context menu panel edit mode examples

* test: 💍 fix OSS functional test

* test: 💍 fix X-Pack functional tests

* fix: 🐛 re-introduce item sorting by title

* test: 💍 allow explicitly opening more menu

* test: 💍 try opening more panel in functional tests

* test: 💍 disable some tests

* chore: 🤖 remove unused code

* test: 💍 use action test helper in unit tests

* refactor: 💡 add helper utility to generate actions in examples

* test: 💍 disable one more functional test

* test: 💍 improve how inspector is opened in functional tests

* test: 💍 enable functional test

* refactor: 💡 convert test suite to typescript

* test: 💍 move panel replace tests into a separate test suite

* test: 💍 move panel cloning tests to a separate test suite

* test: 💍 set up dashboard context menu test suite

* test: 💍 enable few panel context menu tests

* test: 💍 enable saved search panel tests

* test: 💍 enable expanded panel context menu tests

* test: 💍 remove render complete awaits

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
streamich and elasticmachine authored Sep 21, 2020
1 parent 337fe73 commit 4b49e5a
Show file tree
Hide file tree
Showing 31 changed files with 1,379 additions and 458 deletions.
6 changes: 6 additions & 0 deletions examples/ui_actions_explorer/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { UiActionsStart, createAction } from '../../../src/plugins/ui_actions/pu
import { AppMountParameters, OverlayStart } from '../../../src/core/public';
import { HELLO_WORLD_TRIGGER_ID, ACTION_HELLO_WORLD } from '../../ui_action_examples/public';
import { TriggerContextExample } from './trigger_context_example';
import { ContextMenuExamples } from './context_menu_examples';

interface Props {
uiActionsApi: UiActionsStart;
Expand Down Expand Up @@ -109,7 +110,12 @@ const ActionsExplorer = ({ uiActionsApi, openModal }: Props) => {
</EuiText>

<EuiSpacer />

<TriggerContextExample uiActionsApi={uiActionsApi} />

<EuiSpacer />

<ContextMenuExamples />
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { EuiCode, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { PanelView } from './panel_view';
import { PanelViewWithSharing } from './panel_view_with_sharing';
import { PanelViewWithSharingLong } from './panel_view_with_sharing_long';
import { PanelEdit } from './panel_edit';
import { PanelEditWithDrilldowns } from './panel_edit_with_drilldowns';
import { PanelEditWithDrilldownsAndContextActions } from './panel_edit_with_drilldowns_and_context_actions';

export const ContextMenuExamples: React.FC = () => {
return (
<EuiText>
<h1>Context menu examples</h1>
<p>
Below examples show how context menu panels look with varying number of actions and how the
actions can be grouped into different panels using <EuiCode>grouping</EuiCode> field.
</p>

<EuiFlexGroup>
<EuiFlexItem>
<PanelView />
</EuiFlexItem>
<EuiFlexItem>
<PanelViewWithSharing />
</EuiFlexItem>
<EuiFlexItem>
<PanelViewWithSharingLong />
</EuiFlexItem>
</EuiFlexGroup>

<EuiFlexGroup>
<EuiFlexItem>
<PanelEdit />
</EuiFlexItem>
<EuiFlexItem>
<PanelEditWithDrilldowns />
</EuiFlexItem>
<EuiFlexItem>
<PanelEditWithDrilldownsAndContextActions />
</EuiFlexItem>
</EuiFlexGroup>
</EuiText>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export * from './context_menu_examples';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as React from 'react';
import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
import useAsync from 'react-use/lib/useAsync';
import { buildContextMenuForActions } from '../../../../src/plugins/ui_actions/public';
import { sampleAction } from './util';

export const PanelEdit: React.FC = () => {
const [open, setOpen] = React.useState(false);

const context = {};
const trigger: any = 'TEST_TRIGGER';
const actions = [
sampleAction('test-1', 100, 'Edit visualization', 'pencil'),
sampleAction('test-2', 99, 'Clone panel', 'partial'),
sampleAction('test-3', 98, 'Edit panel title', 'pencil'),
sampleAction('test-4', 97, 'Customize time range', 'calendar'),
sampleAction('test-5', 96, 'Inspect', 'inspect'),
sampleAction('test-6', 95, 'Full screen', 'fullScreen'),
sampleAction('test-7', 94, 'Replace panel', 'submodule'),
sampleAction('test-8', 93, 'Delete from dashboard', 'trash'),
];

const panels = useAsync(() =>
buildContextMenuForActions({
actions: actions.map((action) => ({ action, context, trigger })),
})
);

return (
<EuiPopover
button={<EuiButton onClick={() => setOpen((x) => !x)}>Edit mode</EuiButton>}
isOpen={open}
panelPaddingSize="none"
anchorPosition="downLeft"
closePopover={() => setOpen(false)}
>
<EuiContextMenu initialPanelId={'mainMenu'} panels={panels.value} />
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as React from 'react';
import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
import useAsync from 'react-use/lib/useAsync';
import { buildContextMenuForActions, Action } from '../../../../src/plugins/ui_actions/public';
import { sampleAction } from './util';

export const PanelEditWithDrilldowns: React.FC = () => {
const [open, setOpen] = React.useState(false);

const context = {};
const trigger: any = 'TEST_TRIGGER';
const grouping: Action['grouping'] = [
{
id: 'drilldowns',
getDisplayName: () => 'Drilldowns',
getIconType: () => 'popout',
order: 20,
},
];
const actions = [
sampleAction('test-1', 100, 'Edit visualization', 'pencil'),
sampleAction('test-2', 99, 'Clone panel', 'partial'),
sampleAction('test-3', 98, 'Edit panel title', 'pencil'),
sampleAction('test-4', 97, 'Customize time range', 'calendar'),
sampleAction('test-5', 96, 'Inspect', 'inspect'),
sampleAction('test-6', 95, 'Full screen', 'fullScreen'),
sampleAction('test-7', 94, 'Replace panel', 'submodule'),
sampleAction('test-8', 93, 'Delete from dashboard', 'trash'),

sampleAction('test-9', 10, 'Create drilldown', 'plusInCircle', grouping),
sampleAction('test-10', 9, 'Manage drilldowns', 'list', grouping),
];

const panels = useAsync(() =>
buildContextMenuForActions({
actions: actions.map((action) => ({ action, context, trigger })),
})
);

return (
<EuiPopover
button={<EuiButton onClick={() => setOpen((x) => !x)}>Edit mode with drilldowns</EuiButton>}
isOpen={open}
panelPaddingSize="none"
anchorPosition="downLeft"
closePopover={() => setOpen(false)}
>
<EuiContextMenu initialPanelId={'mainMenu'} panels={panels.value} />
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as React from 'react';
import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
import useAsync from 'react-use/lib/useAsync';
import { buildContextMenuForActions, Action } from '../../../../src/plugins/ui_actions/public';
import { sampleAction } from './util';

export const PanelEditWithDrilldownsAndContextActions: React.FC = () => {
const [open, setOpen] = React.useState(false);

const context = {};
const trigger: any = 'TEST_TRIGGER';
const drilldownGrouping: Action['grouping'] = [
{
id: 'drilldowns',
getDisplayName: () => 'Drilldowns',
getIconType: () => 'popout',
order: 20,
},
];
const customActionGrouping: Action['grouping'] = [
{
id: 'actions',
getDisplayName: () => 'Custom actions',
getIconType: () => 'cloudStormy',
order: 20,
},
];
const actions = [
sampleAction('test-1', 100, 'Edit visualization', 'pencil'),
sampleAction('test-2', 99, 'Clone panel', 'partial'),
sampleAction('test-3', 98, 'Edit panel title', 'pencil'),
sampleAction('test-4', 97, 'Customize time range', 'calendar'),
sampleAction('test-5', 96, 'Inspect', 'inspect'),
sampleAction('test-6', 95, 'Full screen', 'fullScreen'),
sampleAction('test-7', 94, 'Replace panel', 'submodule'),
sampleAction('test-8', 93, 'Delete from dashboard', 'trash'),

sampleAction('test-9', 10, 'Create drilldown', 'plusInCircle', drilldownGrouping),
sampleAction('test-10', 9, 'Manage drilldowns', 'list', drilldownGrouping),

sampleAction('test-11', 10, 'Go to Sales dashboard', 'dashboardApp', customActionGrouping),
sampleAction('test-12', 9, 'Go to Traffic dashboard', 'dashboardApp', customActionGrouping),
sampleAction('test-13', 8, 'Custom actions', 'cloudStormy', customActionGrouping),
sampleAction('test-14', 7, 'View in Salesforce', 'link', customActionGrouping),
];

const panels = useAsync(() =>
buildContextMenuForActions({
actions: actions.map((action) => ({ action, context, trigger })),
})
);

return (
<EuiPopover
button={
<EuiButton onClick={() => setOpen((x) => !x)}>
Edit mode with drilldowns and custom actions
</EuiButton>
}
isOpen={open}
panelPaddingSize="none"
anchorPosition="downLeft"
closePopover={() => setOpen(false)}
>
<EuiContextMenu initialPanelId={'mainMenu'} panels={panels.value} />
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import * as React from 'react';
import { EuiButton, EuiContextMenu, EuiPopover } from '@elastic/eui';
import useAsync from 'react-use/lib/useAsync';
import { buildContextMenuForActions } from '../../../../src/plugins/ui_actions/public';
import { sampleAction } from './util';

export const PanelView: React.FC = () => {
const [open, setOpen] = React.useState(false);

const context = {};
const trigger: any = 'TEST_TRIGGER';
const actions = [
sampleAction('test-1', 100, 'Explore underlying data', 'discoverApp'),
sampleAction('test-2', 99, 'Customize time range', 'calendar'),
sampleAction('test-3', 98, 'Inspect', 'inspect'),
sampleAction('test-4', 97, 'Full screen', 'fullScreen'),
];

const panels = useAsync(() =>
buildContextMenuForActions({
actions: actions.map((action) => ({ action, context, trigger })),
})
);

return (
<EuiPopover
button={<EuiButton onClick={() => setOpen((x) => !x)}>View mode</EuiButton>}
isOpen={open}
panelPaddingSize="none"
anchorPosition="downLeft"
closePopover={() => setOpen(false)}
>
<EuiContextMenu initialPanelId={'mainMenu'} panels={panels.value} />
</EuiPopover>
);
};
Loading

0 comments on commit 4b49e5a

Please sign in to comment.