From 2010f764b124d2808d6a4d043cd50a006aae8a6e Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 26 Nov 2020 18:37:29 +0100 Subject: [PATCH 01/14] :sparkles: New sorting feature for the datatable visualization --- .../datatable_visualization/expression.tsx | 203 ++++++++++++------ .../datatable_visualization/visualization.tsx | 6 + .../workspace_panel/workspace_panel.tsx | 16 +- x-pack/plugins/lens/public/types.ts | 17 +- 4 files changed, 169 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 6502e07697816..ed55d50304c12 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -10,11 +10,21 @@ import React, { useMemo } from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; -import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { + EuiBasicTable, + EuiFlexGroup, + EuiButtonIcon, + EuiFlexItem, + EuiToolTip, + Direction, +} from '@elastic/eui'; +import { orderBy } from 'lodash'; import { IAggType } from 'src/plugins/data/public'; +import { DatatableColumnMeta } from 'src/plugins/expressions'; import { FormatFactory, ILensInterpreterRenderHandlers, + LensEditEvent, LensFilterEvent, LensMultiTable, } from '../types'; @@ -29,6 +39,8 @@ import { LensIconChartDatatable } from '../assets/chart_datatable'; export interface DatatableColumns { columnIds: string[]; + sortBy: string; + sortDirection: string; } interface Args { @@ -45,6 +57,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; onClickValue: (data: LensFilterEvent['data']) => void; + onEditAction?: (data: LensEditEvent['data']) => void; getType: (name: string) => IAggType; }; @@ -108,6 +121,8 @@ export const datatableColumns: ExpressionFunctionDefinition< help: '', inputTypes: ['null'], args: { + sortBy: { types: ['string'], help: '' }, + sortDirection: { types: ['string'], help: '' }, columnIds: { types: ['string'], multi: true, @@ -143,12 +158,19 @@ export const getDatatableRenderer = (dependencies: { const onClickValue = (data: LensFilterEvent['data']) => { handlers.event({ name: 'filter', data }); }; + + const onEditAction = (data: LensEditEvent['data']) => { + if (handlers.getRenderMode() === 'edit') { + handlers.event({ name: 'edit', data }); + } + }; ReactDOM.render( , @@ -161,6 +183,12 @@ export const getDatatableRenderer = (dependencies: { }, }); +function getNextOrderValue(currentValue: LensEditEvent['data']['direction']) { + const states: Array = ['asc', 'desc', 'none']; + const newStateIndex = (1 + states.findIndex((state) => state === currentValue)) % states.length; + return states[newStateIndex]; +} + export function DatatableComponent(props: DatatableRenderProps) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; @@ -169,7 +197,7 @@ export function DatatableComponent(props: DatatableRenderProps) { formatters[column.id] = props.formatFactory(column.meta?.params); }); - const { onClickValue } = props; + const { onClickValue, onEditAction } = props; const handleFilterClick = useMemo( () => (field: string, value: unknown, colIndex: number, negate: boolean = false) => { const col = firstTable.columns[colIndex]; @@ -214,6 +242,23 @@ export function DatatableComponent(props: DatatableRenderProps) { return ; } + const visibleColumns = props.args.columns.columnIds.filter((field) => !!field); + const columnsReverseLookup = firstTable.columns.reduce< + Record + >((memo, { id, name, meta }, i) => { + memo[id] = { name, index: i, meta }; + return memo; + }, {}); + + const { sortBy, sortDirection } = props.args.columns; + + const rows = firstTable?.rows || []; + let sortedRows = rows; + + if (sortBy && sortDirection !== 'none') { + sortedRows = orderBy(rows, [sortBy], sortDirection as Direction); + } + return ( { - const col = firstTable.columns.find((c) => c.id === field); - const filterable = bucketColumns.includes(field); - const colIndex = firstTable.columns.findIndex((c) => c.id === field); - return { - field, - name: (col && col.name) || '', - render: (value: unknown) => { - const formattedValue = formatters[field]?.convert(value); - const fieldName = col?.meta?.field; + sorting={{ + sort: + !sortBy || sortDirection === 'none' + ? undefined + : { + field: sortBy, + direction: sortDirection as Direction, + }, + allowNeutralSort: true, // this flag enables the 3rd Neutral state on the column header + }} + onChange={(event: { sort?: { field: string } }) => { + if (event.sort && onEditAction) { + // unfortunately the neutral state is not propagated and we need to manually handle it + const nextDirection = getNextOrderValue( + sortDirection as LensEditEvent['data']['direction'] + ); + return onEditAction({ + action: 'sort', + columnId: nextDirection !== 'none' ? event.sort.field : undefined, + direction: nextDirection, + }); + } + }} + columns={visibleColumns.map((field) => { + const filterable = bucketColumns.includes(field); + const { name, index: colIndex, meta } = columnsReverseLookup[field]; + const fieldName = meta?.field; + return { + field, + name: name || '', + sortable: true, + render: (value: unknown) => { + const formattedValue = formatters[field]?.convert(value); - if (filterable) { - return ( - - {formattedValue} - - + {formattedValue} + + + + handleFilterClick(field, value, colIndex)} + /> + + handleFilterClick(field, value, colIndex)} + data-test-subj="lensDatatableFilterOut" + onClick={() => handleFilterClick(field, value, colIndex, true)} /> - - - handleFilterClick(field, value, colIndex, true)} - /> - - - - - - ); - } - return {formattedValue}; - }, - }; - }) - .filter(({ field }) => !!field)} - items={firstTable ? firstTable.rows : []} + + + + + ); + } + return {formattedValue}; + }, + }; + })} + items={sortedRows} /> ); diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 8b5d2d7d73348..0860635cc9aeb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -22,6 +22,10 @@ export interface LayerState { export interface DatatableVisualizationState { layers: LayerState[]; + sorting?: { + columnId: string | undefined; + direction: 'asc' | 'desc'; + }; } function newLayerState(layerId: string): LayerState { @@ -232,6 +236,8 @@ export const datatableVisualization: Visualization function: 'lens_datatable_columns', arguments: { columnIds: operations.map((o) => o.columnId), + sortBy: [state.sorting?.columnId || ''], + sortDirection: [state.sorting?.direction || 'none'], }, }, ], diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 6c2c01d944cd9..4bc8bfcccb901 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -37,6 +37,7 @@ import { FramePublicAPI, isLensBrushEvent, isLensFilterEvent, + isLensEditEvent, } from '../../../types'; import { DragDrop, DragContext } from '../../../drag_drop'; import { getSuggestions, switchToSuggestion } from '../suggestion_helpers'; @@ -217,8 +218,20 @@ export function WorkspacePanel({ data: event.data, }); } + if (isLensEditEvent(event)) { + if (event.data.action === 'sort' && activeVisualization?.id) { + dispatch({ + type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, + newState: { + ...(visualizationState as object), + sorting: { columnId: event.data.columnId, direction: event.data.direction }, + }, + }); + } + } }, - [plugins.uiActions] + [plugins.uiActions, dispatch, activeVisualization?.id, visualizationState] ); useEffect(() => { @@ -472,6 +485,7 @@ export const InnerVisualizationWrapper = ({ reload$={autoRefreshFetch$} onEvent={onEvent} onData$={onData$} + renderMode="edit" renderError={(errorMessage?: string | null, error?: ExpressionRenderError | null) => { const visibleErrorMessage = getOriginalRequestErrorMessage(error) || errorMessage; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2f40f21455310..c9f01ed9a9e08 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -605,6 +605,17 @@ export interface LensBrushEvent { data: TriggerContext['data']; } +export interface LensSortActionData { + action: 'sort'; + columnId: string | undefined; + direction: 'asc' | 'desc' | 'none'; +} + +export interface LensEditEvent { + name: 'edit'; + data: LensSortActionData; +} + export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent { return event.name === 'filter'; } @@ -613,11 +624,15 @@ export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensB return event.name === 'brush'; } +export function isLensEditEvent(event: ExpressionRendererEvent): event is LensEditEvent { + return event.name === 'edit'; +} + /** * Expression renderer handlers specifically for lens renderers. This is a narrowed down * version of the general render handlers, specifying supported event types. If this type is * used, dispatched events will be handled correctly. */ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { - event: (event: LensFilterEvent | LensBrushEvent) => void; + event: (event: LensFilterEvent | LensBrushEvent | LensEditEvent) => void; } From 9534f6af368c2f5da6c5094cf4f23fa60102c004 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 30 Nov 2020 12:08:05 +0100 Subject: [PATCH 02/14] :white_check_mark: Add some unit testing for table storting --- .../__snapshots__/expression.test.tsx.snap | 10 +++++ .../expression.test.tsx | 44 ++++++++++++++++++- .../visualization.test.tsx | 2 + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap index 9c7bdc3397f9c..7fd138c42d500 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap @@ -12,16 +12,19 @@ exports[`datatable_expression DatatableComponent it renders the title and value "field": "a", "name": "a", "render": [Function], + "sortable": true, }, Object { "field": "b", "name": "b", "render": [Function], + "sortable": true, }, Object { "field": "c", "name": "c", "render": [Function], + "sortable": true, }, ] } @@ -36,7 +39,14 @@ exports[`datatable_expression DatatableComponent it renders the title and value ] } noItemsMessage="No items found" + onChange={[Function]} responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } tableLayout="auto" /> diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index ea6b99a3fc767..bc7490b410ac5 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -67,6 +67,8 @@ function sampleArgs() { title: 'My fanci metric chart', columns: { columnIds: ['a', 'b', 'c'], + sortBy: '', + sortDirection: 'none', type: 'lens_datatable_columns', }, }; @@ -76,8 +78,11 @@ function sampleArgs() { describe('datatable_expression', () => { let onClickValue: jest.Mock; + let onEditAction: jest.Mock; + beforeEach(() => { onClickValue = jest.fn(); + onEditAction = jest.fn(); }); describe('datatable renders', () => { @@ -214,7 +219,12 @@ describe('datatable_expression', () => { const args: DatatableProps['args'] = { title: '', - columns: { columnIds: ['a', 'b'], type: 'lens_datatable_columns' }, + columns: { + columnIds: ['a', 'b'], + sortBy: '', + sortDirection: 'none', + type: 'lens_datatable_columns', + }, }; const wrapper = mountWithIntl( @@ -274,5 +284,37 @@ describe('datatable_expression', () => { ); expect(component.find(EmptyPlaceholder).prop('icon')).toEqual(LensIconChartDatatable); }); + + test('it renders the the table with the given sorting', () => { + const { data, args } = sampleArgs(); + + const wrapper = mountWithIntl( + x as IFieldFormat} + onClickValue={onClickValue} + onEditAction={onEditAction} + getType={jest.fn()} + /> + ); + + expect(wrapper.exists('[data-test-subj="tableHeaderSortButton"]')).toBe(true); + // check that the sorting is passing the right next state + wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); + + expect(onEditAction).toHaveBeenCalledWith({ + action: 'sort', + columnId: undefined, + direction: 'none', + }); + }); }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx index cf3752e649600..753d561ba177e 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx @@ -371,6 +371,8 @@ describe('Datatable Visualization', () => { expect(tableArgs).toHaveLength(1); expect(tableArgs[0].arguments).toEqual({ columnIds: ['c', 'b'], + sortBy: [''], + sortDirection: ['none'], }); }); From f92107c8f9c4cf07e75a08a9996d4b45a750bf67 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 30 Nov 2020 19:07:11 +0100 Subject: [PATCH 03/14] :white_check_mark: Add test for column switch --- .../expression.test.tsx | 25 ++++++++++++++++--- .../datatable_visualization/expression.tsx | 5 ++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index bc7490b410ac5..7fded4392fcad 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -306,15 +306,34 @@ describe('datatable_expression', () => { /> ); - expect(wrapper.exists('[data-test-subj="tableHeaderSortButton"]')).toBe(true); - // check that the sorting is passing the right next state - wrapper.find('[data-test-subj="tableHeaderSortButton"]').first().simulate('click'); + // there's currently no way to detect the sorting column via DOM + expect( + wrapper.exists('[className*="isSorted"][data-test-subj="tableHeaderSortButton"]') + ).toBe(true); + // check that the sorting is passing the right next state for the same column + wrapper + .find('[className*="isSorted"][data-test-subj="tableHeaderSortButton"]') + .first() + .simulate('click'); expect(onEditAction).toHaveBeenCalledWith({ action: 'sort', columnId: undefined, direction: 'none', }); + + // check that the sorting is passing the right next state for another column + wrapper + .find('[data-test-subj="tableHeaderSortButton"]') + .not('[className*="isSorted"]') + .first() + .simulate('click'); + + expect(onEditAction).toHaveBeenCalledWith({ + action: 'sort', + columnId: 'a', + direction: 'asc', + }); }); }); }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index ed55d50304c12..4180e80409056 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -280,13 +280,14 @@ export function DatatableComponent(props: DatatableRenderProps) { }} onChange={(event: { sort?: { field: string } }) => { if (event.sort && onEditAction) { + const isNewColumn = sortBy !== event.sort.field; // unfortunately the neutral state is not propagated and we need to manually handle it const nextDirection = getNextOrderValue( - sortDirection as LensEditEvent['data']['direction'] + (isNewColumn ? 'none' : sortDirection) as LensEditEvent['data']['direction'] ); return onEditAction({ action: 'sort', - columnId: nextDirection !== 'none' ? event.sort.field : undefined, + columnId: nextDirection !== 'none' || isNewColumn ? event.sort.field : undefined, direction: nextDirection, }); } From 11135c5162acab8c16eed70f7af34ae6ba435394 Mon Sep 17 00:00:00 2001 From: dej611 Date: Wed, 2 Dec 2020 16:01:34 +0100 Subject: [PATCH 04/14] :bug: Fix the dimension removal scenario --- .../visualization.test.tsx | 35 +++++++++++++++++++ .../datatable_visualization/visualization.tsx | 1 + 2 files changed, 36 insertions(+) diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx index 753d561ba177e..088246ccf4b9c 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.test.tsx @@ -306,6 +306,41 @@ describe('Datatable Visualization', () => { ], }); }); + + it('should handle correctly the sorting state on removing dimension', () => { + const layer = { layerId: 'layer1', columns: ['b', 'c'] }; + expect( + datatableVisualization.removeDimension({ + prevState: { layers: [layer], sorting: { columnId: 'b', direction: 'asc' } }, + layerId: 'layer1', + columnId: 'b', + }) + ).toEqual({ + sorting: undefined, + layers: [ + { + layerId: 'layer1', + columns: ['c'], + }, + ], + }); + + expect( + datatableVisualization.removeDimension({ + prevState: { layers: [layer], sorting: { columnId: 'c', direction: 'asc' } }, + layerId: 'layer1', + columnId: 'b', + }) + ).toEqual({ + sorting: { columnId: 'c', direction: 'asc' }, + layers: [ + { + layerId: 'layer1', + columns: ['c'], + }, + ], + }); + }); }); describe('#setDimension', () => { diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 0860635cc9aeb..93a0ab587ab11 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -200,6 +200,7 @@ export const datatableVisualization: Visualization } : l ), + sorting: prevState.sorting?.columnId === columnId ? undefined : prevState.sorting, }; }, From 3877af0b669be43d3b48c44ff2ebcfbcbb101cde Mon Sep 17 00:00:00 2001 From: dej611 Date: Wed, 2 Dec 2020 16:02:01 +0100 Subject: [PATCH 05/14] :label: Refactor types for edit events --- .../datatable_visualization/expression.tsx | 27 ++++++++++++++----- x-pack/plugins/lens/public/types.ts | 27 +++++++++++++------ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 4180e80409056..5d4fb179a9ce6 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -37,6 +37,15 @@ import { EmptyPlaceholder } from '../shared_components'; import { desanitizeFilterContext } from '../utils'; import { LensIconChartDatatable } from '../assets/chart_datatable'; +export const LENS_EDIT_SORT_ACTION = 'sort'; + +export interface LensSortActionData { + columnId: string | undefined; + direction: 'asc' | 'desc' | 'none'; +} + +type LensSortAction = LensEditEvent; + export interface DatatableColumns { columnIds: string[]; sortBy: string; @@ -57,7 +66,7 @@ export interface DatatableProps { type DatatableRenderProps = DatatableProps & { formatFactory: FormatFactory; onClickValue: (data: LensFilterEvent['data']) => void; - onEditAction?: (data: LensEditEvent['data']) => void; + onEditAction?: (data: LensSortAction['data']) => void; getType: (name: string) => IAggType; }; @@ -159,7 +168,7 @@ export const getDatatableRenderer = (dependencies: { handlers.event({ name: 'filter', data }); }; - const onEditAction = (data: LensEditEvent['data']) => { + const onEditAction = (data: LensSortAction['data']) => { if (handlers.getRenderMode() === 'edit') { handlers.event({ name: 'edit', data }); } @@ -183,8 +192,8 @@ export const getDatatableRenderer = (dependencies: { }, }); -function getNextOrderValue(currentValue: LensEditEvent['data']['direction']) { - const states: Array = ['asc', 'desc', 'none']; +function getNextOrderValue(currentValue: LensSortAction['data']['direction']) { + const states: Array = ['asc', 'desc', 'none']; const newStateIndex = (1 + states.findIndex((state) => state === currentValue)) % states.length; return states[newStateIndex]; } @@ -256,7 +265,13 @@ export function DatatableComponent(props: DatatableRenderProps) { let sortedRows = rows; if (sortBy && sortDirection !== 'none') { - sortedRows = orderBy(rows, [sortBy], sortDirection as Direction); + // Sort on raw values for these types, while use the formatted value for the rest + const sortingCriteria = ['number', 'date'].includes( + columnsReverseLookup[sortBy]?.meta?.type || '' + ) + ? sortBy + : (row: Record) => formatters[sortBy]?.convert(row[sortBy]); + sortedRows = orderBy(rows, [sortingCriteria], sortDirection as Direction); } return ( @@ -283,7 +298,7 @@ export function DatatableComponent(props: DatatableRenderProps) { const isNewColumn = sortBy !== event.sort.field; // unfortunately the neutral state is not propagated and we need to manually handle it const nextDirection = getNextOrderValue( - (isNewColumn ? 'none' : sortDirection) as LensEditEvent['data']['direction'] + (isNewColumn ? 'none' : sortDirection) as LensSortAction['data']['direction'] ); return onEditAction({ action: 'sort', diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index c9f01ed9a9e08..a85e114b08991 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -25,6 +25,7 @@ import { VALUE_CLICK_TRIGGER, VisualizeFieldContext, } from '../../../../src/plugins/ui_actions/public'; +import { LensSortActionData, LENS_EDIT_SORT_ACTION } from './datatable_visualization/expression'; export type ErrorCallback = (e: { message: string }) => void; @@ -605,15 +606,21 @@ export interface LensBrushEvent { data: TriggerContext['data']; } -export interface LensSortActionData { - action: 'sort'; - columnId: string | undefined; - direction: 'asc' | 'desc' | 'none'; +// Use same technique as TriggerContext +interface LensEditContextMapping { + [LENS_EDIT_SORT_ACTION]: LensSortActionData; } +type LensEditSupportedActions = keyof LensEditContextMapping; -export interface LensEditEvent { +export type LensEditPayload = { + action: T; +} & LensEditContextMapping[T]; + +type EditPayloadContext = T extends LensEditSupportedActions ? LensEditPayload : never; + +export interface LensEditEvent { name: 'edit'; - data: LensSortActionData; + data: EditPayloadContext; } export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent { @@ -624,7 +631,9 @@ export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensB return event.name === 'brush'; } -export function isLensEditEvent(event: ExpressionRendererEvent): event is LensEditEvent { +export function isLensEditEvent( + event: ExpressionRendererEvent +): event is LensEditEvent { return event.name === 'edit'; } @@ -634,5 +643,7 @@ export function isLensEditEvent(event: ExpressionRendererEvent): event is LensEd * used, dispatched events will be handled correctly. */ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers { - event: (event: LensFilterEvent | LensBrushEvent | LensEditEvent) => void; + event: ( + event: LensFilterEvent | LensBrushEvent | LensEditEvent + ) => void; } From cb5006c2cba55a3e983b680ab2ed727d607a789c Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 3 Dec 2020 11:33:43 +0100 Subject: [PATCH 06/14] :lipstick: Implement readOnly state for sorting --- .../datatable_visualization/expression.tsx | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 5d4fb179a9ce6..1560c82836bf0 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -17,10 +17,12 @@ import { EuiFlexItem, EuiToolTip, Direction, + EuiScreenReaderOnly, + EuiIcon, } from '@elastic/eui'; import { orderBy } from 'lodash'; import { IAggType } from 'src/plugins/data/public'; -import { DatatableColumnMeta } from 'src/plugins/expressions'; +import { DatatableColumnMeta, RenderMode } from 'src/plugins/expressions'; import { FormatFactory, ILensInterpreterRenderHandlers, @@ -68,6 +70,7 @@ type DatatableRenderProps = DatatableProps & { onClickValue: (data: LensFilterEvent['data']) => void; onEditAction?: (data: LensSortAction['data']) => void; getType: (name: string) => IAggType; + renderMode: RenderMode; }; export interface DatatableRender { @@ -181,6 +184,7 @@ export const getDatatableRenderer = (dependencies: { onClickValue={onClickValue} onEditAction={onEditAction} getType={resolvedGetType} + renderMode={handlers.getRenderMode()} /> , domNode, @@ -198,6 +202,39 @@ function getNextOrderValue(currentValue: LensSortAction['data']['direction']) { return states[newStateIndex]; } +function getDirectionLongLabel(sortDirection: LensSortAction['data']['direction']) { + if (sortDirection === 'none') { + return sortDirection; + } + return sortDirection === 'asc' ? 'ascending' : 'descending'; +} + +function getHeaderSortingCell( + name: string, + columnId: string, + sorting: Omit, + sortingLabel: string +) { + if (columnId !== sorting.columnId || sorting.direction === 'none') { + return name || ''; + } + // This is a workaround to hijack the title value of the header cell + return ( + + {name || ''} + + {sortingLabel} + + + + ); +} + export function DatatableComponent(props: DatatableRenderProps) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; @@ -263,6 +300,7 @@ export function DatatableComponent(props: DatatableRenderProps) { const rows = firstTable?.rows || []; let sortedRows = rows; + const isReadOnlySorted = props.renderMode !== 'edit'; if (sortBy && sortDirection !== 'none') { // Sort on raw values for these types, while use the formatted value for the rest @@ -274,6 +312,13 @@ export function DatatableComponent(props: DatatableRenderProps) { sortedRows = orderBy(rows, [sortingCriteria], sortDirection as Direction); } + const sortedInLabel = i18n.translate('xpack.lens.datatableSortedInReadOnlyMode', { + defaultMessage: 'Sorted in {sortValue} order', + values: { + sortValue: sortDirection === 'asc' ? 'ascending' : 'descending', + }, + }); + return ( { const formattedValue = formatters[field]?.convert(value); From 81396a4907ff54712744c842512e08f8a19aa737 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 3 Dec 2020 12:31:40 +0100 Subject: [PATCH 07/14] :white_check_mark: Fix type issue + add test for readOnly mode --- .../expression.test.tsx | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 7fded4392fcad..8bdf692e83ad1 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -15,6 +15,7 @@ import { IFieldFormat } from '../../../../../src/plugins/data/public'; import { IAggType } from 'src/plugins/data/public'; import { EmptyPlaceholder } from '../shared_components'; import { LensIconChartDatatable } from '../assets/chart_datatable'; +import { EuiBasicTable } from '@elastic/eui'; function sampleArgs() { const indexPatternId = 'indexPatternId'; @@ -110,6 +111,7 @@ describe('datatable_expression', () => { formatFactory={(x) => x as IFieldFormat} onClickValue={onClickValue} getType={jest.fn()} + renderMode="edit" /> ) ).toMatchSnapshot(); @@ -131,6 +133,7 @@ describe('datatable_expression', () => { formatFactory={(x) => x as IFieldFormat} onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} + renderMode="edit" /> ); @@ -166,6 +169,7 @@ describe('datatable_expression', () => { formatFactory={(x) => x as IFieldFormat} onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} + renderMode="edit" /> ); @@ -240,6 +244,7 @@ describe('datatable_expression', () => { formatFactory={(x) => x as IFieldFormat} onClickValue={onClickValue} getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} + renderMode="edit" /> ); @@ -280,12 +285,13 @@ describe('datatable_expression', () => { getType={jest.fn((type) => type === 'count' ? ({ type: 'metrics' } as IAggType) : ({ type: 'buckets' } as IAggType) )} + renderMode="edit" /> ); expect(component.find(EmptyPlaceholder).prop('icon')).toEqual(LensIconChartDatatable); }); - test('it renders the the table with the given sorting', () => { + test('it renders the table with the given sorting', () => { const { data, args } = sampleArgs(); const wrapper = mountWithIntl( @@ -303,6 +309,7 @@ describe('datatable_expression', () => { onClickValue={onClickValue} onEditAction={onEditAction} getType={jest.fn()} + renderMode="edit" /> ); @@ -335,5 +342,33 @@ describe('datatable_expression', () => { direction: 'asc', }); }); + + test('it renders the table with the given sorting in readOnly mode', () => { + const { data, args } = sampleArgs(); + + const wrapper = mountWithIntl( + x as IFieldFormat} + onClickValue={onClickValue} + onEditAction={onEditAction} + getType={jest.fn()} + renderMode="display" + /> + ); + + expect(wrapper.find(EuiBasicTable).prop('sorting')).toMatchObject({ + sort: undefined, + allowNeutralSort: true, + }); + }); }); }); From 468005def815527660c68b8cddda23f06b6fc81f Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 3 Dec 2020 15:26:36 +0100 Subject: [PATCH 08/14] :bug: Fix bundle size issue --- x-pack/plugins/lens/public/types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 4d033c316592e..4208501a41647 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -25,7 +25,10 @@ import { VALUE_CLICK_TRIGGER, VisualizeFieldContext, } from '../../../../src/plugins/ui_actions/public'; -import { LensSortActionData, LENS_EDIT_SORT_ACTION } from './datatable_visualization/expression'; +import type { + LensSortActionData, + LENS_EDIT_SORT_ACTION, +} from './datatable_visualization/expression'; export type ErrorCallback = (e: { message: string }) => void; From 59a27158e37cc8349ecb7c3fd0f8faa84ff996a9 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 3 Dec 2020 15:51:16 +0100 Subject: [PATCH 09/14] :sparkles: Make sorting happen at the expression level --- .../expression.test.tsx | 8 +- .../datatable_visualization/expression.tsx | 85 +++++++++++++------ .../public/datatable_visualization/index.ts | 7 +- 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index 8bdf692e83ad1..1065f8ef23f28 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { mountWithIntl } from '@kbn/test/jest'; -import { datatable, DatatableComponent } from './expression'; +import { getDatatable, DatatableComponent } from './expression'; import { LensMultiTable } from '../types'; import { DatatableProps } from './expression'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; @@ -89,7 +89,11 @@ describe('datatable_expression', () => { describe('datatable renders', () => { test('it renders with the specified data and args', () => { const { data, args } = sampleArgs(); - const result = datatable.fn(data, args, createMockExecutionContext()); + const result = getDatatable({ formatFactory: (x) => x as IFieldFormat }).fn( + data, + args, + createMockExecutionContext() + ); expect(result).toEqual({ type: 'render', diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 1560c82836bf0..ce4c8cb0c856d 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -79,12 +79,11 @@ export interface DatatableRender { value: DatatableProps; } -export const datatable: ExpressionFunctionDefinition< - 'lens_datatable', - LensMultiTable, - Args, - DatatableRender -> = { +export const getDatatable = ({ + formatFactory, +}: { + formatFactory: FormatFactory; +}): ExpressionFunctionDefinition<'lens_datatable', LensMultiTable, Args, DatatableRender> => ({ name: 'lens_datatable', type: 'render', inputTypes: ['lens_multitable'], @@ -107,7 +106,44 @@ export const datatable: ExpressionFunctionDefinition< help: '', }, }, - fn(data, args) { + fn(data, args, context) { + // do the sorting at this level to propagate it also at CSV download + const [firstTable] = Object.values(data.tables); + const [layerId] = Object.keys(context.inspectorAdapters.tables || {}); + const formatters: Record> = {}; + + firstTable.columns.forEach((column) => { + formatters[column.id] = formatFactory(column.meta?.params); + }); + const { sortBy, sortDirection } = args.columns; + + const columnsReverseLookup = firstTable.columns.reduce< + Record + >((memo, { id, name, meta }, i) => { + memo[id] = { name, index: i, meta }; + return memo; + }, {}); + + if (sortBy && sortDirection !== 'none') { + // Sort on raw values for these types, while use the formatted value for the rest + const sortingCriteria = ['number', 'date'].includes( + columnsReverseLookup[sortBy]?.meta?.type || '' + ) + ? sortBy + : (row: Record) => + possiblyWrapFormat( + formatters[sortBy]?.convert(row[sortBy]), + columnsReverseLookup[sortBy]?.meta?.type + ); + // replace the table here + context.inspectorAdapters.tables[layerId].rows = orderBy( + firstTable.rows || [], + [sortingCriteria], + sortDirection as Direction + ); + // replace also the local copy + firstTable.rows = context.inspectorAdapters.tables[layerId].rows; + } return { type: 'render', as: 'lens_datatable_renderer', @@ -117,7 +153,7 @@ export const datatable: ExpressionFunctionDefinition< }, }; }, -}; +}); type DatatableColumnsResult = DatatableColumns & { type: 'lens_datatable_columns' }; @@ -150,7 +186,7 @@ export const datatableColumns: ExpressionFunctionDefinition< }; export const getDatatableRenderer = (dependencies: { - formatFactory: Promise; + formatFactory: FormatFactory; getType: Promise<(name: string) => IAggType>; }): ExpressionRenderDefinition => ({ name: 'lens_datatable_renderer', @@ -165,7 +201,6 @@ export const getDatatableRenderer = (dependencies: { config: DatatableProps, handlers: ILensInterpreterRenderHandlers ) => { - const resolvedFormatFactory = await dependencies.formatFactory; const resolvedGetType = await dependencies.getType; const onClickValue = (data: LensFilterEvent['data']) => { handlers.event({ name: 'filter', data }); @@ -180,7 +215,7 @@ export const getDatatableRenderer = (dependencies: { `000${n}`.slice(-3)) + .join('') + ); +} + function getNextOrderValue(currentValue: LensSortAction['data']['direction']) { const states: Array = ['asc', 'desc', 'none']; const newStateIndex = (1 + states.findIndex((state) => state === currentValue)) % states.length; @@ -220,7 +268,7 @@ function getHeaderSortingCell( } // This is a workaround to hijack the title value of the header cell return ( - + {name || ''} {sortingLabel} @@ -298,20 +346,9 @@ export function DatatableComponent(props: DatatableRenderProps) { const { sortBy, sortDirection } = props.args.columns; - const rows = firstTable?.rows || []; - let sortedRows = rows; + const sortedRows = firstTable?.rows || []; const isReadOnlySorted = props.renderMode !== 'edit'; - if (sortBy && sortDirection !== 'none') { - // Sort on raw values for these types, while use the formatted value for the rest - const sortingCriteria = ['number', 'date'].includes( - columnsReverseLookup[sortBy]?.meta?.type || '' - ) - ? sortBy - : (row: Record) => formatters[sortBy]?.convert(row[sortBy]); - sortedRows = orderBy(rows, [sortingCriteria], sortDirection as Direction); - } - const sortedInLabel = i18n.translate('xpack.lens.datatableSortedInReadOnlyMode', { defaultMessage: 'Sorted in {sortValue} order', values: { diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index 5d9be46db7fb5..e29dc8a454101 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -29,16 +29,17 @@ export class DatatableVisualization { ) { editorFrame.registerVisualization(async () => { const { - datatable, + getDatatable, datatableColumns, getDatatableRenderer, datatableVisualization, } = await import('../async_services'); + const resolvedFormatFactory = await formatFactory; expressions.registerFunction(() => datatableColumns); - expressions.registerFunction(() => datatable); + expressions.registerFunction(() => getDatatable({ formatFactory: resolvedFormatFactory })); expressions.registerRenderer(() => getDatatableRenderer({ - formatFactory, + formatFactory: resolvedFormatFactory, getType: core .getStartServices() .then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get), From a5e1086c196b9675b77222322f3e5a7cc6850554 Mon Sep 17 00:00:00 2001 From: dej611 Date: Fri, 4 Dec 2020 16:28:30 +0100 Subject: [PATCH 10/14] :recycle: Refactor edit event flow + vis state updater action --- .../datatable_visualization/visualization.tsx | 15 ++++++++++++++- .../editor_frame/config_panel/config_panel.tsx | 2 +- .../editor_frame/editor_frame.tsx | 4 ++-- .../editor_frame/state_management.test.ts | 2 +- .../editor_frame/state_management.ts | 7 +++++-- .../workspace_panel/workspace_panel.tsx | 9 +++------ .../workspace_panel/workspace_panel_wrapper.tsx | 2 +- x-pack/plugins/lens/public/types.ts | 5 +++++ 8 files changed, 32 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 93a0ab587ab11..e4f787a265186 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -24,7 +24,7 @@ export interface DatatableVisualizationState { layers: LayerState[]; sorting?: { columnId: string | undefined; - direction: 'asc' | 'desc'; + direction: 'asc' | 'desc' | 'none'; }; } @@ -253,6 +253,19 @@ export const datatableVisualization: Visualization getErrorMessages(state, frame) { return undefined; }, + + onEditAction(state, event) { + if (event.data.action !== 'sort') { + return state; + } + return { + ...state, + sorting: { + columnId: event.data.columnId, + direction: event.data.direction, + }, + }; + }, }; function getDataSourceAndSortedColumns( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 3d453cd078b7f..2d7f859521ef8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -42,7 +42,7 @@ function LayerPanels( dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, - newState, + updater: newState, clearStagedPreview: false, }); }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index fea9723aa700d..977947b5afbeb 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -126,7 +126,7 @@ export function EditorFrame(props: EditorFrameProps) { dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, - newState: layerIds.reduce( + updater: layerIds.reduce( (acc, layerId) => activeVisualization.removeLayer ? activeVisualization.removeLayer(acc, layerId) : acc, state.visualization.state @@ -187,7 +187,7 @@ export function EditorFrame(props: EditorFrameProps) { dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, - newState: initialVisualizationState, + updater: initialVisualizationState, }); } }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts index 6d0e1ad48dc21..792fdc6d1ace7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.test.ts @@ -129,7 +129,7 @@ describe('editor_frame state management', () => { { type: 'UPDATE_VISUALIZATION_STATE', visualizationId: 'testVis', - newState: newVisState, + updater: newVisState, } ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts index 55a4cb567fda1..3e6c079ea3b6c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_management.ts @@ -54,7 +54,7 @@ export type Action = | { type: 'UPDATE_VISUALIZATION_STATE'; visualizationId: string; - newState: unknown; + updater: unknown | ((state: unknown) => unknown); clearStagedPreview?: boolean; } | { @@ -282,7 +282,10 @@ export const reducer = (state: EditorFrameState, action: Action): EditorFrameSta ...state, visualization: { ...state.visualization, - state: action.newState, + state: + typeof action.updater === 'function' + ? action.updater(state.visualization.state) + : action.updater, }, stagedPreview: action.clearStagedPreview ? undefined : state.stagedPreview, }; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 4bc8bfcccb901..bfb1493100bbc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -219,19 +219,16 @@ export function WorkspacePanel({ }); } if (isLensEditEvent(event)) { - if (event.data.action === 'sort' && activeVisualization?.id) { + if (activeVisualization?.onEditAction) { dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, - newState: { - ...(visualizationState as object), - sorting: { columnId: event.data.columnId, direction: event.data.direction }, - }, + updater: (oldState: unknown) => activeVisualization.onEditAction!(oldState, event), }); } } }, - [plugins.uiActions, dispatch, activeVisualization?.id, visualizationState] + [plugins.uiActions, dispatch, activeVisualization] ); useEffect(() => { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx index 046bebb33a57d..128241aa766cc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.tsx @@ -59,7 +59,7 @@ export function WorkspacePanelWrapper({ dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, - newState, + updater: newState, clearStagedPreview: false, }); }, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 4208501a41647..5aa776b385a3d 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -607,6 +607,11 @@ export interface Visualization { * The frame calls this function to display warnings about visualization */ getWarningMessages?: (state: T, frame: FramePublicAPI) => React.ReactNode[] | undefined; + + /** + * On Edit events the frame will call this to know what's going to be the next visualization state + */ + onEditAction?: (state: T, event: LensEditEvent) => T; } export interface LensFilterEvent { From 121c701530beb44b7d9b45e02c5b545ab6fc8027 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 14 Dec 2020 12:46:20 +0100 Subject: [PATCH 11/14] :fire: Remove IPv4 specific sorting logic for now --- .../datatable_visualization/expression.tsx | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index ce4c8cb0c856d..dce0066693f42 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -130,11 +130,7 @@ export const getDatatable = ({ columnsReverseLookup[sortBy]?.meta?.type || '' ) ? sortBy - : (row: Record) => - possiblyWrapFormat( - formatters[sortBy]?.convert(row[sortBy]), - columnsReverseLookup[sortBy]?.meta?.type - ); + : (row: Record) => formatters[sortBy]?.convert(row[sortBy]); // replace the table here context.inspectorAdapters.tables[layerId].rows = orderBy( firstTable.rows || [], @@ -231,19 +227,6 @@ export const getDatatableRenderer = (dependencies: { }, }); -function possiblyWrapFormat(rowFormatted: string, type: string | undefined) { - if (type !== 'ip') { - return rowFormatted; - } - // Note this supports only IPv4: maybe there already a solution to support IPv6 and more? - return Number( - rowFormatted - .split('.') - .map((n) => `000${n}`.slice(-3)) - .join('') - ); -} - function getNextOrderValue(currentValue: LensSortAction['data']['direction']) { const states: Array = ['asc', 'desc', 'none']; const newStateIndex = (1 + states.findIndex((state) => state === currentValue)) % states.length; From 406dd81e7a86e1fd84061731a08b3c25de6381f4 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Tue, 15 Dec 2020 09:46:13 +0100 Subject: [PATCH 12/14] Update x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx Co-authored-by: Wylie Conlon --- .../editor_frame/workspace_panel/workspace_panel.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index bfb1493100bbc..c667eea296759 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -218,8 +218,7 @@ export function WorkspacePanel({ data: event.data, }); } - if (isLensEditEvent(event)) { - if (activeVisualization?.onEditAction) { + if (isLensEditEvent(event) && activeVisualization?.onEditAction) { dispatch({ type: 'UPDATE_VISUALIZATION_STATE', visualizationId: activeVisualization.id, From a421bbbd86698d66a238533413b79cfc0f3e7617 Mon Sep 17 00:00:00 2001 From: dej611 Date: Tue, 15 Dec 2020 09:50:46 +0100 Subject: [PATCH 13/14] :camera_flash: Update snapshot --- .../__snapshots__/expression.test.tsx.snap | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap index 89b745d6fdc3e..23460d442cfa8 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap @@ -12,16 +12,19 @@ exports[`datatable_expression DatatableComponent it renders actions column when "field": "a", "name": "a", "render": [Function], + "sortable": true, }, Object { "field": "b", "name": "b", "render": [Function], + "sortable": true, }, Object { "field": "c", "name": "c", "render": [Function], + "sortable": true, }, Object { "actions": Array [ @@ -49,7 +52,14 @@ exports[`datatable_expression DatatableComponent it renders actions column when ] } noItemsMessage="No items found" + onChange={[Function]} responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } + } tableLayout="auto" /> From 6f65ab31473b38bac29875ac7708f3b1ddca9b95 Mon Sep 17 00:00:00 2001 From: dej611 Date: Tue, 15 Dec 2020 09:51:05 +0100 Subject: [PATCH 14/14] :bug: Fix suggestion conflict syntax issue --- .../editor_frame/workspace_panel/workspace_panel.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index c667eea296759..52d1b2729c7ab 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -219,12 +219,11 @@ export function WorkspacePanel({ }); } if (isLensEditEvent(event) && activeVisualization?.onEditAction) { - dispatch({ - type: 'UPDATE_VISUALIZATION_STATE', - visualizationId: activeVisualization.id, - updater: (oldState: unknown) => activeVisualization.onEditAction!(oldState, event), - }); - } + dispatch({ + type: 'UPDATE_VISUALIZATION_STATE', + visualizationId: activeVisualization.id, + updater: (oldState: unknown) => activeVisualization.onEditAction!(oldState, event), + }); } }, [plugins.uiActions, dispatch, activeVisualization]