mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Move Lens functions to common
(#105455)
* 🚚 First move batch to common * 🚚 Second batch of move * 🏷️ Import types only * 🚚 Third batch * 🚚 Fourth batch move * 🚚 Another module moved * 🚚 More function moved * 🚚 Last bit of move * ⚡ Reduce page load bundle size * 🐛 Fix import issue * 🐛 More import fix * ✨ Registered functions on the server * 🐛 Expose datatable_column as well * ✅ Add server side expression test * 🚚 Moved back render functions to public * ✨ Add a timezone arg to time_scale * 🔥 Remove timezone arg * 🔥 Remove server side code for now * 👌 Integrated feedback * 🚚 Move back datatable render function * 🏷️ Fix imports * 🏷️ Fix missing export * 🚚 Move render functions back! Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8924ff3219
commit
3c6b85469b
136 changed files with 2124 additions and 1793 deletions
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { counterRate, CounterRateArgs } from '../counter_rate';
|
||||
import { counterRate, CounterRateArgs } from './index';
|
||||
|
||||
import { Datatable } from 'src/plugins/expressions/public';
|
||||
import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils';
|
|
@ -6,11 +6,14 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public';
|
||||
import {
|
||||
getBucketIdentifier,
|
||||
buildResultColumns,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
import type {
|
||||
ExpressionFunctionDefinition,
|
||||
Datatable,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export interface CounterRateArgs {
|
||||
by?: string[];
|
158
x-pack/plugins/lens/common/expressions/datatable/datatable.ts
Normal file
158
x-pack/plugins/lens/common/expressions/datatable/datatable.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import type {
|
||||
DatatableColumnMeta,
|
||||
ExpressionFunctionDefinition,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
import type { FormatFactory, LensMultiTable } from '../../types';
|
||||
import type { ColumnConfigArg } from './datatable_column';
|
||||
import { getSortingCriteria } from './sorting';
|
||||
import { computeSummaryRowForColumn } from './summary';
|
||||
import { transposeTable } from './transpose_helpers';
|
||||
|
||||
export interface SortingState {
|
||||
columnId: string | undefined;
|
||||
direction: 'asc' | 'desc' | 'none';
|
||||
}
|
||||
|
||||
export interface DatatableProps {
|
||||
data: LensMultiTable;
|
||||
untransposedData?: LensMultiTable;
|
||||
args: DatatableArgs;
|
||||
}
|
||||
|
||||
export interface DatatableRender {
|
||||
type: 'render';
|
||||
as: 'lens_datatable_renderer';
|
||||
value: DatatableProps;
|
||||
}
|
||||
|
||||
export interface DatatableArgs {
|
||||
title: string;
|
||||
description?: string;
|
||||
columns: ColumnConfigArg[];
|
||||
sortingColumnId: SortingState['columnId'];
|
||||
sortingDirection: SortingState['direction'];
|
||||
}
|
||||
|
||||
function isRange(meta: { params?: { id?: string } } | undefined) {
|
||||
return meta?.params?.id === 'range';
|
||||
}
|
||||
|
||||
export const getDatatable = ({
|
||||
formatFactory,
|
||||
}: {
|
||||
formatFactory: FormatFactory;
|
||||
}): ExpressionFunctionDefinition<
|
||||
'lens_datatable',
|
||||
LensMultiTable,
|
||||
DatatableArgs,
|
||||
DatatableRender
|
||||
> => ({
|
||||
name: 'lens_datatable',
|
||||
type: 'render',
|
||||
inputTypes: ['lens_multitable'],
|
||||
help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', {
|
||||
defaultMessage: 'Datatable renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.datatable.titleLabel', {
|
||||
defaultMessage: 'Title',
|
||||
}),
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
columns: {
|
||||
types: ['lens_datatable_column'],
|
||||
help: '',
|
||||
multi: true,
|
||||
},
|
||||
sortingColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
sortingDirection: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
},
|
||||
fn(data, args, context) {
|
||||
let untransposedData: LensMultiTable | undefined;
|
||||
// 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<string, ReturnType<FormatFactory>> = {};
|
||||
|
||||
firstTable.columns.forEach((column) => {
|
||||
formatters[column.id] = formatFactory(column.meta?.params);
|
||||
});
|
||||
|
||||
const hasTransposedColumns = args.columns.some((c) => c.isTransposed);
|
||||
if (hasTransposedColumns) {
|
||||
// store original shape of data separately
|
||||
untransposedData = cloneDeep(data);
|
||||
// transposes table and args inplace
|
||||
transposeTable(args, firstTable, formatters);
|
||||
}
|
||||
|
||||
const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args;
|
||||
|
||||
const columnsReverseLookup = firstTable.columns.reduce<
|
||||
Record<string, { name: string; index: number; meta?: DatatableColumnMeta }>
|
||||
>((memo, { id, name, meta }, i) => {
|
||||
memo[id] = { name, index: i, meta };
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const columnsWithSummary = args.columns.filter((c) => c.summaryRow);
|
||||
for (const column of columnsWithSummary) {
|
||||
column.summaryRowValue = computeSummaryRowForColumn(
|
||||
column,
|
||||
firstTable,
|
||||
formatters,
|
||||
formatFactory({ id: 'number' })
|
||||
);
|
||||
}
|
||||
|
||||
if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') {
|
||||
// Sort on raw values for these types, while use the formatted value for the rest
|
||||
const sortingCriteria = getSortingCriteria(
|
||||
isRange(columnsReverseLookup[sortBy]?.meta)
|
||||
? 'range'
|
||||
: columnsReverseLookup[sortBy]?.meta?.type,
|
||||
sortBy,
|
||||
formatters[sortBy],
|
||||
sortDirection
|
||||
);
|
||||
// replace the table here
|
||||
context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || [])
|
||||
.slice()
|
||||
.sort(sortingCriteria);
|
||||
// replace also the local copy
|
||||
firstTable.rows = context.inspectorAdapters.tables[layerId].rows;
|
||||
} else {
|
||||
args.sortingColumnId = undefined;
|
||||
args.sortingDirection = 'none';
|
||||
}
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_datatable_renderer',
|
||||
value: {
|
||||
data,
|
||||
untransposedData,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Direction } from '@elastic/eui';
|
||||
import type {
|
||||
CustomPaletteState,
|
||||
PaletteOutput,
|
||||
} from '../../../../../../src/plugins/charts/common';
|
||||
import type {
|
||||
ExpressionFunctionDefinition,
|
||||
DatatableColumn,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
import type { CustomPaletteParams } from '../../types';
|
||||
|
||||
export type LensGridDirection = 'none' | Direction;
|
||||
|
||||
export interface ColumnConfig {
|
||||
columns: ColumnConfigArg[];
|
||||
sortingColumnId: string | undefined;
|
||||
sortingDirection: LensGridDirection;
|
||||
}
|
||||
|
||||
export type ColumnConfigArg = Omit<ColumnState, 'palette'> & {
|
||||
type: 'lens_datatable_column';
|
||||
palette?: PaletteOutput<CustomPaletteState>;
|
||||
summaryRowValue?: unknown;
|
||||
};
|
||||
|
||||
export interface ColumnState {
|
||||
columnId: string;
|
||||
width?: number;
|
||||
hidden?: boolean;
|
||||
isTransposed?: boolean;
|
||||
// These flags are necessary to transpose columns and map them back later
|
||||
// They are set automatically and are not user-editable
|
||||
transposable?: boolean;
|
||||
originalColumnId?: string;
|
||||
originalName?: string;
|
||||
bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>;
|
||||
alignment?: 'left' | 'right' | 'center';
|
||||
palette?: PaletteOutput<CustomPaletteParams>;
|
||||
colorMode?: 'none' | 'cell' | 'text';
|
||||
summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max';
|
||||
summaryLabel?: string;
|
||||
}
|
||||
|
||||
export type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' };
|
||||
|
||||
export const datatableColumn: ExpressionFunctionDefinition<
|
||||
'lens_datatable_column',
|
||||
null,
|
||||
ColumnState,
|
||||
DatatableColumnResult
|
||||
> = {
|
||||
name: 'lens_datatable_column',
|
||||
aliases: [],
|
||||
type: 'lens_datatable_column',
|
||||
help: '',
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
columnId: { types: ['string'], help: '' },
|
||||
alignment: { types: ['string'], help: '' },
|
||||
hidden: { types: ['boolean'], help: '' },
|
||||
width: { types: ['number'], help: '' },
|
||||
isTransposed: { types: ['boolean'], help: '' },
|
||||
transposable: { types: ['boolean'], help: '' },
|
||||
colorMode: { types: ['string'], help: '' },
|
||||
palette: {
|
||||
types: ['palette'],
|
||||
help: '',
|
||||
},
|
||||
summaryRow: { types: ['string'], help: '' },
|
||||
summaryLabel: { types: ['string'], help: '' },
|
||||
},
|
||||
fn: function fn(input: unknown, args: ColumnState) {
|
||||
return {
|
||||
type: 'lens_datatable_column',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
12
x-pack/plugins/lens/common/expressions/datatable/index.ts
Normal file
12
x-pack/plugins/lens/common/expressions/datatable/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './datatable_column';
|
||||
export * from './datatable';
|
||||
export * from './summary';
|
||||
export * from './transpose_helpers';
|
||||
export * from './utils';
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import ipaddr from 'ipaddr.js';
|
||||
import type { IPv4, IPv6 } from 'ipaddr.js';
|
||||
import { FieldFormat } from 'src/plugins/data/public';
|
||||
import type { FieldFormat } from '../../../../../../src/plugins/data/common';
|
||||
|
||||
function isIPv6Address(ip: IPv4 | IPv6): ip is IPv6 {
|
||||
return ip.kind() === 'ipv6';
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IFieldFormat } from 'src/plugins/data/public';
|
||||
import { IFieldFormat } from 'src/plugins/data/common';
|
||||
import { Datatable } from 'src/plugins/expressions';
|
||||
import { computeSummaryRowForColumn, getFinalSummaryConfiguration } from './summary';
|
||||
|
|
@ -6,11 +6,11 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FieldFormat } from 'src/plugins/data/public';
|
||||
import { Datatable } from 'src/plugins/expressions/public';
|
||||
import { ColumnConfigArg } from './datatable_visualization';
|
||||
import type { FieldFormat } from '../../../../../../src/plugins/data/common';
|
||||
import type { Datatable } from '../../../../../../src/plugins/expressions/common';
|
||||
import { ColumnConfigArg } from './datatable_column';
|
||||
import { getOriginalId } from './transpose_helpers';
|
||||
import { isNumericField } from './utils';
|
||||
import { isNumericFieldForDatatable } from './utils';
|
||||
|
||||
type SummaryRowType = Extract<ColumnConfigArg['summaryRow'], string>;
|
||||
|
||||
|
@ -19,7 +19,7 @@ export function getFinalSummaryConfiguration(
|
|||
columnArgs: Pick<ColumnConfigArg, 'summaryRow' | 'summaryLabel'> | undefined,
|
||||
table: Datatable | undefined
|
||||
) {
|
||||
const isNumeric = isNumericField(table, columnId);
|
||||
const isNumeric = isNumericFieldForDatatable(table, columnId);
|
||||
|
||||
const summaryRow = isNumeric ? columnArgs?.summaryRow || 'none' : 'none';
|
||||
const summaryLabel = columnArgs?.summaryLabel ?? getDefaultSummaryLabel(summaryRow);
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import type { FieldFormat } from 'src/plugins/data/public';
|
||||
import type { Datatable } from 'src/plugins/expressions';
|
||||
import { DatatableArgs } from './datatable';
|
||||
|
||||
import { Args } from './expression';
|
||||
import { transposeTable } from './transpose_helpers';
|
||||
|
||||
describe('transpose_helpes', () => {
|
||||
|
@ -59,7 +59,7 @@ describe('transpose_helpes', () => {
|
|||
};
|
||||
}
|
||||
|
||||
function buildArgs(): Args {
|
||||
function buildArgs(): DatatableArgs {
|
||||
return {
|
||||
title: 'Table',
|
||||
sortingColumnId: undefined,
|
|
@ -5,11 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FieldFormat } from 'src/plugins/data/public';
|
||||
import type { Datatable, DatatableColumn, DatatableRow } from 'src/plugins/expressions';
|
||||
import { ColumnConfig } from './components/table_basic';
|
||||
|
||||
import { Args, ColumnConfigArg } from './expression';
|
||||
import type {
|
||||
Datatable,
|
||||
DatatableColumn,
|
||||
DatatableRow,
|
||||
} from '../../../../../../src/plugins/expressions';
|
||||
import type { FieldFormat } from '../../../../../../src/plugins/data/common';
|
||||
import type { DatatableArgs } from './datatable';
|
||||
import type { ColumnConfig, ColumnConfigArg } from './datatable_column';
|
||||
|
||||
const TRANSPOSE_SEPARATOR = '---';
|
||||
|
||||
|
@ -42,7 +45,7 @@ export function getOriginalId(id: string) {
|
|||
* @param formatters Formatters for all columns to transpose columns by actual display values
|
||||
*/
|
||||
export function transposeTable(
|
||||
args: Args,
|
||||
args: DatatableArgs,
|
||||
firstTable: Datatable,
|
||||
formatters: Record<string, FieldFormat>
|
||||
) {
|
||||
|
@ -112,7 +115,7 @@ function transposeRows(
|
|||
* grouped by unique value
|
||||
*/
|
||||
function updateColumnArgs(
|
||||
args: Args,
|
||||
args: DatatableArgs,
|
||||
bucketsColumnArgs: ColumnConfig['columns'],
|
||||
transposedColumnGroups: Array<ColumnConfig['columns']>
|
||||
) {
|
||||
|
@ -150,7 +153,7 @@ function getUniqueValues(table: Datatable, formatter: FieldFormat, columnId: str
|
|||
* @param uniqueValues
|
||||
*/
|
||||
function transposeColumns(
|
||||
args: Args,
|
||||
args: DatatableArgs,
|
||||
bucketsColumnArgs: ColumnConfig['columns'],
|
||||
metricColumns: ColumnConfig['columns'],
|
||||
firstTable: Datatable,
|
|
@ -5,14 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Datatable } from 'src/plugins/expressions/public';
|
||||
import type { Datatable } from '../../../../../../src/plugins/expressions/common';
|
||||
import { getOriginalId } from './transpose_helpers';
|
||||
|
||||
function isValidNumber(value: unknown): boolean {
|
||||
return typeof value === 'number' || value == null;
|
||||
}
|
||||
|
||||
export function isNumericField(currentData: Datatable | undefined, accessor: string) {
|
||||
export function isNumericFieldForDatatable(currentData: Datatable | undefined, accessor: string) {
|
||||
const isNumeric =
|
||||
currentData?.columns.find((col) => col.id === accessor || getOriginalId(col.id) === accessor)
|
||||
?.meta.type === 'number';
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { Datatable, DatatableColumn } from 'src/plugins/expressions/public';
|
||||
import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils';
|
||||
import { FormatColumnArgs, formatColumn } from './format_column';
|
||||
import { FormatColumnArgs, formatColumn } from './index';
|
||||
|
||||
describe('format_column', () => {
|
||||
const fn: (input: Datatable, args: FormatColumnArgs) => Datatable = functionWrapper(formatColumn);
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
import type {
|
||||
ExpressionFunctionDefinition,
|
||||
Datatable,
|
||||
DatatableColumn,
|
||||
} from 'src/plugins/expressions/public';
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export interface FormatColumnArgs {
|
||||
format: string;
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { PaletteOutput } from '../../../../../../src/plugins/charts/common';
|
||||
import type { LensMultiTable, CustomPaletteParams } from '../../types';
|
||||
import { HeatmapGridConfigResult, HEATMAP_GRID_FUNCTION } from './heatmap_grid';
|
||||
import { HeatmapLegendConfigResult, HEATMAP_LEGEND_FUNCTION } from './heatmap_legend';
|
||||
|
||||
export const HEATMAP_FUNCTION = 'lens_heatmap';
|
||||
export const HEATMAP_FUNCTION_RENDERER = 'lens_heatmap_renderer';
|
||||
|
||||
export type ChartShapes = 'heatmap';
|
||||
|
||||
export interface SharedHeatmapLayerState {
|
||||
shape: ChartShapes;
|
||||
xAccessor?: string;
|
||||
yAccessor?: string;
|
||||
valueAccessor?: string;
|
||||
legend: HeatmapLegendConfigResult;
|
||||
gridConfig: HeatmapGridConfigResult;
|
||||
}
|
||||
|
||||
export type HeatmapLayerState = SharedHeatmapLayerState & {
|
||||
layerId: string;
|
||||
};
|
||||
|
||||
export type HeatmapVisualizationState = HeatmapLayerState & {
|
||||
// need to store the current accessor to reset the color stops at accessor change
|
||||
palette?: PaletteOutput<CustomPaletteParams> & { accessor: string };
|
||||
};
|
||||
|
||||
export type HeatmapExpressionArgs = SharedHeatmapLayerState & {
|
||||
title?: string;
|
||||
description?: string;
|
||||
palette: PaletteOutput;
|
||||
};
|
||||
|
||||
export interface HeatmapRender {
|
||||
type: 'render';
|
||||
as: typeof HEATMAP_FUNCTION_RENDERER;
|
||||
value: HeatmapExpressionProps;
|
||||
}
|
||||
|
||||
export interface HeatmapExpressionProps {
|
||||
data: LensMultiTable;
|
||||
args: HeatmapExpressionArgs;
|
||||
}
|
||||
|
||||
export const heatmap: ExpressionFunctionDefinition<
|
||||
typeof HEATMAP_FUNCTION,
|
||||
LensMultiTable,
|
||||
HeatmapExpressionArgs,
|
||||
HeatmapRender
|
||||
> = {
|
||||
name: HEATMAP_FUNCTION,
|
||||
type: 'render',
|
||||
help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', {
|
||||
defaultMessage: 'Heatmap renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmap.titleLabel', {
|
||||
defaultMessage: 'Title',
|
||||
}),
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
xAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
yAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
valueAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
shape: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
palette: {
|
||||
default: `{theme "palette" default={system_palette name="default"} }`,
|
||||
help: '',
|
||||
types: ['palette'],
|
||||
},
|
||||
legend: {
|
||||
types: [HEATMAP_LEGEND_FUNCTION],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.help', {
|
||||
defaultMessage: 'Configure the chart legend.',
|
||||
}),
|
||||
},
|
||||
gridConfig: {
|
||||
types: [HEATMAP_GRID_FUNCTION],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', {
|
||||
defaultMessage: 'Configure the heatmap layout.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data: LensMultiTable, args: HeatmapExpressionArgs) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: HEATMAP_FUNCTION_RENDERER,
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export const HEATMAP_GRID_FUNCTION = 'lens_heatmap_grid';
|
||||
|
||||
export interface HeatmapGridConfig {
|
||||
// grid
|
||||
strokeWidth?: number;
|
||||
strokeColor?: string;
|
||||
cellHeight?: number;
|
||||
cellWidth?: number;
|
||||
// cells
|
||||
isCellLabelVisible: boolean;
|
||||
// Y-axis
|
||||
isYAxisLabelVisible: boolean;
|
||||
yAxisLabelWidth?: number;
|
||||
yAxisLabelColor?: string;
|
||||
// X-axis
|
||||
isXAxisLabelVisible: boolean;
|
||||
}
|
||||
|
||||
export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION };
|
||||
|
||||
export const heatmapGridConfig: ExpressionFunctionDefinition<
|
||||
typeof HEATMAP_GRID_FUNCTION,
|
||||
null,
|
||||
HeatmapGridConfig,
|
||||
HeatmapGridConfigResult
|
||||
> = {
|
||||
name: HEATMAP_GRID_FUNCTION,
|
||||
aliases: [],
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
help: `Configure the heatmap layout `,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
// grid
|
||||
strokeWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', {
|
||||
defaultMessage: 'Specifies the grid stroke width',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
strokeColor: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', {
|
||||
defaultMessage: 'Specifies the grid stroke color',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
cellHeight: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', {
|
||||
defaultMessage: 'Specifies the grid cell height',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
cellWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', {
|
||||
defaultMessage: 'Specifies the grid cell width',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
// cells
|
||||
isCellLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the cell label is visible.',
|
||||
}),
|
||||
},
|
||||
// Y-axis
|
||||
isYAxisLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the Y-axis labels are visible.',
|
||||
}),
|
||||
},
|
||||
yAxisLabelWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', {
|
||||
defaultMessage: 'Specifies the width of the Y-axis labels.',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
yAxisLabelColor: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', {
|
||||
defaultMessage: 'Specifies the color of the Y-axis labels.',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
// X-axis
|
||||
isXAxisLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the X-axis labels are visible.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
return {
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { Position } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export const HEATMAP_LEGEND_FUNCTION = 'lens_heatmap_legendConfig';
|
||||
|
||||
export interface HeatmapLegendConfig {
|
||||
/**
|
||||
* Flag whether the legend should be shown. If there is just a single series, it will be hidden
|
||||
*/
|
||||
isVisible: boolean;
|
||||
/**
|
||||
* Position of the legend relative to the chart
|
||||
*/
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export type HeatmapLegendConfigResult = HeatmapLegendConfig & {
|
||||
type: typeof HEATMAP_LEGEND_FUNCTION;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO check if it's possible to make a shared function
|
||||
* based on the XY chart
|
||||
*/
|
||||
export const heatmapLegendConfig: ExpressionFunctionDefinition<
|
||||
typeof HEATMAP_LEGEND_FUNCTION,
|
||||
null,
|
||||
HeatmapLegendConfig,
|
||||
HeatmapLegendConfigResult
|
||||
> = {
|
||||
name: HEATMAP_LEGEND_FUNCTION,
|
||||
aliases: [],
|
||||
type: HEATMAP_LEGEND_FUNCTION,
|
||||
help: `Configure the heatmap chart's legend`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
isVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the legend is visible.',
|
||||
}),
|
||||
},
|
||||
position: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', {
|
||||
defaultMessage: 'Specifies the legend position.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
return {
|
||||
type: HEATMAP_LEGEND_FUNCTION,
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './heatmap_grid';
|
||||
export * from './heatmap_legend';
|
||||
export * from './heatmap_chart';
|
17
x-pack/plugins/lens/common/expressions/index.ts
Normal file
17
x-pack/plugins/lens/common/expressions/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './counter_rate';
|
||||
export * from './format_column';
|
||||
export * from './rename_columns';
|
||||
export * from './merge_tables';
|
||||
export * from './time_scale';
|
||||
export * from './datatable';
|
||||
export * from './heatmap_chart';
|
||||
export * from './metric_chart';
|
||||
export * from './pie_chart';
|
||||
export * from './xy_chart';
|
|
@ -6,16 +6,16 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
ExecutionContext,
|
||||
Datatable,
|
||||
import type {
|
||||
ExpressionFunctionDefinition,
|
||||
} from 'src/plugins/expressions/public';
|
||||
import { ExpressionValueSearchContext, search } from '../../../../../src/plugins/data/public';
|
||||
const { toAbsoluteDates } = search.aggs;
|
||||
Datatable,
|
||||
ExecutionContext,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
import { toAbsoluteDates } from '../../../../../../src/plugins/data/common';
|
||||
import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common';
|
||||
|
||||
import { LensMultiTable } from '../types';
|
||||
import { Adapters } from '../../../../../src/plugins/inspector/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
import { Adapters } from '../../../../../../src/plugins/inspector/common';
|
||||
|
||||
interface MergeTables {
|
||||
layerIds: string[];
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { mergeTables } from './merge_tables';
|
||||
import { mergeTables } from './index';
|
||||
import { ExpressionValueSearchContext } from 'src/plugins/data/public';
|
||||
import {
|
||||
Datatable,
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './metric_chart';
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
import type { MetricConfig } from './types';
|
||||
|
||||
export interface MetricChartProps {
|
||||
data: LensMultiTable;
|
||||
args: MetricConfig;
|
||||
}
|
||||
|
||||
export interface MetricRender {
|
||||
type: 'render';
|
||||
as: 'lens_metric_chart_renderer';
|
||||
value: MetricChartProps;
|
||||
}
|
||||
|
||||
export const metricChart: ExpressionFunctionDefinition<
|
||||
'lens_metric_chart',
|
||||
LensMultiTable,
|
||||
Omit<MetricConfig, 'layerId'>,
|
||||
MetricRender
|
||||
> = {
|
||||
name: 'lens_metric_chart',
|
||||
type: 'render',
|
||||
help: 'A metric chart',
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: 'The chart title.',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
metricTitle: {
|
||||
types: ['string'],
|
||||
help: 'The title of the metric shown.',
|
||||
},
|
||||
accessor: {
|
||||
types: ['string'],
|
||||
help: 'The column whose value is being displayed',
|
||||
},
|
||||
mode: {
|
||||
types: ['string'],
|
||||
options: ['reduced', 'full'],
|
||||
default: 'full',
|
||||
help:
|
||||
'The display mode of the chart - reduced will only show the metric itself without min size',
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data, args) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_metric_chart_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
} as MetricRender;
|
||||
},
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './pie_chart';
|
103
x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
Normal file
103
x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Position } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
import type { PieExpressionProps, PieExpressionArgs } from './types';
|
||||
|
||||
export interface PieRender {
|
||||
type: 'render';
|
||||
as: 'lens_pie_renderer';
|
||||
value: PieExpressionProps;
|
||||
}
|
||||
|
||||
export const pie: ExpressionFunctionDefinition<
|
||||
'lens_pie',
|
||||
LensMultiTable,
|
||||
PieExpressionArgs,
|
||||
PieRender
|
||||
> = {
|
||||
name: 'lens_pie',
|
||||
type: 'render',
|
||||
help: i18n.translate('xpack.lens.pie.expressionHelpLabel', {
|
||||
defaultMessage: 'Pie renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: 'The chart title.',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
groups: {
|
||||
types: ['string'],
|
||||
multi: true,
|
||||
help: '',
|
||||
},
|
||||
metric: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
shape: {
|
||||
types: ['string'],
|
||||
options: ['pie', 'donut', 'treemap'],
|
||||
help: '',
|
||||
},
|
||||
hideLabels: {
|
||||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
numberDisplay: {
|
||||
types: ['string'],
|
||||
options: ['hidden', 'percent', 'value'],
|
||||
help: '',
|
||||
},
|
||||
categoryDisplay: {
|
||||
types: ['string'],
|
||||
options: ['default', 'inside', 'hide'],
|
||||
help: '',
|
||||
},
|
||||
legendDisplay: {
|
||||
types: ['string'],
|
||||
options: ['default', 'show', 'hide'],
|
||||
help: '',
|
||||
},
|
||||
nestedLegend: {
|
||||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
legendPosition: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
help: '',
|
||||
},
|
||||
percentDecimals: {
|
||||
types: ['number'],
|
||||
help: '',
|
||||
},
|
||||
palette: {
|
||||
default: `{theme "palette" default={system_palette name="default"} }`,
|
||||
help: '',
|
||||
types: ['palette'],
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data: LensMultiTable, args: PieExpressionArgs) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_pie_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PaletteOutput } from 'src/plugins/charts/public';
|
||||
import { LensMultiTable } from '../types';
|
||||
import type { PaletteOutput } from '../../../../../../src/plugins/charts/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
|
||||
export interface SharedPieLayerState {
|
||||
groups: string[];
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './rename_columns';
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
import { renameColumns } from './rename_columns';
|
||||
import { Datatable } from '../../../../../src/plugins/expressions/public';
|
||||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { Datatable } from '../../../../../../src/plugins/expressions/common';
|
||||
import { createMockExecutionContext } from '../../../../../../src/plugins/expressions/common/mocks';
|
||||
|
||||
describe('rename_columns', () => {
|
||||
it('should rename columns of a given datatable', () => {
|
|
@ -6,14 +6,20 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition, Datatable, DatatableColumn } from 'src/plugins/expressions';
|
||||
import { IndexPatternColumn } from './operations';
|
||||
import {
|
||||
ExpressionFunctionDefinition,
|
||||
Datatable,
|
||||
DatatableColumn,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
interface RemapArgs {
|
||||
idMap: string;
|
||||
}
|
||||
|
||||
export type OriginalColumn = { id: string } & IndexPatternColumn;
|
||||
type OriginalColumn = { id: string; label: string } & (
|
||||
| { operationType: 'date_histogram'; sourceField: string }
|
||||
| { operationType: string; sourceField: never }
|
||||
);
|
||||
|
||||
export const renameColumns: ExpressionFunctionDefinition<
|
||||
'lens_rename_columns',
|
||||
|
@ -75,7 +81,7 @@ export const renameColumns: ExpressionFunctionDefinition<
|
|||
};
|
||||
|
||||
function getColumnName(originalColumn: OriginalColumn, newColumn: DatatableColumn) {
|
||||
if (originalColumn && originalColumn.operationType === 'date_histogram') {
|
||||
if (originalColumn?.operationType === 'date_histogram') {
|
||||
const fieldName = originalColumn.sourceField;
|
||||
|
||||
// HACK: This is a hack, and introduces some fragility into
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './time_scale';
|
||||
export * from './types';
|
|
@ -7,14 +7,25 @@
|
|||
|
||||
import moment from 'moment';
|
||||
import { Datatable } from 'src/plugins/expressions/public';
|
||||
import { DataPublicPluginStart, TimeRange } from 'src/plugins/data/public';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
|
||||
import { TimeRange } from 'src/plugins/data/public';
|
||||
import { functionWrapper } from 'src/plugins/expressions/common/expression_functions/specs/tests/utils';
|
||||
import { getTimeScaleFunction, TimeScaleArgs } from './time_scale';
|
||||
|
||||
// mock the specific inner variable:
|
||||
// there are intra dependencies in the data plugin we might break trying to mock the whole thing
|
||||
jest.mock('../../../../../../src/plugins/data/common/query/timefilter/get_time', () => {
|
||||
const localMoment = jest.requireActual('moment');
|
||||
return {
|
||||
calculateBounds: jest.fn(({ from, to }) => ({
|
||||
min: localMoment(from),
|
||||
max: localMoment(to),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
import { timeScale, TimeScaleArgs } from './time_scale';
|
||||
|
||||
describe('time_scale', () => {
|
||||
let timeScale: (input: Datatable, args: TimeScaleArgs) => Promise<Datatable>;
|
||||
let dataMock: jest.Mocked<DataPublicPluginStart>;
|
||||
let timeScaleWrapped: (input: Datatable, args: TimeScaleArgs) => Promise<Datatable>;
|
||||
|
||||
const emptyTable: Datatable = {
|
||||
type: 'datatable',
|
||||
|
@ -61,7 +72,6 @@ describe('time_scale', () => {
|
|||
}
|
||||
|
||||
beforeEach(() => {
|
||||
dataMock = dataPluginMock.createStartContract();
|
||||
setDateHistogramMeta({
|
||||
timeZone: 'UTC',
|
||||
timeRange: {
|
||||
|
@ -70,17 +80,11 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '1d',
|
||||
});
|
||||
(dataMock.query.timefilter.timefilter.calculateBounds as jest.Mock).mockImplementation(
|
||||
({ from, to }) => ({
|
||||
min: moment(from),
|
||||
max: moment(to),
|
||||
})
|
||||
);
|
||||
timeScale = functionWrapper(getTimeScaleFunction(dataMock));
|
||||
timeScaleWrapped = functionWrapper(timeScale);
|
||||
});
|
||||
|
||||
it('should apply time scale factor to each row', async () => {
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -115,7 +119,7 @@ describe('time_scale', () => {
|
|||
});
|
||||
|
||||
it('should skip gaps in the data', async () => {
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -163,7 +167,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
],
|
||||
};
|
||||
const result = await timeScale(mismatchedTable, {
|
||||
const result = await timeScaleWrapped(mismatchedTable, {
|
||||
...defaultArgs,
|
||||
inputColumnId: 'nonexistent',
|
||||
});
|
||||
|
@ -180,7 +184,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '1h',
|
||||
});
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -220,7 +224,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '3h',
|
||||
});
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -262,7 +266,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '1d',
|
||||
});
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -307,7 +311,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '1d',
|
||||
});
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
||||
|
@ -347,7 +351,7 @@ describe('time_scale', () => {
|
|||
},
|
||||
interval: '1y',
|
||||
});
|
||||
const result = await timeScale(
|
||||
const result = await timeScaleWrapped(
|
||||
{
|
||||
...emptyTable,
|
||||
rows: [
|
151
x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts
Normal file
151
x-pack/plugins/lens/common/expressions/time_scale/time_scale.ts
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type {
|
||||
ExpressionFunctionDefinition,
|
||||
Datatable,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
import {
|
||||
getDateHistogramMetaDataByDatatableColumn,
|
||||
parseInterval,
|
||||
calculateBounds,
|
||||
} from '../../../../../../src/plugins/data/common';
|
||||
import { buildResultColumns } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { TimeScaleUnit } from './types';
|
||||
|
||||
export interface TimeScaleArgs {
|
||||
dateColumnId: string;
|
||||
inputColumnId: string;
|
||||
outputColumnId: string;
|
||||
targetUnit: TimeScaleUnit;
|
||||
outputColumnName?: string;
|
||||
}
|
||||
|
||||
const unitInMs: Record<TimeScaleUnit, number> = {
|
||||
s: 1000,
|
||||
m: 1000 * 60,
|
||||
h: 1000 * 60 * 60,
|
||||
d: 1000 * 60 * 60 * 24,
|
||||
};
|
||||
|
||||
export const timeScale: ExpressionFunctionDefinition<
|
||||
'lens_time_scale',
|
||||
Datatable,
|
||||
TimeScaleArgs,
|
||||
Promise<Datatable>
|
||||
> = {
|
||||
name: 'lens_time_scale',
|
||||
type: 'datatable',
|
||||
help: '',
|
||||
args: {
|
||||
dateColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
inputColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
outputColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
outputColumnName: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
targetUnit: {
|
||||
types: ['string'],
|
||||
options: ['s', 'm', 'h', 'd'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
inputTypes: ['datatable'],
|
||||
async fn(
|
||||
input,
|
||||
{ dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs
|
||||
) {
|
||||
const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId);
|
||||
|
||||
if (!dateColumnDefinition) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', {
|
||||
defaultMessage: 'Specified dateColumnId {columnId} does not exist.',
|
||||
values: {
|
||||
columnId: dateColumnId,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const resultColumns = buildResultColumns(
|
||||
input,
|
||||
outputColumnId,
|
||||
inputColumnId,
|
||||
outputColumnName,
|
||||
{ allowColumnOverwrite: true }
|
||||
);
|
||||
|
||||
if (!resultColumns) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const targetUnitInMs = unitInMs[targetUnit];
|
||||
const timeInfo = getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition);
|
||||
const intervalDuration = timeInfo?.interval && parseInterval(timeInfo.interval);
|
||||
|
||||
if (!timeInfo || !intervalDuration) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', {
|
||||
defaultMessage: 'Could not fetch date histogram information',
|
||||
})
|
||||
);
|
||||
}
|
||||
// the datemath plugin always parses dates by using the current default moment time zone.
|
||||
// to use the configured time zone, we are switching just for the bounds calculation.
|
||||
const defaultTimezone = moment().zoneName();
|
||||
moment.tz.setDefault(timeInfo.timeZone);
|
||||
|
||||
const timeBounds = timeInfo.timeRange && calculateBounds(timeInfo.timeRange);
|
||||
|
||||
const result = {
|
||||
...input,
|
||||
columns: resultColumns,
|
||||
rows: input.rows.map((row) => {
|
||||
const newRow = { ...row };
|
||||
|
||||
let startOfBucket = moment(row[dateColumnId]);
|
||||
let endOfBucket = startOfBucket.clone().add(intervalDuration);
|
||||
if (timeBounds && timeBounds.min) {
|
||||
startOfBucket = moment.max(startOfBucket, timeBounds.min);
|
||||
}
|
||||
if (timeBounds && timeBounds.max) {
|
||||
endOfBucket = moment.min(endOfBucket, timeBounds.max);
|
||||
}
|
||||
const bucketSize = endOfBucket.diff(startOfBucket);
|
||||
const factor = bucketSize / targetUnitInMs;
|
||||
|
||||
const currentValue = newRow[inputColumnId];
|
||||
if (currentValue != null) {
|
||||
newRow[outputColumnId] = Number(currentValue) / factor;
|
||||
}
|
||||
|
||||
return newRow;
|
||||
}),
|
||||
};
|
||||
// reset default moment timezone
|
||||
moment.tz.setDefault(defaultTimezone);
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export type TimeScaleUnit = 's' | 'm' | 'h' | 'd';
|
171
x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts
Normal file
171
x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type {
|
||||
ArgumentType,
|
||||
ExpressionFunctionDefinition,
|
||||
} from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export interface AxesSettingsConfig {
|
||||
x: boolean;
|
||||
yLeft: boolean;
|
||||
yRight: boolean;
|
||||
}
|
||||
|
||||
export interface AxisExtentConfig {
|
||||
mode: 'full' | 'dataBounds' | 'custom';
|
||||
lowerBound?: number;
|
||||
upperBound?: number;
|
||||
}
|
||||
|
||||
interface AxisConfig {
|
||||
title: string;
|
||||
hide?: boolean;
|
||||
}
|
||||
|
||||
export type YAxisMode = 'auto' | 'left' | 'right';
|
||||
|
||||
export interface YConfig {
|
||||
forAccessor: string;
|
||||
axisMode?: YAxisMode;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & {
|
||||
type: 'lens_xy_axisTitlesVisibilityConfig';
|
||||
};
|
||||
|
||||
export const axisTitlesVisibilityConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_axisTitlesVisibilityConfig',
|
||||
null,
|
||||
AxesSettingsConfig,
|
||||
AxisTitlesVisibilityConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_axisTitlesVisibilityConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_axisTitlesVisibilityConfig',
|
||||
help: `Configure the xy chart's axis titles appearance`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
x: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.xAxisTitle.help', {
|
||||
defaultMessage: 'Specifies whether or not the title of the x-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yLeft: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yLeftAxisTitle.help', {
|
||||
defaultMessage: 'Specifies whether or not the title of the left y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yRight: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yRightAxisTitle.help', {
|
||||
defaultMessage: 'Specifies whether or not the title of the right y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: AxesSettingsConfig) {
|
||||
return {
|
||||
type: 'lens_xy_axisTitlesVisibilityConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export type AxisExtentConfigResult = AxisExtentConfig & { type: 'lens_xy_axisExtentConfig' };
|
||||
|
||||
export const axisExtentConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_axisExtentConfig',
|
||||
null,
|
||||
AxisExtentConfig,
|
||||
AxisExtentConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_axisExtentConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_axisExtentConfig',
|
||||
help: `Configure the xy chart's axis extents`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
mode: {
|
||||
types: ['string'],
|
||||
options: ['full', 'dataBounds', 'custom'],
|
||||
help: i18n.translate('xpack.lens.xyChart.extentMode.help', {
|
||||
defaultMessage: 'The extent mode',
|
||||
}),
|
||||
},
|
||||
lowerBound: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.xyChart.extentMode.help', {
|
||||
defaultMessage: 'The extent mode',
|
||||
}),
|
||||
},
|
||||
upperBound: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.xyChart.extentMode.help', {
|
||||
defaultMessage: 'The extent mode',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: AxisExtentConfig) {
|
||||
return {
|
||||
type: 'lens_xy_axisExtentConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const axisConfig: { [key in keyof AxisConfig]: ArgumentType<AxisConfig[key]> } = {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.xyChart.title.help', {
|
||||
defaultMessage: 'The axis title',
|
||||
}),
|
||||
},
|
||||
hide: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Show / hide axis',
|
||||
},
|
||||
};
|
||||
|
||||
export type YConfigResult = YConfig & { type: 'lens_xy_yConfig' };
|
||||
|
||||
export const yAxisConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_yConfig',
|
||||
null,
|
||||
YConfig,
|
||||
YConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_yConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_yConfig',
|
||||
help: `Configure the behavior of a xy chart's y axis metric`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
forAccessor: {
|
||||
types: ['string'],
|
||||
help: 'The accessor this configuration is for',
|
||||
},
|
||||
axisMode: {
|
||||
types: ['string'],
|
||||
options: ['auto', 'left', 'right'],
|
||||
help: 'The axis mode of the metric',
|
||||
},
|
||||
color: {
|
||||
types: ['string'],
|
||||
help: 'The color of the series',
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: YConfig) {
|
||||
return {
|
||||
type: 'lens_xy_yConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export type FittingFunction = typeof fittingFunctionDefinitions[number]['id'];
|
||||
|
||||
export const fittingFunctionDefinitions = [
|
||||
{
|
||||
id: 'None',
|
||||
title: i18n.translate('xpack.lens.fittingFunctionsTitle.none', {
|
||||
defaultMessage: 'Hide',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.fittingFunctionsDescription.none', {
|
||||
defaultMessage: 'Do not fill gaps',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'Zero',
|
||||
title: i18n.translate('xpack.lens.fittingFunctionsTitle.zero', {
|
||||
defaultMessage: 'Zero',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.fittingFunctionsDescription.zero', {
|
||||
defaultMessage: 'Fill gaps with zeros',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'Linear',
|
||||
title: i18n.translate('xpack.lens.fittingFunctionsTitle.linear', {
|
||||
defaultMessage: 'Linear',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.fittingFunctionsDescription.linear', {
|
||||
defaultMessage: 'Fill gaps with a line',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'Carry',
|
||||
title: i18n.translate('xpack.lens.fittingFunctionsTitle.carry', {
|
||||
defaultMessage: 'Last',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.fittingFunctionsDescription.carry', {
|
||||
defaultMessage: 'Fill gaps with the last value',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'Lookahead',
|
||||
title: i18n.translate('xpack.lens.fittingFunctionsTitle.lookahead', {
|
||||
defaultMessage: 'Next',
|
||||
}),
|
||||
description: i18n.translate('xpack.lens.fittingFunctionsDescription.lookahead', {
|
||||
defaultMessage: 'Fill gaps with the next value',
|
||||
}),
|
||||
},
|
||||
] as const;
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { AxesSettingsConfig } from './axis_config';
|
||||
|
||||
export type GridlinesConfigResult = AxesSettingsConfig & { type: 'lens_xy_gridlinesConfig' };
|
||||
|
||||
export const gridlinesConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_gridlinesConfig',
|
||||
null,
|
||||
AxesSettingsConfig,
|
||||
GridlinesConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_gridlinesConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_gridlinesConfig',
|
||||
help: `Configure the xy chart's gridlines appearance`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
x: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.xAxisGridlines.help', {
|
||||
defaultMessage: 'Specifies whether or not the gridlines of the x-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yLeft: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yLeftAxisgridlines.help', {
|
||||
defaultMessage: 'Specifies whether or not the gridlines of the left y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yRight: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yRightAxisgridlines.help', {
|
||||
defaultMessage: 'Specifies whether or not the gridlines of the right y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: AxesSettingsConfig) {
|
||||
return {
|
||||
type: 'lens_xy_gridlinesConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
16
x-pack/plugins/lens/common/expressions/xy_chart/index.ts
Normal file
16
x-pack/plugins/lens/common/expressions/xy_chart/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './axis_config';
|
||||
export * from './fitting_function';
|
||||
export * from './grid_lines_config';
|
||||
export * from './layer_config';
|
||||
export * from './legend_config';
|
||||
export * from './series_type';
|
||||
export * from './tick_labels_config';
|
||||
export * from './xy_args';
|
||||
export * from './xy_chart';
|
120
x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts
Normal file
120
x-pack/plugins/lens/common/expressions/xy_chart/layer_config.ts
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PaletteOutput } from '../../../../../../src/plugins/charts/common';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import { axisConfig, YConfig } from './axis_config';
|
||||
import type { SeriesType } from './series_type';
|
||||
|
||||
export interface XYLayerConfig {
|
||||
hide?: boolean;
|
||||
layerId: string;
|
||||
xAccessor?: string;
|
||||
accessors: string[];
|
||||
yConfig?: YConfig[];
|
||||
seriesType: SeriesType;
|
||||
splitAccessor?: string;
|
||||
palette?: PaletteOutput;
|
||||
}
|
||||
|
||||
export interface ValidLayer extends XYLayerConfig {
|
||||
xAccessor: NonNullable<XYLayerConfig['xAccessor']>;
|
||||
}
|
||||
|
||||
export type LayerArgs = XYLayerConfig & {
|
||||
columnToLabel?: string; // Actually a JSON key-value pair
|
||||
yScaleType: 'time' | 'linear' | 'log' | 'sqrt';
|
||||
xScaleType: 'time' | 'linear' | 'ordinal';
|
||||
isHistogram: boolean;
|
||||
// palette will always be set on the expression
|
||||
palette: PaletteOutput;
|
||||
};
|
||||
|
||||
export type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' };
|
||||
|
||||
export const layerConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_layer',
|
||||
null,
|
||||
LayerArgs,
|
||||
LayerConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_layer',
|
||||
aliases: [],
|
||||
type: 'lens_xy_layer',
|
||||
help: `Configure a layer in the xy chart`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
...axisConfig,
|
||||
layerId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
xAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
seriesType: {
|
||||
types: ['string'],
|
||||
options: [
|
||||
'bar',
|
||||
'line',
|
||||
'area',
|
||||
'bar_stacked',
|
||||
'area_stacked',
|
||||
'bar_percentage_stacked',
|
||||
'area_percentage_stacked',
|
||||
],
|
||||
help: 'The type of chart to display.',
|
||||
},
|
||||
xScaleType: {
|
||||
options: ['ordinal', 'linear', 'time'],
|
||||
help: 'The scale type of the x axis',
|
||||
default: 'ordinal',
|
||||
},
|
||||
isHistogram: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: 'Whether to layout the chart as a histogram',
|
||||
},
|
||||
yScaleType: {
|
||||
options: ['log', 'sqrt', 'linear', 'time'],
|
||||
help: 'The scale type of the y axes',
|
||||
default: 'linear',
|
||||
},
|
||||
splitAccessor: {
|
||||
types: ['string'],
|
||||
help: 'The column to split by',
|
||||
multi: false,
|
||||
},
|
||||
accessors: {
|
||||
types: ['string'],
|
||||
help: 'The columns to display on the y axis.',
|
||||
multi: true,
|
||||
},
|
||||
yConfig: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
types: ['lens_xy_yConfig' as any],
|
||||
help: 'Additional configuration for y axes',
|
||||
multi: true,
|
||||
},
|
||||
columnToLabel: {
|
||||
types: ['string'],
|
||||
help: 'JSON key-value pairs of column ID to label',
|
||||
},
|
||||
palette: {
|
||||
default: `{theme "palette" default={system_palette name="default"} }`,
|
||||
help: '',
|
||||
types: ['palette'],
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: LayerArgs) {
|
||||
return {
|
||||
type: 'lens_xy_layer',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
110
x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts
Normal file
110
x-pack/plugins/lens/common/expressions/xy_chart/legend_config.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
|
||||
export interface LegendConfig {
|
||||
/**
|
||||
* Flag whether the legend should be shown. If there is just a single series, it will be hidden
|
||||
*/
|
||||
isVisible: boolean;
|
||||
/**
|
||||
* Position of the legend relative to the chart
|
||||
*/
|
||||
position: Position;
|
||||
/**
|
||||
* Flag whether the legend should be shown even with just a single series
|
||||
*/
|
||||
showSingleSeries?: boolean;
|
||||
/**
|
||||
* Flag whether the legend is inside the chart
|
||||
*/
|
||||
isInside?: boolean;
|
||||
/**
|
||||
* Horizontal Alignment of the legend when it is set inside chart
|
||||
*/
|
||||
horizontalAlignment?: HorizontalAlignment;
|
||||
/**
|
||||
* Vertical Alignment of the legend when it is set inside chart
|
||||
*/
|
||||
verticalAlignment?: VerticalAlignment;
|
||||
/**
|
||||
* Number of columns when legend is set inside chart
|
||||
*/
|
||||
floatingColumns?: number;
|
||||
}
|
||||
|
||||
export type LegendConfigResult = LegendConfig & { type: 'lens_xy_legendConfig' };
|
||||
|
||||
export const legendConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_legendConfig',
|
||||
null,
|
||||
LegendConfig,
|
||||
LegendConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_legendConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_legendConfig',
|
||||
help: `Configure the xy chart's legend`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
isVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.isVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the legend is visible.',
|
||||
}),
|
||||
},
|
||||
position: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
help: i18n.translate('xpack.lens.xyChart.position.help', {
|
||||
defaultMessage: 'Specifies the legend position.',
|
||||
}),
|
||||
},
|
||||
showSingleSeries: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.showSingleSeries.help', {
|
||||
defaultMessage: 'Specifies whether a legend with just a single entry should be shown',
|
||||
}),
|
||||
},
|
||||
isInside: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.isInside.help', {
|
||||
defaultMessage: 'Specifies whether a legend is inside the chart',
|
||||
}),
|
||||
},
|
||||
horizontalAlignment: {
|
||||
types: ['string'],
|
||||
options: [HorizontalAlignment.Right, HorizontalAlignment.Left],
|
||||
help: i18n.translate('xpack.lens.xyChart.horizontalAlignment.help', {
|
||||
defaultMessage:
|
||||
'Specifies the horizontal alignment of the legend when it is displayed inside chart.',
|
||||
}),
|
||||
},
|
||||
verticalAlignment: {
|
||||
types: ['string'],
|
||||
options: [VerticalAlignment.Top, VerticalAlignment.Bottom],
|
||||
help: i18n.translate('xpack.lens.xyChart.verticalAlignment.help', {
|
||||
defaultMessage:
|
||||
'Specifies the vertical alignment of the legend when it is displayed inside chart.',
|
||||
}),
|
||||
},
|
||||
floatingColumns: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.xyChart.floatingColumns.help', {
|
||||
defaultMessage: 'Specifies the number of columns when legend is displayed inside chart.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: LegendConfig) {
|
||||
return {
|
||||
type: 'lens_xy_legendConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export type SeriesType =
|
||||
| 'bar'
|
||||
| 'bar_horizontal'
|
||||
| 'line'
|
||||
| 'area'
|
||||
| 'bar_stacked'
|
||||
| 'bar_percentage_stacked'
|
||||
| 'bar_horizontal_stacked'
|
||||
| 'bar_horizontal_percentage_stacked'
|
||||
| 'area_stacked'
|
||||
| 'area_percentage_stacked';
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { AxesSettingsConfig } from './axis_config';
|
||||
|
||||
export type TickLabelsConfigResult = AxesSettingsConfig & { type: 'lens_xy_tickLabelsConfig' };
|
||||
|
||||
export const tickLabelsConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_tickLabelsConfig',
|
||||
null,
|
||||
AxesSettingsConfig,
|
||||
TickLabelsConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_tickLabelsConfig',
|
||||
aliases: [],
|
||||
type: 'lens_xy_tickLabelsConfig',
|
||||
help: `Configure the xy chart's tick labels appearance`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
x: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.xAxisTickLabels.help', {
|
||||
defaultMessage: 'Specifies whether or not the tick labels of the x-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yLeft: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yLeftAxisTickLabels.help', {
|
||||
defaultMessage: 'Specifies whether or not the tick labels of the left y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
yRight: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yRightAxisTickLabels.help', {
|
||||
defaultMessage: 'Specifies whether or not the tick labels of the right y-axis are visible.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: AxesSettingsConfig) {
|
||||
return {
|
||||
type: 'lens_xy_tickLabelsConfig',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
39
x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts
Normal file
39
x-pack/plugins/lens/common/expressions/xy_chart/xy_args.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config';
|
||||
import type { FittingFunction } from './fitting_function';
|
||||
import type { GridlinesConfigResult } from './grid_lines_config';
|
||||
import type { LayerArgs } from './layer_config';
|
||||
import type { LegendConfigResult } from './legend_config';
|
||||
import type { TickLabelsConfigResult } from './tick_labels_config';
|
||||
|
||||
export type ValueLabelConfig = 'hide' | 'inside' | 'outside';
|
||||
|
||||
export type XYCurveType = 'LINEAR' | 'CURVE_MONOTONE_X';
|
||||
|
||||
// Arguments to XY chart expression, with computed properties
|
||||
export interface XYArgs {
|
||||
title?: string;
|
||||
description?: string;
|
||||
xTitle: string;
|
||||
yTitle: string;
|
||||
yRightTitle: string;
|
||||
yLeftExtent: AxisExtentConfigResult;
|
||||
yRightExtent: AxisExtentConfigResult;
|
||||
legend: LegendConfigResult;
|
||||
valueLabels: ValueLabelConfig;
|
||||
layers: LayerArgs[];
|
||||
fittingFunction?: FittingFunction;
|
||||
axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult;
|
||||
tickLabelsVisibilitySettings?: TickLabelsConfigResult;
|
||||
gridlinesVisibilitySettings?: GridlinesConfigResult;
|
||||
curveType?: XYCurveType;
|
||||
fillOpacity?: number;
|
||||
hideEndzones?: boolean;
|
||||
valuesInLegend?: boolean;
|
||||
}
|
156
x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts
Normal file
156
x-pack/plugins/lens/common/expressions/xy_chart/xy_chart.ts
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { ExpressionValueSearchContext } from '../../../../../../src/plugins/data/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
import type { XYArgs } from './xy_args';
|
||||
import { fittingFunctionDefinitions } from './fitting_function';
|
||||
|
||||
export interface XYChartProps {
|
||||
data: LensMultiTable;
|
||||
args: XYArgs;
|
||||
}
|
||||
|
||||
export interface XYRender {
|
||||
type: 'render';
|
||||
as: 'lens_xy_chart_renderer';
|
||||
value: XYChartProps;
|
||||
}
|
||||
|
||||
export const xyChart: ExpressionFunctionDefinition<
|
||||
'lens_xy_chart',
|
||||
LensMultiTable | ExpressionValueSearchContext | null,
|
||||
XYArgs,
|
||||
XYRender
|
||||
> = {
|
||||
name: 'lens_xy_chart',
|
||||
type: 'render',
|
||||
inputTypes: ['lens_multitable', 'kibana_context', 'null'],
|
||||
help: i18n.translate('xpack.lens.xyChart.help', {
|
||||
defaultMessage: 'An X/Y chart',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: 'The chart title.',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
xTitle: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.xyChart.xTitle.help', {
|
||||
defaultMessage: 'X axis title',
|
||||
}),
|
||||
},
|
||||
yTitle: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yLeftTitle.help', {
|
||||
defaultMessage: 'Y left axis title',
|
||||
}),
|
||||
},
|
||||
yRightTitle: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yRightTitle.help', {
|
||||
defaultMessage: 'Y right axis title',
|
||||
}),
|
||||
},
|
||||
yLeftExtent: {
|
||||
types: ['lens_xy_axisExtentConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yLeftExtent.help', {
|
||||
defaultMessage: 'Y left axis extents',
|
||||
}),
|
||||
},
|
||||
yRightExtent: {
|
||||
types: ['lens_xy_axisExtentConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.yRightExtent.help', {
|
||||
defaultMessage: 'Y right axis extents',
|
||||
}),
|
||||
},
|
||||
legend: {
|
||||
types: ['lens_xy_legendConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.legend.help', {
|
||||
defaultMessage: 'Configure the chart legend.',
|
||||
}),
|
||||
},
|
||||
fittingFunction: {
|
||||
types: ['string'],
|
||||
options: [...fittingFunctionDefinitions.map(({ id }) => id)],
|
||||
help: i18n.translate('xpack.lens.xyChart.fittingFunction.help', {
|
||||
defaultMessage: 'Define how missing values are treated',
|
||||
}),
|
||||
},
|
||||
valueLabels: {
|
||||
types: ['string'],
|
||||
options: ['hide', 'inside'],
|
||||
help: '',
|
||||
},
|
||||
tickLabelsVisibilitySettings: {
|
||||
types: ['lens_xy_tickLabelsConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', {
|
||||
defaultMessage: 'Show x and y axes tick labels',
|
||||
}),
|
||||
},
|
||||
gridlinesVisibilitySettings: {
|
||||
types: ['lens_xy_gridlinesConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.gridlinesSettings.help', {
|
||||
defaultMessage: 'Show x and y axes gridlines',
|
||||
}),
|
||||
},
|
||||
axisTitlesVisibilitySettings: {
|
||||
types: ['lens_xy_axisTitlesVisibilityConfig'],
|
||||
help: i18n.translate('xpack.lens.xyChart.axisTitlesSettings.help', {
|
||||
defaultMessage: 'Show x and y axes titles',
|
||||
}),
|
||||
},
|
||||
layers: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
types: ['lens_xy_layer'] as any,
|
||||
help: 'Layers of visual series',
|
||||
multi: true,
|
||||
},
|
||||
curveType: {
|
||||
types: ['string'],
|
||||
options: ['LINEAR', 'CURVE_MONOTONE_X'],
|
||||
help: i18n.translate('xpack.lens.xyChart.curveType.help', {
|
||||
defaultMessage: 'Define how curve type is rendered for a line chart',
|
||||
}),
|
||||
},
|
||||
fillOpacity: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.xyChart.fillOpacity.help', {
|
||||
defaultMessage: 'Define the area chart fill opacity',
|
||||
}),
|
||||
},
|
||||
hideEndzones: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('xpack.lens.xyChart.hideEndzones.help', {
|
||||
defaultMessage: 'Hide endzone markers for partial data',
|
||||
}),
|
||||
},
|
||||
valuesInLegend: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('xpack.lens.xyChart.valuesInLegend.help', {
|
||||
defaultMessage: 'Show values in legend',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(data: LensMultiTable, args: XYArgs) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_xy_chart_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -8,3 +8,6 @@
|
|||
export * from './api';
|
||||
export * from './constants';
|
||||
export * from './types';
|
||||
|
||||
// Note: do not import the expression folder here or the page bundle will be bloated with all
|
||||
// the package
|
||||
|
|
|
@ -10,9 +10,9 @@ import {
|
|||
FieldFormat,
|
||||
FieldFormatInstanceType,
|
||||
KBN_FIELD_TYPES,
|
||||
} from '../../../../../src/plugins/data/public';
|
||||
import { FormatFactory } from '../types';
|
||||
import { TimeScaleUnit } from './time_scale';
|
||||
} from '../../../../../src/plugins/data/common';
|
||||
import type { FormatFactory } from '../types';
|
||||
import type { TimeScaleUnit } from '../expressions/time_scale';
|
||||
|
||||
const unitSuffixes: Record<TimeScaleUnit, string> = {
|
||||
s: i18n.translate('xpack.lens.fieldFormats.suffix.s', { defaultMessage: '/s' }),
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { FormatFactory } from '../types';
|
||||
import { getSuffixFormatter } from './suffix_formatter';
|
||||
import { getSuffixFormatter } from './index';
|
||||
|
||||
describe('suffix formatter', () => {
|
||||
it('should call nested formatter and apply suffix', () => {
|
|
@ -5,7 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FilterMeta, Filter } from 'src/plugins/data/common';
|
||||
import type { FilterMeta, Filter, IFieldFormat } from '../../../../src/plugins/data/common';
|
||||
import type { Datatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/common';
|
||||
|
||||
export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat;
|
||||
|
||||
export interface ExistingFields {
|
||||
indexPatternTitle: string;
|
||||
|
@ -24,3 +27,32 @@ export interface PersistableFilterMeta extends FilterMeta {
|
|||
export interface PersistableFilter extends Filter {
|
||||
meta: PersistableFilterMeta;
|
||||
}
|
||||
|
||||
export interface LensMultiTable {
|
||||
type: 'lens_multitable';
|
||||
tables: Record<string, Datatable>;
|
||||
dateRange?: {
|
||||
fromDate: Date;
|
||||
toDate: Date;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ColorStop {
|
||||
color: string;
|
||||
stop: number;
|
||||
}
|
||||
|
||||
export interface CustomPaletteParams {
|
||||
name?: string;
|
||||
reverse?: boolean;
|
||||
rangeType?: 'number' | 'percent';
|
||||
continuity?: 'above' | 'below' | 'all' | 'none';
|
||||
progression?: 'fixed';
|
||||
rangeMin?: number;
|
||||
rangeMax?: number;
|
||||
stops?: ColorStop[];
|
||||
colorStops?: ColorStop[];
|
||||
steps?: number;
|
||||
}
|
||||
|
||||
export type RequiredPaletteParamTypes = Required<CustomPaletteParams>;
|
||||
|
|
|
@ -14,7 +14,7 @@ import { Datatable } from 'src/plugins/expressions/public';
|
|||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { Args, ColumnConfigArg } from '../expression';
|
||||
import { DatatableArgs, ColumnConfigArg } from '../../../common/expressions';
|
||||
import { DataContextType } from './types';
|
||||
import { chartPluginMock } from 'src/plugins/charts/public/mocks';
|
||||
|
||||
|
@ -91,7 +91,7 @@ describe('datatable cell renderer', () => {
|
|||
const paletteRegistry = chartPluginMock.createPaletteRegistry();
|
||||
const customPalette = paletteRegistry.get('custom');
|
||||
|
||||
function getCellRenderer(columnConfig: Args) {
|
||||
function getCellRenderer(columnConfig: DatatableArgs) {
|
||||
return createGridCell(
|
||||
{
|
||||
a: { convert: (x) => `formatted ${x}` } as FieldFormat,
|
||||
|
@ -101,7 +101,7 @@ describe('datatable cell renderer', () => {
|
|||
({ get: jest.fn() } as unknown) as IUiSettingsClient
|
||||
);
|
||||
}
|
||||
function getColumnConfiguration(): Args {
|
||||
function getColumnConfiguration(): DatatableArgs {
|
||||
return {
|
||||
title: 'myData',
|
||||
columns: [
|
||||
|
@ -136,7 +136,10 @@ describe('datatable cell renderer', () => {
|
|||
});
|
||||
}
|
||||
|
||||
async function renderCellComponent(columnConfig: Args, context: Partial<DataContextType> = {}) {
|
||||
async function renderCellComponent(
|
||||
columnConfig: DatatableArgs,
|
||||
context: Partial<DataContextType> = {}
|
||||
) {
|
||||
const CellRendererWithPalette = getCellRenderer(columnConfig);
|
||||
const setCellProps = jest.fn();
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
import React, { useContext, useEffect } from 'react';
|
||||
import { EuiDataGridCellValueElementProps } from '@elastic/eui';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import type { FormatFactory } from '../../types';
|
||||
import type { FormatFactory } from '../../../common';
|
||||
import { getOriginalId } from '../../../common/expressions';
|
||||
import type { ColumnConfig } from '../../../common/expressions';
|
||||
import type { DataContextType } from './types';
|
||||
import { ColumnConfig } from './table_basic';
|
||||
import { getContrastColor, getNumericValue } from '../../shared_components/coloring/utils';
|
||||
import { getOriginalId } from '../transpose_helpers';
|
||||
|
||||
export const createGridCell = (
|
||||
formatters: Record<string, ReturnType<FormatFactory>>,
|
||||
|
|
|
@ -13,8 +13,8 @@ import {
|
|||
EuiListGroupItemProps,
|
||||
} from '@elastic/eui';
|
||||
import type { Datatable, DatatableColumn, DatatableColumnMeta } from 'src/plugins/expressions';
|
||||
import type { FormatFactory } from '../../types';
|
||||
import { ColumnConfig } from './table_basic';
|
||||
import type { FormatFactory } from '../../../common';
|
||||
import type { ColumnConfig } from '../../../common/expressions';
|
||||
|
||||
export const createGridColumns = (
|
||||
bucketColumns: string[],
|
||||
|
|
|
@ -21,8 +21,7 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import { VisualizationDimensionEditorProps } from '../../types';
|
||||
import { ColumnState, DatatableVisualizationState } from '../visualization';
|
||||
import { getOriginalId } from '../transpose_helpers';
|
||||
import { DatatableVisualizationState } from '../visualization';
|
||||
import {
|
||||
CustomizablePalette,
|
||||
applyPaletteParams,
|
||||
|
@ -33,13 +32,16 @@ import {
|
|||
PalettePanelContainer,
|
||||
findMinMaxByColumnId,
|
||||
} from '../../shared_components/';
|
||||
import './dimension_editor.scss';
|
||||
import type { ColumnState } from '../../../common/expressions';
|
||||
import {
|
||||
isNumericFieldForDatatable,
|
||||
getDefaultSummaryLabel,
|
||||
getFinalSummaryConfiguration,
|
||||
getSummaryRowOptions,
|
||||
} from '../summary';
|
||||
import { isNumericField } from '../utils';
|
||||
getOriginalId,
|
||||
} from '../../../common/expressions';
|
||||
|
||||
import './dimension_editor.scss';
|
||||
|
||||
const idPrefix = htmlIdGenerator()();
|
||||
|
||||
|
@ -93,7 +95,7 @@ export function TableDimensionEditor(
|
|||
const currentData = frame.activeData?.[state.layerId];
|
||||
|
||||
// either read config state or use same logic as chart itself
|
||||
const isNumeric = isNumericField(currentData, accessor);
|
||||
const isNumeric = isNumericFieldForDatatable(currentData, accessor);
|
||||
const currentAlignment = column?.alignment || (isNumeric ? 'right' : 'left');
|
||||
const currentColorMode = column?.colorMode || 'none';
|
||||
const hasDynamicColoring = currentColorMode !== 'none';
|
||||
|
|
|
@ -17,9 +17,8 @@ import {
|
|||
createGridHideHandler,
|
||||
createTransposeColumnFilterHandler,
|
||||
} from './table_actions';
|
||||
import { LensGridDirection } from './types';
|
||||
import { ColumnConfig } from './table_basic';
|
||||
import { LensMultiTable } from '../../types';
|
||||
import { LensMultiTable } from '../../../common';
|
||||
import { LensGridDirection, ColumnConfig } from '../../../common/expressions';
|
||||
|
||||
function getDefaultConfig(): ColumnConfig {
|
||||
return {
|
||||
|
|
|
@ -7,15 +7,11 @@
|
|||
|
||||
import type { EuiDataGridSorting } from '@elastic/eui';
|
||||
import type { Datatable, DatatableColumn } from 'src/plugins/expressions';
|
||||
import type { LensFilterEvent, LensMultiTable } from '../../types';
|
||||
import type {
|
||||
LensGridDirection,
|
||||
LensResizeAction,
|
||||
LensSortAction,
|
||||
LensToggleAction,
|
||||
} from './types';
|
||||
import { ColumnConfig } from './table_basic';
|
||||
import { getOriginalId } from '../transpose_helpers';
|
||||
import type { LensFilterEvent } from '../../types';
|
||||
import type { LensMultiTable } from '../../../common';
|
||||
import type { LensResizeAction, LensSortAction, LensToggleAction } from './types';
|
||||
import type { ColumnConfig, LensGridDirection } from '../../../common/expressions';
|
||||
import { getOriginalId } from '../../../common/expressions';
|
||||
|
||||
export const createGridResizeHandler = (
|
||||
columnConfig: ColumnConfig,
|
||||
|
|
|
@ -15,8 +15,8 @@ import { VisualizationContainer } from '../../visualization_container';
|
|||
import { EmptyPlaceholder } from '../../shared_components';
|
||||
import { LensIconChartDatatable } from '../../assets/chart_datatable';
|
||||
import { DataContext, DatatableComponent } from './table_basic';
|
||||
import { LensMultiTable } from '../../types';
|
||||
import { DatatableProps } from '../expression';
|
||||
import { LensMultiTable } from '../../../common';
|
||||
import { DatatableProps } from '../../../common/expressions';
|
||||
import { chartPluginMock } from 'src/plugins/charts/public/mocks';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
|
||||
|
|
|
@ -18,18 +18,17 @@ import {
|
|||
EuiDataGridSorting,
|
||||
EuiDataGridStyle,
|
||||
} from '@elastic/eui';
|
||||
import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common';
|
||||
import { FormatFactory, LensFilterEvent, LensTableRowContextMenuEvent } from '../../types';
|
||||
import type { LensFilterEvent, LensTableRowContextMenuEvent } from '../../types';
|
||||
import type { FormatFactory } from '../../../common';
|
||||
import { LensGridDirection } from '../../../common/expressions';
|
||||
import { VisualizationContainer } from '../../visualization_container';
|
||||
import { EmptyPlaceholder, findMinMaxByColumnId } from '../../shared_components';
|
||||
import { LensIconChartDatatable } from '../../assets/chart_datatable';
|
||||
import { ColumnState } from '../visualization';
|
||||
import {
|
||||
import type {
|
||||
DataContextType,
|
||||
DatatableRenderProps,
|
||||
LensSortAction,
|
||||
LensResizeAction,
|
||||
LensGridDirection,
|
||||
LensToggleAction,
|
||||
} from './types';
|
||||
import { createGridColumns } from './columns';
|
||||
|
@ -42,8 +41,7 @@ import {
|
|||
createTransposeColumnFilterHandler,
|
||||
} from './table_actions';
|
||||
import { CUSTOM_PALETTE } from '../../shared_components/coloring/constants';
|
||||
import { getFinalSummaryConfiguration } from '../summary';
|
||||
import { getOriginalId } from '../transpose_helpers';
|
||||
import { getOriginalId, getFinalSummaryConfiguration } from '../../../common/expressions';
|
||||
|
||||
export const DataContext = React.createContext<DataContextType>({});
|
||||
|
||||
|
@ -52,17 +50,6 @@ const gridStyle: EuiDataGridStyle = {
|
|||
header: 'underline',
|
||||
};
|
||||
|
||||
export interface ColumnConfig {
|
||||
columns: Array<
|
||||
Omit<ColumnState, 'palette'> & {
|
||||
type: 'lens_datatable_column';
|
||||
palette?: PaletteOutput<CustomPaletteState>;
|
||||
}
|
||||
>;
|
||||
sortingColumnId: string | undefined;
|
||||
sortingDirection: LensGridDirection;
|
||||
}
|
||||
|
||||
export const DatatableComponent = (props: DatatableRenderProps) => {
|
||||
const [firstTable] = Object.values(props.data.tables);
|
||||
|
||||
|
|
|
@ -5,16 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Direction } from '@elastic/eui';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { CustomPaletteState, PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import type { IAggType } from 'src/plugins/data/public';
|
||||
import type { Datatable, RenderMode } from 'src/plugins/expressions';
|
||||
import type { FormatFactory, ILensInterpreterRenderHandlers, LensEditEvent } from '../../types';
|
||||
import type { DatatableProps } from '../expression';
|
||||
import type { ILensInterpreterRenderHandlers, LensEditEvent } from '../../types';
|
||||
import { LENS_EDIT_SORT_ACTION, LENS_EDIT_RESIZE_ACTION, LENS_TOGGLE_ACTION } from './constants';
|
||||
|
||||
export type LensGridDirection = 'none' | Direction;
|
||||
import type { FormatFactory } from '../../../common';
|
||||
import type { DatatableProps, LensGridDirection } from '../../../common/expressions';
|
||||
|
||||
export interface LensSortActionData {
|
||||
columnId: string | undefined;
|
||||
|
@ -49,12 +47,6 @@ export type DatatableRenderProps = DatatableProps & {
|
|||
rowHasRowClickTriggerActions?: boolean[];
|
||||
};
|
||||
|
||||
export interface DatatableRender {
|
||||
type: 'render';
|
||||
as: 'lens_datatable_renderer';
|
||||
value: DatatableProps;
|
||||
}
|
||||
|
||||
export interface DataContextType {
|
||||
table?: Datatable;
|
||||
rowHasRowClickTriggerActions?: boolean[];
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DatatableProps, getDatatable } from './expression';
|
||||
import { LensMultiTable } from '../types';
|
||||
import { DatatableProps } from '../../common/expressions';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { IFieldFormat } from '../../../../../src/plugins/data/public';
|
||||
import { getDatatable } from './expression';
|
||||
|
||||
function sampleArgs() {
|
||||
const indexPatternId = 'indexPatternId';
|
||||
|
|
|
@ -7,194 +7,20 @@
|
|||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
|
||||
import type { IAggType } from 'src/plugins/data/public';
|
||||
import {
|
||||
DatatableColumnMeta,
|
||||
ExpressionFunctionDefinition,
|
||||
ExpressionRenderDefinition,
|
||||
} from 'src/plugins/expressions';
|
||||
import { CustomPaletteState, PaletteOutput } from 'src/plugins/charts/common';
|
||||
import { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { getSortingCriteria } from './sorting';
|
||||
|
||||
import { ExpressionRenderDefinition } from 'src/plugins/expressions';
|
||||
import { DatatableComponent } from './components/table_basic';
|
||||
import { ColumnState } from './visualization';
|
||||
|
||||
import type { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types';
|
||||
import type { DatatableRender } from './components/types';
|
||||
import { transposeTable } from './transpose_helpers';
|
||||
import { computeSummaryRowForColumn } from './summary';
|
||||
import type { ILensInterpreterRenderHandlers } from '../types';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import { DatatableProps } from '../../common/expressions';
|
||||
|
||||
export type ColumnConfigArg = Omit<ColumnState, 'palette'> & {
|
||||
type: 'lens_datatable_column';
|
||||
palette?: PaletteOutput<CustomPaletteState>;
|
||||
summaryRowValue?: unknown;
|
||||
};
|
||||
|
||||
export interface Args {
|
||||
title: string;
|
||||
description?: string;
|
||||
columns: ColumnConfigArg[];
|
||||
sortingColumnId: string | undefined;
|
||||
sortingDirection: 'asc' | 'desc' | 'none';
|
||||
}
|
||||
|
||||
export interface DatatableProps {
|
||||
data: LensMultiTable;
|
||||
untransposedData?: LensMultiTable;
|
||||
args: Args;
|
||||
}
|
||||
|
||||
function isRange(meta: { params?: { id?: string } } | undefined) {
|
||||
return meta?.params?.id === 'range';
|
||||
}
|
||||
|
||||
export const getDatatable = ({
|
||||
formatFactory,
|
||||
}: {
|
||||
formatFactory: FormatFactory;
|
||||
}): ExpressionFunctionDefinition<'lens_datatable', LensMultiTable, Args, DatatableRender> => ({
|
||||
name: 'lens_datatable',
|
||||
type: 'render',
|
||||
inputTypes: ['lens_multitable'],
|
||||
help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', {
|
||||
defaultMessage: 'Datatable renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.datatable.titleLabel', {
|
||||
defaultMessage: 'Title',
|
||||
}),
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
columns: {
|
||||
types: ['lens_datatable_column'],
|
||||
help: '',
|
||||
multi: true,
|
||||
},
|
||||
sortingColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
sortingDirection: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
},
|
||||
fn(data, args, context) {
|
||||
let untransposedData: LensMultiTable | undefined;
|
||||
// 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<string, ReturnType<FormatFactory>> = {};
|
||||
|
||||
firstTable.columns.forEach((column) => {
|
||||
formatters[column.id] = formatFactory(column.meta?.params);
|
||||
});
|
||||
|
||||
const hasTransposedColumns = args.columns.some((c) => c.isTransposed);
|
||||
if (hasTransposedColumns) {
|
||||
// store original shape of data separately
|
||||
untransposedData = cloneDeep(data);
|
||||
// transposes table and args inplace
|
||||
transposeTable(args, firstTable, formatters);
|
||||
}
|
||||
|
||||
const { sortingColumnId: sortBy, sortingDirection: sortDirection } = args;
|
||||
|
||||
const columnsReverseLookup = firstTable.columns.reduce<
|
||||
Record<string, { name: string; index: number; meta?: DatatableColumnMeta }>
|
||||
>((memo, { id, name, meta }, i) => {
|
||||
memo[id] = { name, index: i, meta };
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
const columnsWithSummary = args.columns.filter((c) => c.summaryRow);
|
||||
for (const column of columnsWithSummary) {
|
||||
column.summaryRowValue = computeSummaryRowForColumn(
|
||||
column,
|
||||
firstTable,
|
||||
formatters,
|
||||
formatFactory({ id: 'number' })
|
||||
);
|
||||
}
|
||||
|
||||
if (sortBy && columnsReverseLookup[sortBy] && sortDirection !== 'none') {
|
||||
// Sort on raw values for these types, while use the formatted value for the rest
|
||||
const sortingCriteria = getSortingCriteria(
|
||||
isRange(columnsReverseLookup[sortBy]?.meta)
|
||||
? 'range'
|
||||
: columnsReverseLookup[sortBy]?.meta?.type,
|
||||
sortBy,
|
||||
formatters[sortBy],
|
||||
sortDirection
|
||||
);
|
||||
// replace the table here
|
||||
context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || [])
|
||||
.slice()
|
||||
.sort(sortingCriteria);
|
||||
// replace also the local copy
|
||||
firstTable.rows = context.inspectorAdapters.tables[layerId].rows;
|
||||
} else {
|
||||
args.sortingColumnId = undefined;
|
||||
args.sortingDirection = 'none';
|
||||
}
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_datatable_renderer',
|
||||
value: {
|
||||
data,
|
||||
untransposedData,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
type DatatableColumnResult = ColumnState & { type: 'lens_datatable_column' };
|
||||
|
||||
export const datatableColumn: ExpressionFunctionDefinition<
|
||||
'lens_datatable_column',
|
||||
null,
|
||||
ColumnState,
|
||||
DatatableColumnResult
|
||||
> = {
|
||||
name: 'lens_datatable_column',
|
||||
aliases: [],
|
||||
type: 'lens_datatable_column',
|
||||
help: '',
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
columnId: { types: ['string'], help: '' },
|
||||
alignment: { types: ['string'], help: '' },
|
||||
hidden: { types: ['boolean'], help: '' },
|
||||
width: { types: ['number'], help: '' },
|
||||
isTransposed: { types: ['boolean'], help: '' },
|
||||
transposable: { types: ['boolean'], help: '' },
|
||||
colorMode: { types: ['string'], help: '' },
|
||||
palette: {
|
||||
types: ['palette'],
|
||||
help: '',
|
||||
},
|
||||
summaryRow: { types: ['string'], help: '' },
|
||||
summaryLabel: { types: ['string'], help: '' },
|
||||
},
|
||||
fn: function fn(input: unknown, args: ColumnState) {
|
||||
return {
|
||||
type: 'lens_datatable_column',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
||||
export { datatableColumn, getDatatable } from '../../common/expressions';
|
||||
|
||||
export const getDatatableRenderer = (dependencies: {
|
||||
formatFactory: FormatFactory;
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { ChartsPluginSetup } from 'src/plugins/charts/public';
|
||||
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
import type { CoreSetup } from 'kibana/public';
|
||||
import type { ChartsPluginSetup } from 'src/plugins/charts/public';
|
||||
import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import type { EditorFrameSetup } from '../types';
|
||||
import type { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
import type { FormatFactory } from '../../common';
|
||||
|
||||
interface DatatableVisualizationPluginStartPlugins {
|
||||
data: DataPublicPluginStart;
|
||||
|
|
|
@ -10,9 +10,8 @@ import { render } from 'react-dom';
|
|||
import { Ast } from '@kbn/interpreter/common';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DatatableColumn } from 'src/plugins/expressions/public';
|
||||
import { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import {
|
||||
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import type {
|
||||
SuggestionRequest,
|
||||
Visualization,
|
||||
VisualizationSuggestion,
|
||||
|
@ -21,32 +20,9 @@ import {
|
|||
import { LensIconChartDatatable } from '../assets/chart_datatable';
|
||||
import { TableDimensionEditor } from './components/dimension_editor';
|
||||
import { CUSTOM_PALETTE } from '../shared_components/coloring/constants';
|
||||
import { CustomPaletteParams } from '../shared_components/coloring/types';
|
||||
import { getStopsForFixedMode } from '../shared_components';
|
||||
import { getDefaultSummaryLabel } from './summary';
|
||||
|
||||
export interface ColumnState {
|
||||
columnId: string;
|
||||
width?: number;
|
||||
hidden?: boolean;
|
||||
isTransposed?: boolean;
|
||||
// These flags are necessary to transpose columns and map them back later
|
||||
// They are set automatically and are not user-editable
|
||||
transposable?: boolean;
|
||||
originalColumnId?: string;
|
||||
originalName?: string;
|
||||
bucketValues?: Array<{ originalBucketColumn: DatatableColumn; value: unknown }>;
|
||||
alignment?: 'left' | 'right' | 'center';
|
||||
palette?: PaletteOutput<CustomPaletteParams>;
|
||||
colorMode?: 'none' | 'cell' | 'text';
|
||||
summaryRow?: 'none' | 'sum' | 'avg' | 'count' | 'min' | 'max';
|
||||
summaryLabel?: string;
|
||||
}
|
||||
|
||||
export interface SortingState {
|
||||
columnId: string | undefined;
|
||||
direction: 'asc' | 'desc' | 'none';
|
||||
}
|
||||
import { getDefaultSummaryLabel } from '../../common/expressions';
|
||||
import type { ColumnState, SortingState } from '../../common/expressions';
|
||||
|
||||
export interface DatatableVisualizationState {
|
||||
columns: ColumnState[];
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
EditorFrameStart,
|
||||
} from '../types';
|
||||
import { Document } from '../persistence/saved_object_store';
|
||||
import { mergeTables } from './merge_tables';
|
||||
import { mergeTables } from '../../common/expressions';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import { DashboardStart } from '../../../../../src/plugins/dashboard/public';
|
||||
|
|
|
@ -23,9 +23,8 @@ import type { LensByReferenceInput, LensByValueInput } from './embeddable';
|
|||
import type { Document } from '../persistence';
|
||||
import type { IndexPatternPersistedState } from '../indexpattern_datasource/types';
|
||||
import type { XYState } from '../xy_visualization/types';
|
||||
import type { PieVisualizationState } from '../pie_visualization/types';
|
||||
import type { PieVisualizationState, MetricState } from '../../common/expressions';
|
||||
import type { DatatableVisualizationState } from '../datatable_visualization/visualization';
|
||||
import type { MetricState } from '../metric_visualization/types';
|
||||
|
||||
type LensAttributes<TVisType, TVisState> = Omit<
|
||||
Document,
|
||||
|
|
|
@ -16,11 +16,11 @@ import {
|
|||
ScaleType,
|
||||
Settings,
|
||||
} from '@elastic/charts';
|
||||
import { CustomPaletteState } from 'src/plugins/charts/public';
|
||||
import type { CustomPaletteState } from 'src/plugins/charts/public';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { HeatmapRenderProps } from './types';
|
||||
import type { HeatmapRenderProps } from './types';
|
||||
import './index.scss';
|
||||
import { LensBrushEvent, LensFilterEvent } from '../types';
|
||||
import type { LensBrushEvent, LensFilterEvent } from '../types';
|
||||
import {
|
||||
applyPaletteParams,
|
||||
defaultPaletteParams,
|
||||
|
|
|
@ -14,8 +14,8 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import { VisualizationDimensionEditorProps } from '../types';
|
||||
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import type { VisualizationDimensionEditorProps } from '../types';
|
||||
import {
|
||||
CustomizablePalette,
|
||||
FIXED_PROGRESSION,
|
||||
|
@ -23,7 +23,7 @@ import {
|
|||
PalettePanelContainer,
|
||||
} from '../shared_components/';
|
||||
import './dimension_editor.scss';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
import { getSafePaletteParams } from './utils';
|
||||
|
||||
export function HeatmapDimensionEditor(
|
||||
|
|
|
@ -9,221 +9,15 @@ import { i18n } from '@kbn/i18n';
|
|||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React from 'react';
|
||||
import { Position } from '@elastic/charts';
|
||||
import {
|
||||
ExpressionFunctionDefinition,
|
||||
IInterpreterRenderHandlers,
|
||||
} from '../../../../../src/plugins/expressions';
|
||||
import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types';
|
||||
import {
|
||||
FUNCTION_NAME,
|
||||
HEATMAP_GRID_FUNCTION,
|
||||
LEGEND_FUNCTION,
|
||||
LENS_HEATMAP_RENDERER,
|
||||
} from './constants';
|
||||
import type {
|
||||
HeatmapExpressionArgs,
|
||||
HeatmapExpressionProps,
|
||||
HeatmapGridConfig,
|
||||
HeatmapGridConfigResult,
|
||||
HeatmapRender,
|
||||
LegendConfigResult,
|
||||
} from './types';
|
||||
import { HeatmapLegendConfig } from './types';
|
||||
import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import type { IInterpreterRenderHandlers } from '../../../../../src/plugins/expressions';
|
||||
import type { LensBrushEvent, LensFilterEvent } from '../types';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import { LENS_HEATMAP_RENDERER } from './constants';
|
||||
import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import { HeatmapChartReportable } from './chart_component';
|
||||
import type { HeatmapExpressionProps } from './types';
|
||||
|
||||
export const heatmapGridConfig: ExpressionFunctionDefinition<
|
||||
typeof HEATMAP_GRID_FUNCTION,
|
||||
null,
|
||||
HeatmapGridConfig,
|
||||
HeatmapGridConfigResult
|
||||
> = {
|
||||
name: HEATMAP_GRID_FUNCTION,
|
||||
aliases: [],
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
help: `Configure the heatmap layout `,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
// grid
|
||||
strokeWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.strokeWidth.help', {
|
||||
defaultMessage: 'Specifies the grid stroke width',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
strokeColor: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.strokeColor.help', {
|
||||
defaultMessage: 'Specifies the grid stroke color',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
cellHeight: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.cellHeight.help', {
|
||||
defaultMessage: 'Specifies the grid cell height',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
cellWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.cellWidth.help', {
|
||||
defaultMessage: 'Specifies the grid cell width',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
// cells
|
||||
isCellLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isCellLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the cell label is visible.',
|
||||
}),
|
||||
},
|
||||
// Y-axis
|
||||
isYAxisLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isYAxisLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the Y-axis labels are visible.',
|
||||
}),
|
||||
},
|
||||
yAxisLabelWidth: {
|
||||
types: ['number'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelWidth.help', {
|
||||
defaultMessage: 'Specifies the width of the Y-axis labels.',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
yAxisLabelColor: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.yAxisLabelColor.help', {
|
||||
defaultMessage: 'Specifies the color of the Y-axis labels.',
|
||||
}),
|
||||
required: false,
|
||||
},
|
||||
// X-axis
|
||||
isXAxisLabelVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.config.isXAxisLabelVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the X-axis labels are visible.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
return {
|
||||
type: HEATMAP_GRID_FUNCTION,
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO check if it's possible to make a shared function
|
||||
* based on the XY chart
|
||||
*/
|
||||
export const heatmapLegendConfig: ExpressionFunctionDefinition<
|
||||
typeof LEGEND_FUNCTION,
|
||||
null,
|
||||
HeatmapLegendConfig,
|
||||
LegendConfigResult
|
||||
> = {
|
||||
name: LEGEND_FUNCTION,
|
||||
aliases: [],
|
||||
type: LEGEND_FUNCTION,
|
||||
help: `Configure the heatmap chart's legend`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
isVisible: {
|
||||
types: ['boolean'],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.isVisible.help', {
|
||||
defaultMessage: 'Specifies whether or not the legend is visible.',
|
||||
}),
|
||||
},
|
||||
position: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.position.help', {
|
||||
defaultMessage: 'Specifies the legend position.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
fn(input, args) {
|
||||
return {
|
||||
type: LEGEND_FUNCTION,
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const heatmap: ExpressionFunctionDefinition<
|
||||
typeof FUNCTION_NAME,
|
||||
LensMultiTable,
|
||||
HeatmapExpressionArgs,
|
||||
HeatmapRender
|
||||
> = {
|
||||
name: FUNCTION_NAME,
|
||||
type: 'render',
|
||||
help: i18n.translate('xpack.lens.heatmap.expressionHelpLabel', {
|
||||
defaultMessage: 'Heatmap renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.heatmap.titleLabel', {
|
||||
defaultMessage: 'Title',
|
||||
}),
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
xAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
yAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
valueAccessor: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
shape: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
palette: {
|
||||
default: `{theme "palette" default={system_palette name="default"} }`,
|
||||
help: '',
|
||||
types: ['palette'],
|
||||
},
|
||||
legend: {
|
||||
types: [LEGEND_FUNCTION],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.legend.help', {
|
||||
defaultMessage: 'Configure the chart legend.',
|
||||
}),
|
||||
},
|
||||
gridConfig: {
|
||||
types: [HEATMAP_GRID_FUNCTION],
|
||||
help: i18n.translate('xpack.lens.heatmapChart.gridConfig.help', {
|
||||
defaultMessage: 'Configure the heatmap layout.',
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data: LensMultiTable, args: HeatmapExpressionArgs) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: LENS_HEATMAP_RENDERER,
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
export { heatmapGridConfig, heatmapLegendConfig, heatmap } from '../../common/expressions';
|
||||
|
||||
export const getHeatmapRenderer = (dependencies: {
|
||||
formatFactory: Promise<FormatFactory>;
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import type { CoreSetup } from 'kibana/public';
|
||||
import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import type { EditorFrameSetup } from '../types';
|
||||
import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import { getTimeZone } from '../utils';
|
||||
import type { FormatFactory } from '../../common';
|
||||
|
||||
export interface HeatmapVisualizationPluginSetupPlugins {
|
||||
expressions: ExpressionsSetup;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getSuggestions } from './suggestions';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import { HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { getSuggestions } from './suggestions';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
import { HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants';
|
||||
|
||||
describe('heatmap suggestions', () => {
|
||||
describe('rejects suggestions', () => {
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import { partition } from 'lodash';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Visualization } from '../types';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import type { Visualization } from '../types';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
import { CHART_SHAPES, HEATMAP_GRID_FUNCTION, LEGEND_FUNCTION } from './constants';
|
||||
|
||||
export const getSuggestions: Visualization<HeatmapVisualizationState>['getSuggestions'] = ({
|
||||
|
|
|
@ -9,9 +9,9 @@ import React, { memo } from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VisualizationToolbarProps } from '../types';
|
||||
import type { VisualizationToolbarProps } from '../types';
|
||||
import { LegendSettingsPopover } from '../shared_components';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
|
||||
const legendOptions: Array<{ id: string; value: 'auto' | 'show' | 'hide'; label: string }> = [
|
||||
{
|
||||
|
|
|
@ -5,17 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Position } from '@elastic/charts';
|
||||
import { PaletteOutput } from '../../../../../src/plugins/charts/common';
|
||||
import { FormatFactory, LensBrushEvent, LensFilterEvent, LensMultiTable } from '../types';
|
||||
import {
|
||||
CHART_SHAPES,
|
||||
HEATMAP_GRID_FUNCTION,
|
||||
LEGEND_FUNCTION,
|
||||
LENS_HEATMAP_RENDERER,
|
||||
} from './constants';
|
||||
import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import { CustomPaletteParams } from '../shared_components';
|
||||
import type { PaletteOutput } from '../../../../../src/plugins/charts/common';
|
||||
import type { LensBrushEvent, LensFilterEvent } from '../types';
|
||||
import type { LensMultiTable, FormatFactory, CustomPaletteParams } from '../../common';
|
||||
import type { HeatmapGridConfigResult, HeatmapLegendConfigResult } from '../../common/expressions';
|
||||
import { CHART_SHAPES, LENS_HEATMAP_RENDERER } from './constants';
|
||||
import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
|
||||
export type ChartShapes = typeof CHART_SHAPES[keyof typeof CHART_SHAPES];
|
||||
|
||||
|
@ -24,7 +19,7 @@ export interface SharedHeatmapLayerState {
|
|||
xAccessor?: string;
|
||||
yAccessor?: string;
|
||||
valueAccessor?: string;
|
||||
legend: LegendConfigResult;
|
||||
legend: HeatmapLegendConfigResult;
|
||||
gridConfig: HeatmapGridConfigResult;
|
||||
}
|
||||
|
||||
|
@ -62,34 +57,3 @@ export type HeatmapRenderProps = HeatmapExpressionProps & {
|
|||
onSelectRange: (data: LensBrushEvent['data']) => void;
|
||||
paletteService: PaletteRegistry;
|
||||
};
|
||||
|
||||
export interface HeatmapLegendConfig {
|
||||
/**
|
||||
* Flag whether the legend should be shown. If there is just a single series, it will be hidden
|
||||
*/
|
||||
isVisible: boolean;
|
||||
/**
|
||||
* Position of the legend relative to the chart
|
||||
*/
|
||||
position: Position;
|
||||
}
|
||||
|
||||
export type LegendConfigResult = HeatmapLegendConfig & { type: typeof LEGEND_FUNCTION };
|
||||
|
||||
export interface HeatmapGridConfig {
|
||||
// grid
|
||||
strokeWidth?: number;
|
||||
strokeColor?: string;
|
||||
cellHeight?: number;
|
||||
cellWidth?: number;
|
||||
// cells
|
||||
isCellLabelVisible: boolean;
|
||||
// Y-axis
|
||||
isYAxisLabelVisible: boolean;
|
||||
yAxisLabelWidth?: number;
|
||||
yAxisLabelColor?: string;
|
||||
// X-axis
|
||||
isXAxisLabelVisible: boolean;
|
||||
}
|
||||
|
||||
export type HeatmapGridConfigResult = HeatmapGridConfig & { type: typeof HEATMAP_GRID_FUNCTION };
|
||||
|
|
|
@ -19,8 +19,8 @@ import {
|
|||
LEGEND_FUNCTION,
|
||||
} from './constants';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import { DatasourcePublicAPI, Operation } from '../types';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
import type { DatasourcePublicAPI, Operation } from '../types';
|
||||
import { chartPluginMock } from 'src/plugins/charts/public/mocks';
|
||||
|
||||
function exampleState(): HeatmapVisualizationState {
|
||||
|
|
|
@ -12,8 +12,8 @@ import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
|
|||
import { Ast } from '@kbn/interpreter/common';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import { OperationMetadata, Visualization } from '../types';
|
||||
import { HeatmapVisualizationState } from './types';
|
||||
import type { OperationMetadata, Visualization } from '../types';
|
||||
import type { HeatmapVisualizationState } from './types';
|
||||
import { getSuggestions } from './suggestions';
|
||||
import {
|
||||
CHART_NAMES,
|
||||
|
@ -27,9 +27,10 @@ import {
|
|||
} from './constants';
|
||||
import { HeatmapToolbar } from './toolbar_component';
|
||||
import { LensIconChartHeatmap } from '../assets/chart_heatmap';
|
||||
import { CustomPaletteParams, CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components';
|
||||
import { CUSTOM_PALETTE, getStopsForFixedMode } from '../shared_components';
|
||||
import { HeatmapDimensionEditor } from './dimension_editor';
|
||||
import { getSafePaletteParams } from './utils';
|
||||
import type { CustomPaletteParams } from '../../common';
|
||||
|
||||
const groupLabelForBar = i18n.translate('xpack.lens.heatmapVisualization.heatmapGroupLabel', {
|
||||
defaultMessage: 'Heatmap',
|
||||
|
|
|
@ -11,8 +11,13 @@ export type {
|
|||
EmbeddableComponentProps,
|
||||
TypedLensByValueInput,
|
||||
} from './embeddable/embeddable_component';
|
||||
export type { XYState } from './xy_visualization/types';
|
||||
export type { DataType, OperationMetadata } from './types';
|
||||
export type {
|
||||
XYState,
|
||||
PieVisualizationState,
|
||||
PieLayerState,
|
||||
SharedPieLayerState,
|
||||
MetricState,
|
||||
AxesSettingsConfig,
|
||||
XYLayerConfig,
|
||||
LegendConfig,
|
||||
|
@ -21,15 +26,8 @@ export type {
|
|||
YAxisMode,
|
||||
XYCurveType,
|
||||
YConfig,
|
||||
} from './xy_visualization/types';
|
||||
export type { DataType, OperationMetadata } from './types';
|
||||
export type {
|
||||
PieVisualizationState,
|
||||
PieLayerState,
|
||||
SharedPieLayerState,
|
||||
} from './pie_visualization/types';
|
||||
} from '../common/expressions';
|
||||
export type { DatatableVisualizationState } from './datatable_visualization/visualization';
|
||||
export type { MetricState } from './metric_visualization/types';
|
||||
export type {
|
||||
IndexPatternPersistedState,
|
||||
PersistedIndexPatternLayer,
|
||||
|
|
|
@ -16,7 +16,7 @@ import { IndexPatternColumn } from '../indexpattern';
|
|||
import { isColumnInvalid } from '../utils';
|
||||
import { IndexPatternPrivateState } from '../types';
|
||||
import { DimensionEditor } from './dimension_editor';
|
||||
import { DateRange } from '../../../common';
|
||||
import type { DateRange } from '../../../common';
|
||||
import { getOperationSupportMatrix } from './operation_support';
|
||||
|
||||
export type IndexPatternDimensionTriggerProps = DatasourceDimensionTriggerProps<IndexPatternPrivateState> & {
|
||||
|
|
|
@ -15,8 +15,8 @@ import {
|
|||
IndexPatternColumn,
|
||||
operationDefinitionMap,
|
||||
} from '../operations';
|
||||
import { unitSuffixesLong } from '../suffix_formatter';
|
||||
import { TimeScaleUnit } from '../time_scale';
|
||||
import type { TimeScaleUnit } from '../../../common/expressions';
|
||||
import { unitSuffixesLong } from '../../../common/suffix_formatter';
|
||||
import { IndexPatternLayer } from '../types';
|
||||
|
||||
export function setTimeScaling(
|
||||
|
|
|
@ -43,14 +43,14 @@ export class IndexPatternDatasource {
|
|||
renameColumns,
|
||||
formatColumn,
|
||||
counterRate,
|
||||
getTimeScaleFunction,
|
||||
timeScale,
|
||||
getSuffixFormatter,
|
||||
} = await import('../async_services');
|
||||
return core
|
||||
.getStartServices()
|
||||
.then(([coreStart, { data, indexPatternFieldEditor, uiActions }]) => {
|
||||
data.fieldFormats.register([getSuffixFormatter(data.fieldFormats.deserialize)]);
|
||||
expressions.registerFunction(getTimeScaleFunction(data));
|
||||
expressions.registerFunction(timeScale);
|
||||
expressions.registerFunction(counterRate);
|
||||
expressions.registerFunction(renameColumns);
|
||||
expressions.registerFunction(formatColumn);
|
||||
|
|
|
@ -69,11 +69,15 @@ export function columnToOperation(column: IndexPatternColumn, uniqueLabel?: stri
|
|||
};
|
||||
}
|
||||
|
||||
export * from './rename_columns';
|
||||
export * from './format_column';
|
||||
export * from './time_scale';
|
||||
export * from './counter_rate';
|
||||
export * from './suffix_formatter';
|
||||
export {
|
||||
CounterRateArgs,
|
||||
ExpressionFunctionCounterRate,
|
||||
counterRate,
|
||||
} from '../../common/expressions';
|
||||
export { FormatColumnArgs, supportedFormats, formatColumn } from '../../common/expressions';
|
||||
export { getSuffixFormatter, unitSuffixesLong } from '../../common/suffix_formatter';
|
||||
export { timeScale, TimeScaleArgs } from '../../common/expressions';
|
||||
export { renameColumns } from '../../common/expressions';
|
||||
|
||||
export function getIndexPatternDatasource({
|
||||
core,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { ExpressionFunctionAST } from '@kbn/interpreter/common';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import type { TimeScaleUnit } from '../../../time_scale';
|
||||
import type { TimeScaleUnit } from '../../../../../common/expressions';
|
||||
import type { IndexPattern, IndexPatternLayer } from '../../../types';
|
||||
import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils';
|
||||
import type { ReferenceBasedIndexPatternColumn } from '../column_types';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { Query } from 'src/plugins/data/public';
|
||||
import type { Operation } from '../../../types';
|
||||
import { TimeScaleUnit } from '../../time_scale';
|
||||
import type { TimeScaleUnit } from '../../../../common/expressions';
|
||||
import type { OperationType } from '../definitions';
|
||||
|
||||
export interface BaseIndexPatternColumn extends Operation {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { RangeEditor } from './range_editor';
|
|||
import { OperationDefinition } from '../index';
|
||||
import { FieldBasedIndexPatternColumn } from '../column_types';
|
||||
import { updateColumnParam } from '../../layer_helpers';
|
||||
import { supportedFormats } from '../../../format_column';
|
||||
import { supportedFormats } from '../../../../../common/expressions';
|
||||
import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants';
|
||||
import { IndexPattern, IndexPatternField } from '../../../types';
|
||||
import { getInvalidFieldMessage, isValidNumber } from '../helpers';
|
||||
|
|
|
@ -32,7 +32,7 @@ import { getSortScoreByPriority } from './operations';
|
|||
import { generateId } from '../../id_generator';
|
||||
import { ReferenceBasedIndexPatternColumn } from './definitions/column_types';
|
||||
import { FormulaIndexPatternColumn, regenerateLayerFromAst } from './definitions/formula';
|
||||
import { TimeScaleUnit } from '../time_scale';
|
||||
import type { TimeScaleUnit } from '../../../common/expressions';
|
||||
|
||||
interface ColumnAdvancedParams {
|
||||
filter?: Query | undefined;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { IndexPatternLayer } from '../types';
|
||||
import type { TimeScaleUnit } from '../time_scale';
|
||||
import type { TimeScaleUnit } from '../../../common/expressions';
|
||||
import type { IndexPatternColumn } from './definitions';
|
||||
import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange } from './time_scale_utils';
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { unitSuffixesLong } from '../suffix_formatter';
|
||||
import type { TimeScaleUnit } from '../time_scale';
|
||||
import { unitSuffixesLong } from '../../../common/suffix_formatter';
|
||||
import type { TimeScaleUnit } from '../../../common/expressions';
|
||||
import type { IndexPatternLayer } from '../types';
|
||||
import type { IndexPatternColumn } from './definitions';
|
||||
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public';
|
||||
import { DataPublicPluginStart } from 'src/plugins/data/public';
|
||||
import { search } from '../../../../../src/plugins/data/public';
|
||||
import { buildResultColumns } from '../../../../../src/plugins/expressions/common';
|
||||
|
||||
export type TimeScaleUnit = 's' | 'm' | 'h' | 'd';
|
||||
|
||||
export interface TimeScaleArgs {
|
||||
dateColumnId: string;
|
||||
inputColumnId: string;
|
||||
outputColumnId: string;
|
||||
targetUnit: TimeScaleUnit;
|
||||
outputColumnName?: string;
|
||||
}
|
||||
|
||||
const unitInMs: Record<TimeScaleUnit, number> = {
|
||||
s: 1000,
|
||||
m: 1000 * 60,
|
||||
h: 1000 * 60 * 60,
|
||||
d: 1000 * 60 * 60 * 24,
|
||||
};
|
||||
|
||||
export function getTimeScaleFunction(data: DataPublicPluginStart) {
|
||||
const timeScale: ExpressionFunctionDefinition<
|
||||
'lens_time_scale',
|
||||
Datatable,
|
||||
TimeScaleArgs,
|
||||
Promise<Datatable>
|
||||
> = {
|
||||
name: 'lens_time_scale',
|
||||
type: 'datatable',
|
||||
help: '',
|
||||
args: {
|
||||
dateColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
inputColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
outputColumnId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
outputColumnName: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
targetUnit: {
|
||||
types: ['string'],
|
||||
options: ['s', 'm', 'h', 'd'],
|
||||
help: '',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
inputTypes: ['datatable'],
|
||||
async fn(
|
||||
input,
|
||||
{ dateColumnId, inputColumnId, outputColumnId, outputColumnName, targetUnit }: TimeScaleArgs
|
||||
) {
|
||||
const dateColumnDefinition = input.columns.find((column) => column.id === dateColumnId);
|
||||
|
||||
if (!dateColumnDefinition) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.lens.functions.timeScale.dateColumnMissingMessage', {
|
||||
defaultMessage: 'Specified dateColumnId {columnId} does not exist.',
|
||||
values: {
|
||||
columnId: dateColumnId,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const resultColumns = buildResultColumns(
|
||||
input,
|
||||
outputColumnId,
|
||||
inputColumnId,
|
||||
outputColumnName,
|
||||
{ allowColumnOverwrite: true }
|
||||
);
|
||||
|
||||
if (!resultColumns) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const targetUnitInMs = unitInMs[targetUnit];
|
||||
const timeInfo = search.aggs.getDateHistogramMetaDataByDatatableColumn(dateColumnDefinition);
|
||||
const intervalDuration = timeInfo?.interval && search.aggs.parseInterval(timeInfo.interval);
|
||||
|
||||
if (!timeInfo || !intervalDuration) {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.lens.functions.timeScale.timeInfoMissingMessage', {
|
||||
defaultMessage: 'Could not fetch date histogram information',
|
||||
})
|
||||
);
|
||||
}
|
||||
// the datemath plugin always parses dates by using the current default moment time zone.
|
||||
// to use the configured time zone, we are switching just for the bounds calculation.
|
||||
const defaultTimezone = moment().zoneName();
|
||||
moment.tz.setDefault(timeInfo.timeZone);
|
||||
|
||||
const timeBounds =
|
||||
timeInfo.timeRange && data.query.timefilter.timefilter.calculateBounds(timeInfo.timeRange);
|
||||
|
||||
const result = {
|
||||
...input,
|
||||
columns: resultColumns,
|
||||
rows: input.rows.map((row) => {
|
||||
const newRow = { ...row };
|
||||
|
||||
let startOfBucket = moment(row[dateColumnId]);
|
||||
let endOfBucket = startOfBucket.clone().add(intervalDuration);
|
||||
if (timeBounds && timeBounds.min) {
|
||||
startOfBucket = moment.max(startOfBucket, timeBounds.min);
|
||||
}
|
||||
if (timeBounds && timeBounds.max) {
|
||||
endOfBucket = moment.min(endOfBucket, timeBounds.max);
|
||||
}
|
||||
const bucketSize = endOfBucket.diff(startOfBucket);
|
||||
const factor = bucketSize / targetUnitInMs;
|
||||
|
||||
const currentValue = newRow[inputColumnId];
|
||||
if (currentValue != null) {
|
||||
newRow[outputColumnId] = Number(currentValue) / factor;
|
||||
}
|
||||
|
||||
return newRow;
|
||||
}),
|
||||
};
|
||||
// reset default moment timezone
|
||||
moment.tz.setDefault(defaultTimezone);
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
return timeScale;
|
||||
}
|
|
@ -22,9 +22,10 @@ import {
|
|||
import { IndexPatternColumn } from './indexpattern';
|
||||
import { operationDefinitionMap } from './operations';
|
||||
import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types';
|
||||
import { OriginalColumn } from './rename_columns';
|
||||
import { dateHistogramOperation } from './operations/definitions';
|
||||
|
||||
type OriginalColumn = { id: string } & IndexPatternColumn;
|
||||
|
||||
function getExpressionForLayer(
|
||||
layer: IndexPatternLayer,
|
||||
indexPattern: IndexPattern,
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { metricChart, MetricChart } from './expression';
|
||||
import { LensMultiTable } from '../types';
|
||||
import { MetricChart, metricChart } from './expression';
|
||||
import { MetricConfig } from '../../common/expressions';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { MetricConfig } from './types';
|
||||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { IFieldFormat } from '../../../../../src/plugins/data/public';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
|
||||
function sampleArgs() {
|
||||
const data: LensMultiTable = {
|
||||
|
|
|
@ -9,75 +9,19 @@ import './expression.scss';
|
|||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {
|
||||
ExpressionFunctionDefinition,
|
||||
import type {
|
||||
ExpressionRenderDefinition,
|
||||
IInterpreterRenderHandlers,
|
||||
} from '../../../../../src/plugins/expressions/public';
|
||||
import { MetricConfig } from './types';
|
||||
import { FormatFactory, LensMultiTable } from '../types';
|
||||
import { AutoScale } from './auto_scale';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { EmptyPlaceholder } from '../shared_components';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import type { MetricChartProps } from '../../common/expressions';
|
||||
|
||||
export interface MetricChartProps {
|
||||
data: LensMultiTable;
|
||||
args: MetricConfig;
|
||||
}
|
||||
|
||||
export interface MetricRender {
|
||||
type: 'render';
|
||||
as: 'lens_metric_chart_renderer';
|
||||
value: MetricChartProps;
|
||||
}
|
||||
|
||||
export const metricChart: ExpressionFunctionDefinition<
|
||||
'lens_metric_chart',
|
||||
LensMultiTable,
|
||||
Omit<MetricConfig, 'layerId'>,
|
||||
MetricRender
|
||||
> = {
|
||||
name: 'lens_metric_chart',
|
||||
type: 'render',
|
||||
help: 'A metric chart',
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: 'The chart title.',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
metricTitle: {
|
||||
types: ['string'],
|
||||
help: 'The title of the metric shown.',
|
||||
},
|
||||
accessor: {
|
||||
types: ['string'],
|
||||
help: 'The column whose value is being displayed',
|
||||
},
|
||||
mode: {
|
||||
types: ['string'],
|
||||
options: ['reduced', 'full'],
|
||||
default: 'full',
|
||||
help:
|
||||
'The display mode of the chart - reduced will only show the metric itself without min size',
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data, args) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_metric_chart_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
} as MetricRender;
|
||||
},
|
||||
};
|
||||
export { metricChart } from '../../common/expressions';
|
||||
export type { MetricState, MetricConfig } from '../../common/expressions';
|
||||
|
||||
export const getMetricChartRenderer = (
|
||||
formatFactory: Promise<FormatFactory>
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import type { CoreSetup } from 'kibana/public';
|
||||
import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import type { EditorFrameSetup } from '../types';
|
||||
import type { FormatFactory } from '../../common';
|
||||
|
||||
export interface MetricVisualizationPluginSetupPlugins {
|
||||
expressions: ExpressionsSetup;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types';
|
||||
import { MetricState } from './types';
|
||||
import type { MetricState } from '../../common/expressions';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { metricVisualization } from './visualization';
|
||||
import { MetricState } from './types';
|
||||
import type { MetricState } from '../../common/expressions';
|
||||
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
|
||||
import { generateId } from '../id_generator';
|
||||
import { DatasourcePublicAPI, FramePublicAPI } from '../types';
|
||||
|
|
|
@ -10,7 +10,7 @@ import { Ast } from '@kbn/interpreter/target/common';
|
|||
import { getSuggestions } from './metric_suggestions';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
import { Visualization, OperationMetadata, DatasourcePublicAPI } from '../types';
|
||||
import { MetricState } from './types';
|
||||
import type { MetricState } from '../../common/expressions';
|
||||
|
||||
const toExpression = (
|
||||
state: MetricState,
|
||||
|
|
|
@ -8,108 +8,18 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import {
|
||||
import type {
|
||||
IInterpreterRenderHandlers,
|
||||
ExpressionRenderDefinition,
|
||||
ExpressionFunctionDefinition,
|
||||
} from 'src/plugins/expressions/public';
|
||||
import { LensMultiTable, FormatFactory, LensFilterEvent } from '../types';
|
||||
import { PieExpressionProps, PieExpressionArgs } from './types';
|
||||
import type { LensFilterEvent } from '../types';
|
||||
import { PieComponent } from './render_function';
|
||||
import { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import type { PieExpressionProps } from '../../common/expressions';
|
||||
import type { ChartsPluginSetup, PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
|
||||
export interface PieRender {
|
||||
type: 'render';
|
||||
as: 'lens_pie_renderer';
|
||||
value: PieExpressionProps;
|
||||
}
|
||||
|
||||
export const pie: ExpressionFunctionDefinition<
|
||||
'lens_pie',
|
||||
LensMultiTable,
|
||||
PieExpressionArgs,
|
||||
PieRender
|
||||
> = {
|
||||
name: 'lens_pie',
|
||||
type: 'render',
|
||||
help: i18n.translate('xpack.lens.pie.expressionHelpLabel', {
|
||||
defaultMessage: 'Pie renderer',
|
||||
}),
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: 'The chart title.',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
groups: {
|
||||
types: ['string'],
|
||||
multi: true,
|
||||
help: '',
|
||||
},
|
||||
metric: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
shape: {
|
||||
types: ['string'],
|
||||
options: ['pie', 'donut', 'treemap'],
|
||||
help: '',
|
||||
},
|
||||
hideLabels: {
|
||||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
numberDisplay: {
|
||||
types: ['string'],
|
||||
options: ['hidden', 'percent', 'value'],
|
||||
help: '',
|
||||
},
|
||||
categoryDisplay: {
|
||||
types: ['string'],
|
||||
options: ['default', 'inside', 'hide'],
|
||||
help: '',
|
||||
},
|
||||
legendDisplay: {
|
||||
types: ['string'],
|
||||
options: ['default', 'show', 'hide'],
|
||||
help: '',
|
||||
},
|
||||
nestedLegend: {
|
||||
types: ['boolean'],
|
||||
help: '',
|
||||
},
|
||||
legendPosition: {
|
||||
types: ['string'],
|
||||
options: [Position.Top, Position.Right, Position.Bottom, Position.Left],
|
||||
help: '',
|
||||
},
|
||||
percentDecimals: {
|
||||
types: ['number'],
|
||||
help: '',
|
||||
},
|
||||
palette: {
|
||||
default: `{theme "palette" default={system_palette name="default"} }`,
|
||||
help: '',
|
||||
types: ['palette'],
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data: LensMultiTable, args: PieExpressionArgs) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_pie_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
export { pie } from '../../common/expressions';
|
||||
|
||||
export const getPieRenderer = (dependencies: {
|
||||
formatFactory: Promise<FormatFactory>;
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CoreSetup } from 'src/core/public';
|
||||
import { ExpressionsSetup } from 'src/plugins/expressions/public';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import type { CoreSetup } from 'src/core/public';
|
||||
import type { ExpressionsSetup } from 'src/plugins/expressions/public';
|
||||
import type { EditorFrameSetup } from '../types';
|
||||
import type { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import type { FormatFactory } from '../../common';
|
||||
|
||||
export interface PieVisualizationPluginSetupPlugins {
|
||||
editorFrame: EditorFrameSetup;
|
||||
|
|
|
@ -16,9 +16,9 @@ import {
|
|||
Chart,
|
||||
} from '@elastic/charts';
|
||||
import { shallow } from 'enzyme';
|
||||
import { LensMultiTable } from '../types';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
import type { PieExpressionArgs } from '../../common/expressions';
|
||||
import { PieComponent } from './render_function';
|
||||
import { PieExpressionArgs } from './types';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { EmptyPlaceholder } from '../shared_components';
|
||||
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
|
||||
|
|
|
@ -24,10 +24,11 @@ import {
|
|||
ElementClickListener,
|
||||
} from '@elastic/charts';
|
||||
import { RenderMode } from 'src/plugins/expressions';
|
||||
import { FormatFactory, LensFilterEvent } from '../types';
|
||||
import type { LensFilterEvent } from '../types';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants';
|
||||
import { PieExpressionProps } from './types';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import type { PieExpressionProps } from '../../common/expressions';
|
||||
import { getSliceValue, getFilterContext } from './render_helpers';
|
||||
import { EmptyPlaceholder } from '../shared_components';
|
||||
import './visualization.scss';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { PaletteOutput } from 'src/plugins/charts/public';
|
||||
import { DataType, SuggestionRequest } from '../types';
|
||||
import { suggestions } from './suggestions';
|
||||
import { PieVisualizationState } from './types';
|
||||
import type { PieVisualizationState } from '../../common/expressions';
|
||||
|
||||
describe('suggestions', () => {
|
||||
describe('pie', () => {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
import { partition } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { SuggestionRequest, VisualizationSuggestion } from '../types';
|
||||
import { PieVisualizationState } from './types';
|
||||
import type { SuggestionRequest, VisualizationSuggestion } from '../types';
|
||||
import type { PieVisualizationState } from '../../common/expressions';
|
||||
import { CHART_NAMES, MAX_PIE_BUCKETS, MAX_TREEMAP_BUCKETS } from './constants';
|
||||
|
||||
function shouldReject({ table, keptLayerIds }: SuggestionRequest<PieVisualizationState>) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Ast } from '@kbn/interpreter/common';
|
|||
import { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import { Operation, DatasourcePublicAPI } from '../types';
|
||||
import { DEFAULT_PERCENT_DECIMALS } from './constants';
|
||||
import { PieVisualizationState } from './types';
|
||||
import type { PieVisualizationState } from '../../common/expressions';
|
||||
|
||||
export function toExpression(
|
||||
state: PieVisualizationState,
|
||||
|
|
|
@ -15,10 +15,10 @@ import {
|
|||
EuiRange,
|
||||
EuiHorizontalRule,
|
||||
} from '@elastic/eui';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import type { Position } from '@elastic/charts';
|
||||
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import { DEFAULT_PERCENT_DECIMALS } from './constants';
|
||||
import { PieVisualizationState, SharedPieLayerState } from './types';
|
||||
import type { PieVisualizationState, SharedPieLayerState } from '../../common/expressions';
|
||||
import { VisualizationDimensionEditorProps, VisualizationToolbarProps } from '../types';
|
||||
import { ToolbarPopover, LegendSettingsPopover, useDebouncedValue } from '../shared_components';
|
||||
import { PalettePicker } from '../shared_components';
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue