[Lens] Add one click filter to Lens table (#139701)

* add one click filter to Lens table

* fix tests
This commit is contained in:
Joe Reuter 2022-09-05 16:14:08 +02:00 committed by GitHub
parent 5e0615f628
commit c72e6ee59e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 8 deletions

View file

@ -30,6 +30,7 @@ export interface ColumnState {
columnId: string;
width?: number;
hidden?: boolean;
oneClickFilter?: boolean;
isTransposed?: boolean;
// These flags are necessary to transpose columns and map them back later
// They are set automatically and are not user-editable
@ -63,6 +64,7 @@ export const datatableColumn: ExpressionFunctionDefinition<
alignment: { types: ['string'], help: '' },
sortingHint: { types: ['string'], help: '' },
hidden: { types: ['boolean'], help: '' },
oneClickFilter: { types: ['boolean'], help: '' },
width: { types: ['number'], help: '' },
isTransposed: { types: ['boolean'], help: '' },
transposable: { types: ['boolean'], help: '' },

View file

@ -13,6 +13,7 @@ exports[`DatatableComponent it renders actions column when there are row actions
"c": "right",
},
"getColorForValue": [MockFunction],
"handleFilterClick": [Function],
"minMaxByColumnId": Object {
"c": Object {
"max": 3,
@ -291,6 +292,7 @@ exports[`DatatableComponent it renders custom row height if set to another value
"c": "right",
},
"getColorForValue": [MockFunction],
"handleFilterClick": [Function],
"minMaxByColumnId": Object {
"c": Object {
"max": 3,
@ -558,6 +560,7 @@ exports[`DatatableComponent it renders the title and value 1`] = `
"c": "right",
},
"getColorForValue": [MockFunction],
"handleFilterClick": [Function],
"minMaxByColumnId": Object {
"c": Object {
"max": 3,
@ -823,6 +826,7 @@ exports[`DatatableComponent it should render hide, reset, and sort actions on he
"c": "right",
},
"getColorForValue": [MockFunction],
"handleFilterClick": [Function],
"minMaxByColumnId": Object {
"c": Object {
"max": 3,

View file

@ -17,6 +17,7 @@ import { ReactWrapper } from 'enzyme';
import { DatatableArgs, ColumnConfigArg } from '../../../../common/expressions';
import { DataContextType } from './types';
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import { EuiLink } from '@elastic/eui';
describe('datatable cell renderer', () => {
const table: Datatable = {
@ -146,6 +147,50 @@ describe('datatable cell renderer', () => {
expect(cell.find('.lnsTableCell--multiline').exists()).toBeTruthy();
});
it('renders as EuiLink if oneClickFilter is set', () => {
const MultiLineCellRenderer = createGridCell(
{
a: { convert: (x) => `formatted ${x}` } as FieldFormat,
},
{
columns: [
{
columnId: 'a',
type: 'lens_datatable_column',
oneClickFilter: true,
},
],
sortingColumnId: '',
sortingDirection: 'none',
},
DataContext,
{ get: jest.fn() } as unknown as IUiSettingsClient,
true
);
const cell = mountWithIntl(
<DataContext.Provider
value={{
table,
alignments: {
a: 'right',
},
handleFilterClick: () => {},
}}
>
<MultiLineCellRenderer
rowIndex={0}
colIndex={0}
columnId="a"
setCellProps={() => {}}
isExpandable={false}
isDetails={false}
isExpanded={false}
/>
</DataContext.Provider>
);
expect(cell.find(EuiLink).text()).toEqual('formatted 123');
});
describe('dynamic coloring', () => {
const paletteRegistry = chartPluginMock.createPaletteRegistry();
const customPalette = paletteRegistry.get('custom');

View file

@ -6,7 +6,7 @@
*/
import React, { useContext, useEffect } from 'react';
import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
import { EuiDataGridCellValueElementProps, EuiLink } from '@elastic/eui';
import type { IUiSettingsClient } from '@kbn/core/public';
import classNames from 'classnames';
import type { FormatFactory } from '../../../../common';
@ -25,13 +25,16 @@ export const createGridCell = (
// Changing theme requires a full reload of the page, so we can cache here
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
return ({ rowIndex, columnId, setCellProps }: EuiDataGridCellValueElementProps) => {
const { table, alignments, minMaxByColumnId, getColorForValue } = useContext(DataContext);
const { table, alignments, minMaxByColumnId, getColorForValue, handleFilterClick } =
useContext(DataContext);
const rowValue = table?.rows[rowIndex]?.[columnId];
const content = formatters[columnId]?.convert(rowValue, 'html');
const currentAlignment = alignments && alignments[columnId];
const { colorMode, palette } =
columnConfig.columns.find(({ columnId: id }) => id === columnId) || {};
const colIndex = columnConfig.columns.findIndex(({ columnId: id }) => id === columnId);
const { colorMode, palette, oneClickFilter } = columnConfig.columns[colIndex] || {};
const filterOnClick = oneClickFilter && handleFilterClick;
const content = formatters[columnId]?.convert(rowValue, filterOnClick ? 'text' : 'html');
const currentAlignment = alignments && alignments[columnId];
useEffect(() => {
const originalId = getOriginalId(columnId);
@ -68,6 +71,25 @@ export const createGridCell = (
};
}, [rowValue, columnId, setCellProps, colorMode, palette, minMaxByColumnId, getColorForValue]);
if (filterOnClick) {
return (
<div
data-test-subj="lnsTableCellContent"
className={classNames({
'lnsTableCell--multiline': fitRowToContent,
[`lnsTableCell--${currentAlignment}`]: true,
})}
>
<EuiLink
onClick={() => {
handleFilterClick?.(columnId, rowValue, colIndex, rowIndex);
}}
>
{content}
</EuiLink>
</div>
);
}
return (
<div
/*

View file

@ -76,8 +76,10 @@ export const createGridColumns = (
const filterable = bucketLookup.has(field);
const { name, index: colIndex } = columnsReverseLookup[field];
const columnArgs = columnConfig.columns.find(({ columnId }) => columnId === field);
const cellActions =
filterable && handleFilterClick
filterable && handleFilterClick && !columnArgs?.oneClickFilter
? [
({ rowIndex, columnId, Component }: EuiDataGridColumnCellActionProps) => {
const { rowValue, contentsIsDefined, cellContent } = getContentData({
@ -158,7 +160,6 @@ export const createGridColumns = (
]
: undefined;
const columnArgs = columnConfig.columns.find(({ columnId }) => columnId === field);
const isTransposed = Boolean(columnArgs?.originalColumnId);
const initialWidth = columnArgs?.width;
const isHidden = columnArgs?.hidden;

View file

@ -318,6 +318,42 @@ export function TableDimensionEditor(
/>
</EuiFormRow>
)}
{props.groupId === 'rows' && (
<EuiFormRow
fullWidth
label={i18n.translate('xpack.lens.table.columnFilterClickLabel', {
defaultMessage: 'Directly filter on click',
})}
display="columnCompressedSwitch"
>
<EuiSwitch
compressed
label={i18n.translate('xpack.lens.table.columnFilterClickLabel', {
defaultMessage: 'Directly filter on click',
})}
showLabel={false}
data-test-subj="lns-table-column-one-click-filter"
checked={Boolean(column?.oneClickFilter)}
disabled={column.hidden}
onChange={() => {
const newState = {
...state,
columns: state.columns.map((currentColumn) => {
if (currentColumn.columnId === accessor) {
return {
...currentColumn,
oneClickFilter: !column.oneClickFilter,
};
} else {
return currentColumn;
}
}),
};
setState(newState);
}}
/>
</EuiFormRow>
)}
</>
);
}

View file

@ -452,6 +452,7 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
alignments,
minMaxByColumnId,
getColorForValue: props.paletteService.get(CUSTOM_PALETTE).getColorForValue!,
handleFilterClick,
}}
>
<EuiDataGrid

View file

@ -65,6 +65,13 @@ export interface DataContextType {
rowHasRowClickTriggerActions?: boolean[];
alignments?: Record<string, 'left' | 'right' | 'center'>;
minMaxByColumnId?: Record<string, { min: number; max: number }>;
handleFilterClick?: (
field: string,
value: unknown,
colIndex: number,
rowIndex: number,
negate?: boolean
) => void;
getColorForValue?: (
value: number | undefined,
state: CustomPaletteState,

View file

@ -449,6 +449,10 @@ export const getDatatableVisualization = ({
arguments: {
columnId: [column.columnId],
hidden: typeof column.hidden === 'undefined' ? [] : [column.hidden],
oneClickFilter:
typeof column.oneClickFilter === 'undefined'
? []
: [column.oneClickFilter],
width: typeof column.width === 'undefined' ? [] : [column.width],
isTransposed:
typeof column.isTransposed === 'undefined' ? [] : [column.isTransposed],