-
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] [CellActions] Move to a package (#149057)
Epic: #144943 ## Summary Moving the existing CellActions implementation to a new home. The `kbn-cell-actions` package contains components and hooks that are going to be used by solutions to show data cell actions with a consistent UI across them. Security Solution is going to start using it by migrating all "hover-actions" to the unified implementation, but the usage is not restricted to it. Any plugin can register and attach its own actions to a trigger via uiActions, and use this package to render the CellActions components in a consistent way. The initial implementation was placed in the uiActions plugin itself due to a types constraints (https://github.com/elastic/kibana/tree/main/src/plugins/ui_actions/public/cell_actions), the constraint has been solved so we are creating the package for it as planned. This PR only moves that implementation to the new package, with small directory changes. The exported components are not being used anywhere currently, so the implementation may change during the migration phase. ### Checklist Delete any items that are not applicable to this PR. - [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] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
- Loading branch information
1 parent
fabfb43
commit bca73b7
Showing
42 changed files
with
1,998 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
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,9 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
module.exports = require('@kbn/storybook').defaultConfig; |
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,15 @@ | ||
This package provides a uniform interface for displaying UI actions for a cell. | ||
For the `CellActions` component to work, it must be wrapped by `CellActionsProvider`. Ideally, the wrapper should stay on the top of the rendering tree. | ||
|
||
Example: | ||
|
||
```JSX | ||
<CellActionsProvider getTriggerCompatibleActions={uiActions.getTriggerCompatibleActions}> | ||
[...] | ||
<CellActions mode={CellActionsMode.HOVER} triggerId={MY_TRIGGER_ID} config={{ field: 'fieldName', value: 'fieldValue', fieldType: 'text' }}> | ||
Hover me | ||
</CellActions> | ||
</CellActionsProvider> | ||
``` | ||
|
||
`CellActions` component will display all compatible actions registered for the trigger id. |
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,9 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export * from './src'; |
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,13 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
module.exports = { | ||
preset: '@kbn/test', | ||
rootDir: '../..', | ||
roots: ['<rootDir>/packages/kbn-cell-actions'], | ||
}; |
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,5 @@ | ||
{ | ||
"type": "shared-common", | ||
"id": "@kbn/cell-actions", | ||
"owner": "@elastic/security-threat-hunting-explore" | ||
} |
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,7 @@ | ||
{ | ||
"name": "@kbn/cell-actions", | ||
"version": "1.0.0", | ||
"description": "Uniform components for displaying UI actions in data cells", | ||
"license": "SSPL-1.0 OR Elastic License 2.0", | ||
"private": true | ||
} |
78 changes: 78 additions & 0 deletions
78
packages/kbn-cell-actions/src/__stories__/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,78 @@ | ||
/* | ||
* 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 { CellActionsProvider } from '../context/cell_actions_context'; | ||
import { makeAction } from '../mocks/helpers'; | ||
import { CellActions } from '../components/cell_actions'; | ||
import { CellActionsMode, type CellActionsProps } from '../types'; | ||
|
||
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) => ( | ||
<CellActionsProvider | ||
// call uiActions getTriggerCompatibleActions(triggerId, data) | ||
getTriggerCompatibleActions={getCompatibleActions} | ||
> | ||
<div style={{ paddingTop: '70px' }} /> | ||
{storyFn()} | ||
</CellActionsProvider> | ||
), | ||
], | ||
}; | ||
|
||
const CellActionsTemplate: ComponentStory<React.FC<CellActionsProps>> = (args) => ( | ||
<CellActions {...args}>Field value</CellActions> | ||
); | ||
|
||
export const DefaultWithControls = CellActionsTemplate.bind({}); | ||
|
||
DefaultWithControls.argTypes = { | ||
mode: { | ||
options: [CellActionsMode.HOVER, CellActionsMode.INLINE], | ||
defaultValue: CellActionsMode.HOVER, | ||
control: { | ||
type: 'radio', | ||
}, | ||
}, | ||
}; | ||
|
||
DefaultWithControls.args = { | ||
showActionTooltips: true, | ||
mode: CellActionsMode.INLINE, | ||
triggerId: TRIGGER_ID, | ||
field: FIELD, | ||
visibleCellActions: 3, | ||
}; | ||
|
||
export const CellActionInline = ({}: {}) => ( | ||
<CellActions mode={CellActionsMode.INLINE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
); | ||
|
||
export const CellActionHoverPopup = ({}: {}) => ( | ||
<CellActions mode={CellActionsMode.HOVER} triggerId={TRIGGER_ID} field={FIELD}> | ||
Hover me | ||
</CellActions> | ||
); |
34 changes: 34 additions & 0 deletions
34
packages/kbn-cell-actions/src/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 '../types'; | ||
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(); | ||
}); | ||
}); |
44 changes: 44 additions & 0 deletions
44
packages/kbn-cell-actions/src/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,44 @@ | ||
/* | ||
* 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 { CellAction, CellActionExecutionContext } from '../types'; | ||
|
||
export const ActionItem = ({ | ||
action, | ||
actionContext, | ||
showTooltip, | ||
}: { | ||
action: CellAction; | ||
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" /> | ||
); | ||
}; |
75 changes: 75 additions & 0 deletions
75
packages/kbn-cell-actions/src/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,75 @@ | ||
/* | ||
* 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 } from './cell_actions'; | ||
import { CellActionsMode } from '../types'; | ||
import { CellActionsProvider } from '../context/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( | ||
<CellActionsProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.INLINE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('cellActions')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders InlineActions when mode is INLINE', async () => { | ||
const getActionsPromise = Promise.resolve([]); | ||
const getActions = () => getActionsPromise; | ||
|
||
const { queryByTestId } = render( | ||
<CellActionsProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.INLINE} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('inlineActions')).toBeInTheDocument(); | ||
}); | ||
|
||
it('renders HoverActionsPopover when mode is HOVER', async () => { | ||
const getActionsPromise = Promise.resolve([]); | ||
const getActions = () => getActionsPromise; | ||
|
||
const { queryByTestId } = render( | ||
<CellActionsProvider getTriggerCompatibleActions={getActions}> | ||
<CellActions mode={CellActionsMode.HOVER} triggerId={TRIGGER_ID} field={FIELD}> | ||
Field value | ||
</CellActions> | ||
</CellActionsProvider> | ||
); | ||
|
||
await act(async () => { | ||
await getActionsPromise; | ||
}); | ||
|
||
expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); | ||
}); | ||
}); |
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,64 @@ | ||
/* | ||
* 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, useRef } from 'react'; | ||
import { InlineActions } from './inline_actions'; | ||
import { HoverActionsPopover } from './hover_actions_popover'; | ||
import { CellActionsMode, type CellActionsProps, type CellActionExecutionContext } from '../types'; | ||
|
||
export const CellActions: React.FC<CellActionsProps> = ({ | ||
field, | ||
triggerId, | ||
children, | ||
mode, | ||
showActionTooltips = true, | ||
visibleCellActions = 3, | ||
metadata, | ||
}) => { | ||
const extraContentNodeRef = useRef<HTMLDivElement | null>(null); | ||
const nodeRef = useRef<HTMLDivElement | null>(null); | ||
|
||
const actionContext: CellActionExecutionContext = useMemo( | ||
() => ({ | ||
field, | ||
trigger: { id: triggerId }, | ||
extraContentNodeRef, | ||
nodeRef, | ||
metadata, | ||
}), | ||
[field, triggerId, metadata] | ||
); | ||
|
||
if (mode === CellActionsMode.HOVER) { | ||
return ( | ||
<div ref={nodeRef} data-test-subj={'cellActions'}> | ||
<HoverActionsPopover | ||
actionContext={actionContext} | ||
showActionTooltips={showActionTooltips} | ||
visibleCellActions={visibleCellActions} | ||
> | ||
{children} | ||
</HoverActionsPopover> | ||
|
||
<div ref={extraContentNodeRef} /> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<div ref={nodeRef} data-test-subj={'cellActions'}> | ||
{children} | ||
<InlineActions | ||
actionContext={actionContext} | ||
showActionTooltips={showActionTooltips} | ||
visibleCellActions={visibleCellActions} | ||
/> | ||
<div ref={extraContentNodeRef} /> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.