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

[Security Solution][Lens] New trigger actions for chart legends and table cell actions #146779

Merged
merged 31 commits into from
Dec 16, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4d41a54
register security lens cellValue actions
semd Nov 30, 2022
be16993
add cell actions to visualizations
semd Dec 1, 2022
5877254
add to timeline display name
semd Dec 1, 2022
11bc30e
rollback dashboards table test
semd Dec 1, 2022
37475e5
lens embeddable conflict solved
semd Dec 1, 2022
8f1e3be
fix types
semd Dec 1, 2022
0fee044
rollback LensRendererEvent type
semd Dec 1, 2022
39bed22
fix tests
semd Dec 1, 2022
6440f4f
unify security datagrid cellActions
semd Dec 2, 2022
34ebdf9
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Dec 2, 2022
0b9ba1a
security actions popover syles
semd Dec 2, 2022
9eab7ca
Merge branch '145708_viz_data_value_actions' of https://github.com/se…
semd Dec 2, 2022
28efa11
new tests added
semd Dec 2, 2022
91c14b8
Merge branch 'main' into 145708_viz_data_value_actions
kibanamachine Dec 5, 2022
2ca99c2
Merge branch 'main' into 145708_viz_data_value_actions
kibanamachine Dec 7, 2022
4c5d024
Merge branch 'main' into 145708_viz_data_value_actions
angorayc Dec 8, 2022
48ab3a9
Merge branch 'main' into 145708_viz_data_value_actions
angorayc Dec 8, 2022
020541f
Merge branch 'main' into 145708_viz_data_value_actions
angorayc Dec 9, 2022
80d36cc
remove comment
semd Dec 12, 2022
15334b2
Merge remote-tracking branch 'upstream/main' into 145708_viz_data_val…
semd Dec 12, 2022
41cca66
Merge branch '145708_viz_data_value_actions' of https://github.com/se…
semd Dec 12, 2022
f60c338
added interative conditionals + tests
semd Dec 14, 2022
dcff83d
tests
semd Dec 14, 2022
d2ba3d9
Merge remote-tracking branch 'upstream/main' into 145708_viz_data_val…
semd Dec 14, 2022
f481439
fix path change
semd Dec 14, 2022
585263c
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Dec 14, 2022
83aa17c
encapsulate layerCellValueActions fuction
semd Dec 15, 2022
f5ef5f6
Merge branch '145708_viz_data_value_actions' of https://github.com/se…
semd Dec 15, 2022
7843c3f
Merge remote-tracking branch 'upstream/main' into 145708_viz_data_val…
semd Dec 15, 2022
cd1bdbe
combine async calls
semd Dec 16, 2022
8432fb8
Merge remote-tracking branch 'upstream/main' into 145708_viz_data_val…
semd Dec 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ describe('PartitionVisComponent', function () {
syncColors: false,
fireEvent: jest.fn(),
renderComplete: jest.fn(),
interactive: true,
columnCellValueActions: [],
services: {
data: dataPluginMock.createStartContract(),
fieldFormats: fieldFormatsServiceMock.createStartContract(),
Expand Down Expand Up @@ -172,6 +174,16 @@ describe('PartitionVisComponent', function () {
});
});

it('should render legend actions when it is interactive', async () => {
const component = shallow(<PartitionVisComponent {...wrapperProps} interactive={true} />);
expect(component.find(Settings).prop('legendAction')).toBeDefined();
});

it('should not render legend actions when it is not interactive', async () => {
const component = shallow(<PartitionVisComponent {...wrapperProps} interactive={false} />);
expect(component.find(Settings).prop('legendAction')).toBeUndefined();
});

it('hides the legend if the legend toggle is clicked', async () => {
const component = mountWithIntl(<PartitionVisComponent {...wrapperProps} />);
await actWithTimeout(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import {
} from './partition_vis_component.styles';
import { ChartTypes } from '../../common/types';
import { filterOutConfig } from '../utils/filter_out_config';
import { FilterEvent, StartDeps } from '../types';
import { ColumnCellValueActions, FilterEvent, StartDeps } from '../types';

declare global {
interface Window {
Expand All @@ -85,19 +85,23 @@ export interface PartitionVisComponentProps {
uiState: PersistedState;
fireEvent: IInterpreterRenderHandlers['event'];
renderComplete: IInterpreterRenderHandlers['done'];
interactive: boolean;
chartsThemeService: ChartsPluginSetup['theme'];
palettesRegistry: PaletteRegistry;
services: Pick<StartDeps, 'data' | 'fieldFormats'>;
syncColors: boolean;
columnCellValueActions: ColumnCellValueActions;
}

const PartitionVisComponent = (props: PartitionVisComponentProps) => {
const {
columnCellValueActions,
visData: originalVisData,
visParams: preVisParams,
visType,
services,
syncColors,
interactive,
} = props;
const visParams = useMemo(() => filterOutConfig(visType, preVisParams), [preVisParams, visType]);
const chartTheme = props.chartsThemeService.useChartsTheme();
Expand Down Expand Up @@ -313,6 +317,32 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => {
]
);

const legendActions = useMemo(
() =>
interactive
? getLegendActions(
canFilter,
getLegendActionEventData(visData),
handleLegendAction,
columnCellValueActions,
visParams,
visData,
services.data.actions,
services.fieldFormats
)
: undefined,
[
columnCellValueActions,
getLegendActionEventData,
handleLegendAction,
interactive,
services.data.actions,
services.fieldFormats,
visData,
visParams,
]
);

const rescaleFactor = useMemo(() => {
const overallSum = visData.rows.reduce((sum, row) => sum + row[metricColumn.id], 0);
const slices = visData.rows.map((row) => row[metricColumn.id] / overallSum);
Expand Down Expand Up @@ -446,15 +476,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => {
splitChartFormatter
);
}}
legendAction={getLegendActions(
canFilter,
getLegendActionEventData(visData),
handleLegendAction,
visParams,
visData,
services.data.actions,
services.fieldFormats
)}
legendAction={legendActions}
theme={[
// Chart background should be transparent for the usage at Canvas.
{ background: { color: 'transparent' } },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { createMockPieParams, createMockVisData } from '../mocks';
import { CellValueAction } from '../types';
import { getColumnCellValueActions } from './partition_vis_renderer';

const visParams = createMockPieParams();
const visData = createMockVisData();

const cellValueAction: CellValueAction = {
displayName: 'Test',
id: 'test',
iconType: 'test-icon',
execute: () => {},
};

describe('getColumnCellValueActions', () => {
it('should get column cellValue actions for each params bucket', async () => {
const result = await getColumnCellValueActions(visParams, visData, async () => [
cellValueAction,
]);
expect(result).toHaveLength(visParams.dimensions.buckets?.length ?? 0);
});

it('should contain the cellValue actions', async () => {
const result = await getColumnCellValueActions(visParams, visData, async () => [
cellValueAction,
cellValueAction,
]);
expect(result[0]).toEqual([cellValueAction, cellValueAction]);
});

it('should return empty array if no buckets', async () => {
const result = await getColumnCellValueActions(
{ ...visParams, dimensions: { ...visParams.dimensions, buckets: undefined } },
visData,
async () => [cellValueAction]
);
expect(result).toEqual([]);
});

it('should return empty array if getCompatibleCellValueActions not passed', async () => {
const result = await getColumnCellValueActions(visParams, visData, undefined);
expect(result).toEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/public';
import { Datatable, ExpressionRenderDefinition } from '@kbn/expressions-plugin/public';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
import { VisTypePieDependencies } from '../plugin';
import { PARTITION_VIS_RENDERER_NAME } from '../../common/constants';
import { ChartTypes, RenderValue } from '../../common/types';
import { CellValueAction, GetCompatibleCellValueActions } from '../types';
import { ChartTypes, PartitionVisParams, RenderValue } from '../../common/types';
// eslint-disable-next-line @kbn/imports/no_boundary_crossing
import { extractContainerType, extractVisualizationType } from '../../../common';

Expand All @@ -42,6 +44,28 @@ const partitionVisRenderer = css({
height: '100%',
});

/**
* Retrieves the compatible CELL_VALUE_TRIGGER actions indexed by column
**/
export const getColumnCellValueActions = async (
visConfig: PartitionVisParams,
visData: Datatable,
getCompatibleCellValueActions: GetCompatibleCellValueActions | undefined
) => {
if (!Array.isArray(visConfig.dimensions.buckets) || !getCompatibleCellValueActions) {
return [];
}
return Promise.all(
visConfig.dimensions.buckets.reduce<Array<Promise<CellValueAction[]>>>((acc, accessor) => {
const column = getColumnByAccessor(accessor, visData.columns);
if (column) {
acc.push(getCompatibleCellValueActions([{ columnMeta: column.meta }]));
}
return acc;
}, [])
);
};

export const getPartitionVisRenderer: (
deps: VisTypePieDependencies
) => ExpressionRenderDefinition<RenderValue> = ({ getStartDeps }) => ({
Expand All @@ -60,6 +84,12 @@ export const getPartitionVisRenderer: (
unmountComponentAtNode(domNode);
});

const columnCellValueActions = await getColumnCellValueActions(
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we combine this with the palette async code below?

visConfig,
visData,
handlers.getCompatibleCellValueActions as GetCompatibleCellValueActions
);

const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
Expand Down Expand Up @@ -90,9 +120,11 @@ export const getPartitionVisRenderer: (
visType={visConfig.isDonut ? ChartTypes.DONUT : visType}
renderComplete={renderComplete}
fireEvent={handlers.event}
interactive={handlers.isInteractive()}
uiState={handlers.uiState as PersistedState}
services={{ data: plugins.data, fieldFormats: plugins.fieldFormats }}
syncColors={syncColors}
columnCellValueActions={columnCellValueActions}
/>
</div>
</KibanaThemeProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { ValueClickContext } from '@kbn/embeddable-plugin/public';
import type { CellValueContext, ValueClickContext } from '@kbn/embeddable-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import {
Plugin as ExpressionsPublicPlugin,
Expand Down Expand Up @@ -35,3 +35,16 @@ export interface FilterEvent {
name: 'filter';
data: ValueClickContext['data'];
}

export interface CellValueAction {
id: string;
iconType: string;
displayName: string;
execute: (data: CellValueContext['data']) => void;
}

export type ColumnCellValueActions = CellValueAction[][];

export type GetCompatibleCellValueActions = (
data: CellValueContext['data']
) => Promise<CellValueAction[]>;
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,7 @@ export const getFilterEventData = (
return acc;
}, []);
};

export const getSeriesValueColumnIndex = (value: string, visData: Datatable): number => {
return visData.columns.findIndex(({ id }) => !!visData.rows.find((r) => r[id] === value));
};
Loading