This PR implements cell actions in the `TGrid`, rendering them via `EuiDataGrid`, per the screenshots below:
### Before
Users previously hovered over a draggable field to view and trigger cell actions, as illustrated by the `Before` screenshots below:
<img width="1348" alt="legacy_cell_actions" src="https://user-images.githubusercontent.com/4459398/128351498-49b4d224-6c51-4293-b14f-46bbb58f7cb3.png">
_Above: legacy `TGrid` cell action rendering_
### After
Cell actions are now rendered via `EuiDataGrid` cell actions:
<img width="997" alt="euidatagrid_cell_actions" src="https://user-images.githubusercontent.com/4459398/128358847-c5540ea4-8ba1-4b35-ab6b-3b3e39ae54ce.png">
_Above: new `TGrid` cell action rendering via `EuiDataGrid`_
## Technical Details
Every instance of the `TGrid` on a page can specify its own set of cell actions via `defaultCellActions` when calling the `timelines.getTGrid()` function create an instance.
For example, the Observability Alerts `TGrid` is initialized in with a default set of actions in `x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx`, as shown in the code below:
```ts
{timelines.getTGrid<'standalone'>({
type: 'standalone',
columns,
deletedEventIds: [],
defaultCellActions: getDefaultCellActions({ enableFilterActions: false }), // <-- defaultCellActions
// ...
</>
```
The type of the `defaultCellActions` is:
```ts
defaultCellActions?: TGridCellAction[];
```
and the definition of `TGridCellAction` is in `x-pack/plugins/timelines/common/types/timeline/columns/index.tsx`:
```ts
/**
* A `TGridCellAction` function accepts `data`, where each row of data is
* represented as a `TimelineNonEcsData[]`. For example, `data[0]` would
* contain a `TimelineNonEcsData[]` with the first row of data.
*
* A `TGridCellAction` returns a function that has access to all the
* `EuiDataGridColumnCellActionProps`, _plus_ access to `data`,
* which enables code like the following example to be written:
*
* Example:
* ```
* ({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => {
* const value = getMappedNonEcsValue({
* data: data[rowIndex], // access a specific row's values
* fieldName: columnId,
* });
*
* return (
* <Component onClick={() => alert(`row ${rowIndex} col ${columnId} has value ${value}`)} iconType="heart">
* {'Love it'}
* </Component>
* );
* };
* ```
*/
export type TGridCellAction = ({
browserFields,
data,
}: {
browserFields: BrowserFields;
/** each row of data is represented as one TimelineNonEcsData[] */
data: TimelineNonEcsData[][];
}) => (props: EuiDataGridColumnCellActionProps) => ReactNode;
```
For example, the following `TGridCellAction[]` defines the `Copy to clipboard` action for the Observability Alerts table in `x-pack/plugins/observability/public/pages/alerts/default_cell_actions.tsx`:
```ts
/** actions common to all cells (e.g. copy to clipboard) */
const commonCellActions: TGridCellAction[] = [
({ data }: { data: TimelineNonEcsData[][] }) => ({ rowIndex, columnId, Component }) => {
const { timelines } = useKibanaServices();
const value = getMappedNonEcsValue({
data: data[rowIndex],
fieldName: columnId,
});
return (
<>
{timelines.getHoverActions().getCopyButton({
Component,
field: columnId,
isHoverAction: false,
ownFocus: false,
showTooltip: false,
value,
})}
</>
);
},
];
```
Note that an _implementation_ of the copy action, including the button, is available for both the Observability and Security solutions to use via `timelines.getHoverActions().getCopyButton()`, (and both solutions use it in this PR), but there's no requirement to use that specific implementation of the copy action.
### Security Solution cell actions
All previously-available hover actions in the Security Solution are now available as cell actions, i.e.:
- Filter for value
- Filter out value
- Add to timeline investigation
- Show Top `<field>` (only enabled for some data types)
- Copy to clipboard
### Observability cell actions
In this PR:
- Only the `Copy to clipboard` cell action is enabled by default in the Observability Alerts table
- The `Filter for value` and `Filter out value` cell actions may be enabled in the `Observability` solution by changing a single line of code, (setting `enableFilterActions` to true), on the following line in `x-pack/plugins/observability/public/pages/alerts/alerts_table_t_grid.tsx`:
```js
defaultCellActions: getDefaultCellActions({ enableFilterActions: false }), // <-- set this to `true` to enable the filter actions
```
`enableFilterActions` is set to `false` in this PR because the Observability Alerts page's search bar, defined in `x-pack/plugins/observability/public/pages/alerts/alerts_search_bar.tsx`:
```ts
return (
<SearchBar
indexPatterns={dynamicIndexPattern}
placeholder={i18n.translate('xpack.observability.alerts.searchBarPlaceholder', {
defaultMessage: 'kibana.alert.evaluation.threshold > 75',
})}
query={{ query: query ?? '', language: queryLanguage }}
// ...
/>
````
must be integrated with a `filterManager` to display the filters. A `filterManager` instance may be obtained in the Observability solution via the following boilerplate:
```ts
const {
services: {
data: {
query: { filterManager },
},
},
} = useKibana<ObservabilityPublicPluginsStart>();
```
## Desk testing
To desk test this PR, you must enable feature flags in the Observability and Security Solution:
- To desk test the `Observability > Alerts` page, add the following settings to `config/kibana.dev.yml`:
```
xpack.observability.unsafe.cases.enabled: true
xpack.observability.unsafe.alertingExperience.enabled: true
xpack.ruleRegistry.write.enabled: true
```
- To desk test the TGrid in the following Security Solution, edit `x-pack/plugins/security_solution/common/experimental_features.ts` and in the `allowedExperimentalValues` section set:
```typescript
tGridEnabled: true,
```
cc @mdefazio