Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RAC] [TGrid] Implements cell actions in the TGrid #107771

Merged
merged 1 commit into from Aug 5, 2021

Conversation

andrew-goldstein
Copy link
Contributor

@andrew-goldstein andrew-goldstein commented Aug 5, 2021

Summary

This PR implements cell actions in the TGrid, rendering them via EuiDataGrid, per the Before and After screenshots below:

Before

Users previously hovered over a draggable field to view and trigger cell actions:

legacy_cell_actions

Above: legacy TGrid cell action rendering

After

Cell actions are now rendered via EuiDataGrid cell actions:

euidatagrid_cell_actions

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 to 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:

      {timelines.getTGrid<'standalone'>({
        type: 'standalone',
        columns,
        deletedEventIds: [],
        defaultCellActions: getDefaultCellActions({ enableFilterActions: false }), // <-- defaultCellActions
        // ...
    </>

The type of the defaultCellActions is:

defaultCellActions?: TGridCellAction[];

and the definition of TGridCellAction is in x-pack/plugins/timelines/common/types/timeline/columns/index.tsx:

/**
 * 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:

/** 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 to clipboard cell 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:
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:

  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:

  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:
tGridEnabled: true,

cc @mdefazio

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
@andrew-goldstein andrew-goldstein added v8.0.0 release_note:skip Skip the PR/issue when compiling release notes Team:Threat Hunting Security Solution Threat Hunting Team auto-backport Deprecated: Automatically backport this PR after it's merged Theme: rac label obsolete v7.15.0 labels Aug 5, 2021
@andrew-goldstein andrew-goldstein requested review from a team as code owners August 5, 2021 15:57
@andrew-goldstein andrew-goldstein self-assigned this Aug 5, 2021
@elasticmachine
Copy link
Contributor

Pinging @elastic/security-threat-hunting (Team:Threat Hunting)

const onFilterAdded = () => {};

/** a hook to eliminate the verbose boilerplate required to use common services */
const useKibanaServices = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏾

*
* return (
* <Component onClick={() => alert(`row ${rowIndex} col ${columnId} has value ${value}`)} iconType="heart">
* {'Love it'}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍟🍔

import { TimelineNonEcsData } from '../../../search_strategy/timeline';

export type ColumnHeaderType = 'not-filtered' | 'text-filter';

/** Uniquely identifies a column */
export type ColumnId = string;

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the awesome comment here and documenting the behavior!

@@ -42,13 +51,34 @@ const CopyButton: React.FC<CopyProps> = React.memo(
}
}
}, [closePopOver, keyboardEvent, ownFocus]);
return (

const text = useMemo(() => `${field}${value != null ? `: "${value}"` : ''}`, [field, value]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup

import { TimelinesUIStart } from '../../../../timelines/public';

/** a noop required by the filter in / out buttons */
const onFilterAdded = () => {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just make this optional in the component. But that can be done in a follow up PR

import { useKibana } from '../kibana';

/** a noop required by the filter in / out buttons */
const onFilterAdded = () => {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting here too: We can just make this optional in the filter component. But that can be done in a follow up PR

@kibanamachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Module Count

Fewer modules leads to a faster build time

id before after diff
observability 260 261 +1
securitySolution 2356 2357 +1
total +2

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
observability 508.0KB 510.0KB +2.0KB
securitySolution 6.5MB 6.5MB +11.0KB
timelines 266.2KB 272.0KB +5.8KB
total +18.8KB
Unknown metric groups

API count

id before after diff
timelines 925 927 +2

API count missing comments

id before after diff
timelines 806 807 +1

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @andrew-goldstein

Copy link
Contributor

@michaelolo24 michaelolo24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Tested locally and all the actions are working as expected. Things to update in a follow up PR:

  • Make the onFilterAdded optional in the actions components
  • Fix the ellipse not showing up in the dataGrid cell when actions show. (running hypothesis is the action tooltips may be causing it)

@andrew-goldstein andrew-goldstein merged commit 5f409bc into elastic:master Aug 5, 2021
@andrew-goldstein andrew-goldstein deleted the tgrid-actions branch August 5, 2021 18:46
kibanamachine pushed a commit to kibanamachine/kibana that referenced this pull request Aug 5, 2021
## Summary

This PR implements cell actions in the `TGrid`, rendering them via `EuiDataGrid`, per the `Before` and `After` screenshots below:

### Before

Users previously hovered over a draggable field to view and trigger cell actions:

<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 to 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 to clipboard cell 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
@kibanamachine
Copy link
Contributor

💚 Backport successful

Status Branch Result
7.x

This backport PR will be merged automatically after passing CI.

kibanamachine added a commit that referenced this pull request Aug 5, 2021
## Summary

This PR implements cell actions in the `TGrid`, rendering them via `EuiDataGrid`, per the `Before` and `After` screenshots below:

### Before

Users previously hovered over a draggable field to view and trigger cell actions:

<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 to 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 to clipboard cell 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

Co-authored-by: Andrew Goldstein <andrew-goldstein@users.noreply.github.com>
streamich pushed a commit to vadimkibana/kibana that referenced this pull request Aug 8, 2021
## Summary

This PR implements cell actions in the `TGrid`, rendering them via `EuiDataGrid`, per the `Before` and `After` screenshots below:

### Before

Users previously hovered over a draggable field to view and trigger cell actions:

<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 to 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 to clipboard cell 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
jloleysens added a commit to jloleysens/kibana that referenced this pull request Aug 9, 2021
…-png-pdf-report-type

* 'master' of github.com:elastic/kibana: (392 commits)
  update linting doc (elastic#105748)
  [APM] Various improvements from elastic#104851 (elastic#107726)
  Update dependency @elastic/charts to v33.2.0 (master) (elastic#107842)
  Fix default route link on kibana homepage (elastic#107809)
  [APM] Invalidate trackPageview on route change (elastic#107741)
  Service map backend links (elastic#107317)
  [index patterns] index pattern create modal (elastic#101853)
  [RAC] integrating rbac search strategy with alert table (elastic#107242)
  [Security Solution] Siem signals -> alerts as data field and index aliases (elastic#106049)
  [Metrics UI] Add checkbox to optionally drop partial buckets (elastic#107676)
  [Metrics UI] Fix metric threshold preview regression (elastic#107674)
  Disable Product check in @elastic/elasticsearch-js (elastic#107642)
  [App Search] Migrate Crawler Status Indicator, Crawler Status Banner, and Crawl Request polling (elastic#107603)
  [Security Solution, Lists] Replace legacy imports from 'elasticsearch' package (elastic#107226)
  [maps] asset tracking tutorial (elastic#104552)
  [scripts/build_ts_refs] when using `--clean` initialize caches (elastic#107777)
  Upgrade EUI to v36.1.0 (elastic#107231)
  [RAC] [TGrid] Implements cell actions in the TGrid (elastic#107771)
  Realign cypress/ccs_integration with cypress/integration (elastic#107743)
  Allow optional OSS to X-Pack dependencies (elastic#107432)
  ...

# Conflicts:
#	x-pack/examples/reporting_example/public/application.tsx
#	x-pack/examples/reporting_example/public/components/app.tsx
#	x-pack/plugins/canvas/public/services/legacy/stubs/reporting.ts
#	x-pack/plugins/reporting/common/types.ts
#	x-pack/plugins/reporting/public/lib/reporting_api_client/context.tsx
#	x-pack/plugins/reporting/public/management/mount_management_section.tsx
#	x-pack/plugins/reporting/public/management/report_listing.test.tsx
#	x-pack/plugins/reporting/public/plugin.ts
#	x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx
#	x-pack/plugins/reporting/server/export_types/printable_pdf/execute_job/index.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport Deprecated: Automatically backport this PR after it's merged release_note:skip Skip the PR/issue when compiling release notes Team:Threat Hunting Security Solution Threat Hunting Team Theme: rac label obsolete v7.15.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants