mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Datatable expression types improvement. (#144173)
* Fixed way of expression building at Lens datatable. * CollapseFn type safety added. * Removed duplicated export. * Small refactoring * Added datatableColumnFn expression. * Added type safety for datatable fn. * Update x-pack/plugins/lens/public/visualizations/datatable/visualization.test.tsx Co-authored-by: Andrew Tate <drewctate@gmail.com> Co-authored-by: Andrew Tate <drewctate@gmail.com>
This commit is contained in:
parent
f8efa76e6e
commit
74829e527f
5 changed files with 99 additions and 116 deletions
|
@ -16,6 +16,8 @@ export interface CollapseArgs {
|
|||
fn: CollapseFunction[];
|
||||
}
|
||||
|
||||
export type { CollapseExpressionFunction };
|
||||
|
||||
/**
|
||||
* Collapses multiple rows into a single row using the specified function.
|
||||
*
|
||||
|
|
|
@ -48,13 +48,14 @@ export interface ColumnState {
|
|||
}
|
||||
|
||||
export type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' };
|
||||
|
||||
export const datatableColumn: ExpressionFunctionDefinition<
|
||||
export type DatatableColumnFunction = ExpressionFunctionDefinition<
|
||||
'lens_datatable_column',
|
||||
null,
|
||||
ColumnState & { sortingHint?: SortingHint },
|
||||
DatatableColumnResult
|
||||
> = {
|
||||
>;
|
||||
|
||||
export const datatableColumn: DatatableColumnFunction = {
|
||||
name: 'lens_datatable_column',
|
||||
aliases: [],
|
||||
type: 'lens_datatable_column',
|
||||
|
|
|
@ -8,4 +8,4 @@
|
|||
export * from './datatable_column';
|
||||
export * from './datatable';
|
||||
|
||||
export type { DatatableProps } from './types';
|
||||
export type { DatatableProps, DatatableExpressionFunction } from './types';
|
||||
|
|
|
@ -550,22 +550,16 @@ describe('Datatable Visualization', () => {
|
|||
expect(columnArgs[0].arguments).toEqual(
|
||||
expect.objectContaining({
|
||||
columnId: ['c'],
|
||||
hidden: [],
|
||||
width: [],
|
||||
isTransposed: [],
|
||||
palette: [expect.any(Object)],
|
||||
transposable: [true],
|
||||
alignment: [],
|
||||
colorMode: ['none'],
|
||||
})
|
||||
);
|
||||
expect(columnArgs[1].arguments).toEqual(
|
||||
expect.objectContaining({
|
||||
columnId: ['b'],
|
||||
hidden: [],
|
||||
width: [],
|
||||
isTransposed: [],
|
||||
palette: [expect.objectContaining({})],
|
||||
transposable: [true],
|
||||
alignment: [],
|
||||
colorMode: ['none'],
|
||||
})
|
||||
);
|
||||
|
@ -592,14 +586,16 @@ describe('Datatable Visualization', () => {
|
|||
});
|
||||
|
||||
it('sets pagination based on state', () => {
|
||||
expect(getDatatableExpressionArgs({ ...defaultExpressionTableState }).pageSize).toEqual([]);
|
||||
expect(getDatatableExpressionArgs({ ...defaultExpressionTableState }).pageSize).toEqual(
|
||||
undefined
|
||||
);
|
||||
|
||||
expect(
|
||||
getDatatableExpressionArgs({
|
||||
...defaultExpressionTableState,
|
||||
paging: { size: 20, enabled: false },
|
||||
}).pageSize
|
||||
).toEqual([]);
|
||||
).toEqual(undefined);
|
||||
|
||||
expect(
|
||||
getDatatableExpressionArgs({
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Ast, AstFunction } from '@kbn/interpreter';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PaletteRegistry, CUSTOM_PALETTE } from '@kbn/coloring';
|
||||
|
@ -16,6 +16,7 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { VIS_EVENT_TO_TRIGGER } from '@kbn/visualizations-plugin/public';
|
||||
import { IconChartDatatable } from '@kbn/chart-icons';
|
||||
import { LayerTypes } from '@kbn/expression-xy-plugin/public';
|
||||
import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugin/common';
|
||||
import type { FormBasedPersistedState } from '../../datasources/form_based/types';
|
||||
import type {
|
||||
SuggestionRequest,
|
||||
|
@ -28,7 +29,14 @@ import { TableDimensionEditor } from './components/dimension_editor';
|
|||
import { TableDimensionEditorAdditionalSection } from './components/dimension_editor_addtional_section';
|
||||
import type { LayerType } from '../../../common';
|
||||
import { getDefaultSummaryLabel } from '../../../common/expressions/datatable/summary';
|
||||
import type { ColumnState, SortingState, PagingState } from '../../../common/expressions';
|
||||
import type {
|
||||
ColumnState,
|
||||
SortingState,
|
||||
PagingState,
|
||||
CollapseExpressionFunction,
|
||||
DatatableColumnFunction,
|
||||
DatatableExpressionFunction,
|
||||
} from '../../../common/expressions';
|
||||
import { DataTableToolbar } from './components/toolbar';
|
||||
|
||||
export interface DatatableVisualizationState {
|
||||
|
@ -398,108 +406,84 @@ export const getDatatableVisualization = ({
|
|||
|
||||
const datasourceExpression = datasourceExpressionsByLayers[state.layerId];
|
||||
|
||||
const lensCollapseFnAsts = columns
|
||||
.filter((c) => c.collapseFn)
|
||||
.map((c) =>
|
||||
buildExpressionFunction<CollapseExpressionFunction>('lens_collapse', {
|
||||
by: columns
|
||||
.filter(
|
||||
(col) =>
|
||||
col.columnId !== c.columnId &&
|
||||
datasource!.getOperationForColumnId(col.columnId)?.isBucketed
|
||||
)
|
||||
.map((col) => col.columnId),
|
||||
metric: columns
|
||||
.filter((col) => !datasource!.getOperationForColumnId(col.columnId)?.isBucketed)
|
||||
.map((col) => col.columnId),
|
||||
fn: [c.collapseFn!],
|
||||
}).toAst()
|
||||
);
|
||||
|
||||
const datatableFnAst = buildExpressionFunction<DatatableExpressionFunction>('lens_datatable', {
|
||||
title: title || '',
|
||||
description: description || '',
|
||||
columns: columns
|
||||
.filter((c) => !c.collapseFn)
|
||||
.map((column) => {
|
||||
const paletteParams = {
|
||||
...column.palette?.params,
|
||||
// rewrite colors and stops as two distinct arguments
|
||||
colors: (column.palette?.params?.stops || []).map(({ color }) => color),
|
||||
stops:
|
||||
column.palette?.params?.name === 'custom'
|
||||
? (column.palette?.params?.stops || []).map(({ stop }) => stop)
|
||||
: [],
|
||||
reverse: false, // managed at UI level
|
||||
};
|
||||
const sortingHint = datasource!.getOperationForColumnId(column.columnId)!.sortingHint;
|
||||
|
||||
const hasNoSummaryRow = column.summaryRow == null || column.summaryRow === 'none';
|
||||
|
||||
const canColor =
|
||||
datasource!.getOperationForColumnId(column.columnId)?.dataType === 'number';
|
||||
|
||||
const datatableColumnFn = buildExpressionFunction<DatatableColumnFunction>(
|
||||
'lens_datatable_column',
|
||||
{
|
||||
columnId: column.columnId,
|
||||
hidden: column.hidden,
|
||||
oneClickFilter: column.oneClickFilter,
|
||||
width: column.width,
|
||||
isTransposed: column.isTransposed,
|
||||
transposable: !datasource!.getOperationForColumnId(column.columnId)?.isBucketed,
|
||||
alignment: column.alignment,
|
||||
colorMode: canColor && column.colorMode ? column.colorMode : 'none',
|
||||
palette: paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams),
|
||||
summaryRow: hasNoSummaryRow ? undefined : column.summaryRow!,
|
||||
summaryLabel: hasNoSummaryRow
|
||||
? undefined
|
||||
: column.summaryLabel ?? getDefaultSummaryLabel(column.summaryRow!),
|
||||
sortingHint,
|
||||
}
|
||||
);
|
||||
return buildExpression([datatableColumnFn]).toAst();
|
||||
}),
|
||||
sortingColumnId: state.sorting?.columnId || '',
|
||||
sortingDirection: state.sorting?.direction || 'none',
|
||||
fitRowToContent: state.rowHeight === 'auto',
|
||||
headerRowHeight: state.headerRowHeight ?? 'single',
|
||||
rowHeightLines:
|
||||
!state.rowHeight || state.rowHeight === 'single' ? 1 : state.rowHeightLines ?? 2,
|
||||
headerRowHeightLines:
|
||||
!state.headerRowHeight || state.headerRowHeight === 'single'
|
||||
? 1
|
||||
: state.headerRowHeightLines ?? 2,
|
||||
pageSize: state.paging?.enabled ? state.paging.size : undefined,
|
||||
}).toAst();
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
...(datasourceExpression?.chain ?? []),
|
||||
...columns
|
||||
.filter((c) => c.collapseFn)
|
||||
.map((c) => {
|
||||
return {
|
||||
type: 'function',
|
||||
function: 'lens_collapse',
|
||||
arguments: {
|
||||
by: columns
|
||||
.filter(
|
||||
(col) =>
|
||||
col.columnId !== c.columnId &&
|
||||
datasource!.getOperationForColumnId(col.columnId)?.isBucketed
|
||||
)
|
||||
.map((col) => col.columnId),
|
||||
metric: columns
|
||||
.filter((col) => !datasource!.getOperationForColumnId(col.columnId)?.isBucketed)
|
||||
.map((col) => col.columnId),
|
||||
fn: [c.collapseFn!],
|
||||
},
|
||||
} as AstFunction;
|
||||
}),
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_datatable',
|
||||
arguments: {
|
||||
title: [title || ''],
|
||||
description: [description || ''],
|
||||
columns: columns
|
||||
.filter((c) => !c.collapseFn)
|
||||
.map((column) => {
|
||||
const paletteParams = {
|
||||
...column.palette?.params,
|
||||
// rewrite colors and stops as two distinct arguments
|
||||
colors: (column.palette?.params?.stops || []).map(({ color }) => color),
|
||||
stops:
|
||||
column.palette?.params?.name === 'custom'
|
||||
? (column.palette?.params?.stops || []).map(({ stop }) => stop)
|
||||
: [],
|
||||
reverse: false, // managed at UI level
|
||||
};
|
||||
const sortingHint = datasource!.getOperationForColumnId(
|
||||
column.columnId
|
||||
)!.sortingHint;
|
||||
|
||||
const hasNoSummaryRow = column.summaryRow == null || column.summaryRow === 'none';
|
||||
|
||||
const canColor =
|
||||
datasource!.getOperationForColumnId(column.columnId)?.dataType === 'number';
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_datatable_column',
|
||||
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],
|
||||
transposable: [
|
||||
!datasource!.getOperationForColumnId(column.columnId)?.isBucketed,
|
||||
],
|
||||
alignment:
|
||||
typeof column.alignment === 'undefined' ? [] : [column.alignment],
|
||||
colorMode: [canColor && column.colorMode ? column.colorMode : 'none'],
|
||||
palette: [paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams)],
|
||||
summaryRow: hasNoSummaryRow ? [] : [column.summaryRow!],
|
||||
summaryLabel: hasNoSummaryRow
|
||||
? []
|
||||
: [column.summaryLabel ?? getDefaultSummaryLabel(column.summaryRow!)],
|
||||
sortingHint: sortingHint ? [sortingHint] : [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
sortingColumnId: [state.sorting?.columnId || ''],
|
||||
sortingDirection: [state.sorting?.direction || 'none'],
|
||||
fitRowToContent: [state.rowHeight === 'auto'],
|
||||
headerRowHeight: [state.headerRowHeight ?? 'single'],
|
||||
rowHeightLines: [
|
||||
!state.rowHeight || state.rowHeight === 'single' ? 1 : state.rowHeightLines ?? 2,
|
||||
],
|
||||
headerRowHeightLines: [
|
||||
!state.headerRowHeight || state.headerRowHeight === 'single'
|
||||
? 1
|
||||
: state.headerRowHeightLines ?? 2,
|
||||
],
|
||||
pageSize: state.paging?.enabled ? [state.paging.size] : [],
|
||||
},
|
||||
},
|
||||
],
|
||||
chain: [...(datasourceExpression?.chain ?? []), ...lensCollapseFnAsts, datatableFnAst],
|
||||
};
|
||||
},
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue