-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solution] Add CellActions (alpha version) component to ui_a…
…ctions plugin (#147434) ## Summary Create a `CellActions` component. It hooks into a UI-Actions trigger and displays all available actions. It has two modes, Hover_Actions and Always_Visible. You can run the storybook and take a look at the component: `yarn storybook ui_actions` or access https://ci-artifacts.kibana.dev/storybooks/pr-147434/226993c612bbe1719de6374219009bc69b0378d8/ui_actions/index.html *** This component is still not in use. <img width="117" alt="Screenshot 2022-12-13 at 13 13 46" src="https://user-images.githubusercontent.com/1490444/207316029-26c7bad8-ae39-48ba-8059-cbacf01a98aa.png"> <img width="224" alt="Screenshot 2022-12-13 at 13 13 30" src="https://user-images.githubusercontent.com/1490444/207316024-0d7706c8-bd59-42e8-bf6d-b5648fc818fd.png"> #### Why? The security Solution team is creating a generic UI component to allow teams to share actions between different plugins. Initially, only the Security solution plugin will use this component and deprecate the Security solution custom implementation. Some actions that will be shared are: "copy to clipboard", "filter in", "filter out" and "add to timeline". #### How to use it: This package provides a uniform interface for displaying UI actions for a cell. For the `CellActions` component to work, it must be wrapped by `CellActionsContextProvider`. Ideally, the wrapper should stay on the top of the rendering tree. Example: ```JSX <CellActionsContextProvider // call uiActions.getTriggerCompatibleActions(triggerId, data) getCompatibleActions={getCompatibleActions}> ... <CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={MY_TRIGGER_ID} config={{ field: 'fieldName', value: 'fieldValue', fieldType: 'text' }}> Hover me </CellActions> </CellActionsContextProvider> ``` `CellActions` component will display all compatible actions registered for the trigger id. ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
- Loading branch information
Showing
25 changed files
with
1,593 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
const defaultConfig = require('@kbn/storybook').defaultConfig; | ||
|
||
module.exports = { | ||
...defaultConfig, | ||
stories: ['../**/*.stories.tsx'], | ||
reactOptions: { | ||
strictMode: true, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
This package provides a uniform interface for displaying UI actions for a cell. | ||
For the `CellActions` component to work, it must be wrapped by `CellActionsContextProvider`. Ideally, the wrapper should stay on the top of the rendering tree. | ||
|
||
Example: | ||
```JSX | ||
<CellActionsContextProvider | ||
// call uiActions.getTriggerCompatibleActions(triggerId, data) | ||
getCompatibleActions={getCompatibleActions}> | ||
... | ||
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={MY_TRIGGER_ID} config={{ field: 'fieldName', value: 'fieldValue', fieldType: 'text' }}> | ||
Hover me | ||
</CellActions> | ||
</CellActionsContextProvider> | ||
|
||
``` | ||
|
||
`CellActions` component will display all compatible actions registered for the trigger id. |
34 changes: 34 additions & 0 deletions
34
src/plugins/ui_actions/public/cell_actions/components/cell_action_item.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* 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 { render } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { makeAction } from '../mocks/helpers'; | ||
import { CellActionExecutionContext } from './cell_actions'; | ||
import { ActionItem } from './cell_action_item'; | ||
|
||
describe('ActionItem', () => { | ||
it('renders', () => { | ||
const action = makeAction('test-action'); | ||
const actionContext = {} as CellActionExecutionContext; | ||
const { queryByTestId } = render( | ||
<ActionItem action={action} actionContext={actionContext} showTooltip={false} /> | ||
); | ||
expect(queryByTestId('actionItem-test-action')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders tooltip when showTooltip=true is received', () => { | ||
const action = makeAction('test-action'); | ||
const actionContext = {} as CellActionExecutionContext; | ||
const { container } = render( | ||
<ActionItem action={action} actionContext={actionContext} showTooltip /> | ||
); | ||
|
||
expect(container.querySelector('.euiToolTipAnchor')).not.toBeNull(); | ||
}); | ||
}); |
45 changes: 45 additions & 0 deletions
45
src/plugins/ui_actions/public/cell_actions/components/cell_action_item.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* 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 React, { useMemo } from 'react'; | ||
|
||
import { EuiButtonIcon, EuiToolTip, IconType } from '@elastic/eui'; | ||
import type { Action } from '../../actions'; | ||
import { CellActionExecutionContext } from './cell_actions'; | ||
|
||
export const ActionItem = ({ | ||
action, | ||
actionContext, | ||
showTooltip, | ||
}: { | ||
action: Action; | ||
actionContext: CellActionExecutionContext; | ||
showTooltip: boolean; | ||
}) => { | ||
const actionProps = useMemo( | ||
() => ({ | ||
iconType: action.getIconType(actionContext) as IconType, | ||
onClick: () => action.execute(actionContext), | ||
'data-test-subj': `actionItem-${action.id}`, | ||
'aria-label': action.getDisplayName(actionContext), | ||
}), | ||
[action, actionContext] | ||
); | ||
|
||
if (!actionProps.iconType) return null; | ||
|
||
return showTooltip ? ( | ||
<EuiToolTip | ||
content={action.getDisplayNameTooltip ? action.getDisplayNameTooltip(actionContext) : ''} | ||
> | ||
<EuiButtonIcon {...actionProps} iconSize="s" /> | ||
</EuiToolTip> | ||
) : ( | ||
<EuiButtonIcon {...actionProps} iconSize="s" /> | ||
); | ||
}; |
77 changes: 77 additions & 0 deletions
77
src/plugins/ui_actions/public/cell_actions/components/cell_actions.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import { ComponentStory } from '@storybook/react'; | ||
import { CellActionsContextProvider } from './cell_actions_context'; | ||
import { makeAction } from '../mocks/helpers'; | ||
import { CellActions, CellActionsMode, CellActionsProps } from './cell_actions'; | ||
|
||
const TRIGGER_ID = 'testTriggerId'; | ||
|
||
const FIELD = { name: 'name', value: '123', type: 'text' }; | ||
|
||
const getCompatibleActions = () => | ||
Promise.resolve([ | ||
makeAction('Filter in', 'plusInCircle', 2), | ||
makeAction('Filter out', 'minusInCircle', 3), | ||
makeAction('Minimize', 'minimize', 1), | ||
makeAction('Send email', 'email', 4), | ||
makeAction('Pin field', 'pin', 5), | ||
]); | ||
|
||
export default { | ||
title: 'CellAction', | ||
decorators: [ | ||
(storyFn: Function) => ( | ||
<CellActionsContextProvider | ||
// call uiActions getTriggerCompatibleActions(triggerId, data) | ||
getTriggerCompatibleActions={getCompatibleActions} | ||
> | ||
<div style={{ paddingTop: '70px' }} /> | ||
{storyFn()} | ||
</CellActionsContextProvider> | ||
), | ||
], | ||
}; | ||
|
||
const CellActionsTemplate: ComponentStory<React.FC<CellActionsProps>> = (args) => ( | ||
<CellActions {...args}>Field value</CellActions> | ||
); | ||
|
||
export const DefaultWithControls = CellActionsTemplate.bind({}); | ||
|
||
DefaultWithControls.argTypes = { | ||
mode: { | ||
options: [CellActionsMode.HOVER_POPOVER, CellActionsMode.ALWAYS_VISIBLE], | ||
defaultValue: CellActionsMode.HOVER_POPOVER, | ||
control: { | ||
type: 'radio', | ||
}, | ||
}, | ||
}; | ||
|
||
DefaultWithControls.args = { | ||
showActionTooltips: true, | ||
mode: CellActionsMode.ALWAYS_VISIBLE, | ||
triggerId: TRIGGER_ID, | ||
field: FIELD, | ||
visibleCellActions: 3, | ||
}; | ||
|
||
export const CellActionInline = ({}: {}) => ( | ||
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
); | ||
|
||
export const CellActionHoverPopup = ({}: {}) => ( | ||
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={TRIGGER_ID} field={FIELD}> | ||
Hover me | ||
</CellActions> | ||
); |
74 changes: 74 additions & 0 deletions
74
src/plugins/ui_actions/public/cell_actions/components/cell_actions.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* 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 { act, render } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { CellActions, CellActionsMode } from './cell_actions'; | ||
import { CellActionsContextProvider } from './cell_actions_context'; | ||
|
||
const TRIGGER_ID = 'test-trigger-id'; | ||
const FIELD = { name: 'name', value: '123', type: 'text' }; | ||
|
||
describe('CellActions', () => { | ||
it('renders', async () => { | ||
const getActionsPromise = Promise.resolve([]); | ||
const getActions = () => getActionsPromise; | ||
|
||
const { queryByTestId } = render( | ||
<CellActionsContextProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsContextProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('cellActions')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders InlineActions when mode is ALWAYS_VISIBLE', async () => { | ||
const getActionsPromise = Promise.resolve([]); | ||
const getActions = () => getActionsPromise; | ||
|
||
const { queryByTestId } = render( | ||
<CellActionsContextProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.ALWAYS_VISIBLE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsContextProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('inlineActions')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders HoverActionsPopover when mode is HOVER_POPOVER', async () => { | ||
const getActionsPromise = Promise.resolve([]); | ||
const getActions = () => getActionsPromise; | ||
|
||
const { queryByTestId } = render( | ||
<CellActionsContextProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.HOVER_POPOVER} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsContextProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); | ||
}); | ||
}); |
Oops, something went wrong.