2019-09-17 20:57:53 +02:00
|
|
|
/*
|
|
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
|
|
* or more contributor license agreements. Licensed under the Elastic License;
|
|
|
|
* you may not use this file except in compliance with the Elastic License.
|
|
|
|
*/
|
|
|
|
|
2021-01-29 12:09:26 +01:00
|
|
|
import React from 'react';
|
2019-09-17 20:57:53 +02:00
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import { i18n } from '@kbn/i18n';
|
2020-04-30 23:44:16 +02:00
|
|
|
import { I18nProvider } from '@kbn/i18n/react';
|
2021-01-11 16:51:00 +01:00
|
|
|
|
2021-01-29 12:09:26 +01:00
|
|
|
import type { IAggType } from 'src/plugins/data/public';
|
|
|
|
import type {
|
|
|
|
DatatableColumnMeta,
|
2020-02-11 19:47:36 +01:00
|
|
|
ExpressionFunctionDefinition,
|
|
|
|
ExpressionRenderDefinition,
|
2021-01-29 12:09:26 +01:00
|
|
|
} from 'src/plugins/expressions';
|
2021-01-11 16:51:00 +01:00
|
|
|
import { getSortingCriteria } from './sorting';
|
2020-09-15 11:24:02 +02:00
|
|
|
|
2021-01-29 12:09:26 +01:00
|
|
|
import { DatatableComponent } from './components/table_basic';
|
2020-12-15 12:37:59 +01:00
|
|
|
|
2021-01-29 12:09:26 +01:00
|
|
|
import type { FormatFactory, ILensInterpreterRenderHandlers, LensMultiTable } from '../types';
|
|
|
|
import type {
|
|
|
|
DatatableRender,
|
|
|
|
DatatableColumns,
|
|
|
|
DatatableColumnWidth,
|
|
|
|
DatatableColumnWidthResult,
|
|
|
|
} from './components/types';
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
interface Args {
|
2020-02-11 19:47:36 +01:00
|
|
|
title: string;
|
2020-10-01 18:02:37 +02:00
|
|
|
description?: string;
|
2020-02-11 19:47:36 +01:00
|
|
|
columns: DatatableColumns & { type: 'lens_datatable_columns' };
|
2019-09-17 20:57:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface DatatableProps {
|
|
|
|
data: LensMultiTable;
|
|
|
|
args: Args;
|
|
|
|
}
|
|
|
|
|
2021-01-11 16:51:00 +01:00
|
|
|
function isRange(meta: { params?: { id?: string } } | undefined) {
|
|
|
|
return meta?.params?.id === 'range';
|
|
|
|
}
|
|
|
|
|
2020-12-15 12:37:59 +01:00
|
|
|
export const getDatatable = ({
|
|
|
|
formatFactory,
|
|
|
|
}: {
|
|
|
|
formatFactory: FormatFactory;
|
|
|
|
}): ExpressionFunctionDefinition<'lens_datatable', LensMultiTable, Args, DatatableRender> => ({
|
2019-09-17 20:57:53 +02:00
|
|
|
name: 'lens_datatable',
|
|
|
|
type: 'render',
|
2020-02-11 19:47:36 +01:00
|
|
|
inputTypes: ['lens_multitable'],
|
2019-09-17 20:57:53 +02:00
|
|
|
help: i18n.translate('xpack.lens.datatable.expressionHelpLabel', {
|
|
|
|
defaultMessage: 'Datatable renderer',
|
|
|
|
}),
|
|
|
|
args: {
|
|
|
|
title: {
|
|
|
|
types: ['string'],
|
|
|
|
help: i18n.translate('xpack.lens.datatable.titleLabel', {
|
|
|
|
defaultMessage: 'Title',
|
|
|
|
}),
|
|
|
|
},
|
2020-10-01 18:02:37 +02:00
|
|
|
description: {
|
|
|
|
types: ['string'],
|
|
|
|
help: '',
|
|
|
|
},
|
2019-09-17 20:57:53 +02:00
|
|
|
columns: {
|
|
|
|
types: ['lens_datatable_columns'],
|
|
|
|
help: '',
|
|
|
|
},
|
|
|
|
},
|
2020-12-15 12:37:59 +01:00
|
|
|
fn(data, args, context) {
|
|
|
|
// do the sorting at this level to propagate it also at CSV download
|
|
|
|
const [firstTable] = Object.values(data.tables);
|
|
|
|
const [layerId] = Object.keys(context.inspectorAdapters.tables || {});
|
|
|
|
const formatters: Record<string, ReturnType<FormatFactory>> = {};
|
|
|
|
|
|
|
|
firstTable.columns.forEach((column) => {
|
|
|
|
formatters[column.id] = formatFactory(column.meta?.params);
|
|
|
|
});
|
|
|
|
const { sortBy, sortDirection } = args.columns;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
if (sortBy && sortDirection !== 'none') {
|
|
|
|
// Sort on raw values for these types, while use the formatted value for the rest
|
2021-01-11 16:51:00 +01:00
|
|
|
const sortingCriteria = getSortingCriteria(
|
|
|
|
isRange(columnsReverseLookup[sortBy]?.meta)
|
|
|
|
? 'range'
|
|
|
|
: columnsReverseLookup[sortBy]?.meta?.type,
|
|
|
|
sortBy,
|
|
|
|
formatters[sortBy],
|
|
|
|
sortDirection
|
2020-12-15 12:37:59 +01:00
|
|
|
);
|
2021-01-11 16:51:00 +01:00
|
|
|
// replace the table here
|
|
|
|
context.inspectorAdapters.tables[layerId].rows = (firstTable.rows || [])
|
|
|
|
.slice()
|
|
|
|
.sort(sortingCriteria);
|
2020-12-15 12:37:59 +01:00
|
|
|
// replace also the local copy
|
|
|
|
firstTable.rows = context.inspectorAdapters.tables[layerId].rows;
|
|
|
|
}
|
2019-09-17 20:57:53 +02:00
|
|
|
return {
|
|
|
|
type: 'render',
|
|
|
|
as: 'lens_datatable_renderer',
|
|
|
|
value: {
|
|
|
|
data,
|
|
|
|
args,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
2020-12-15 12:37:59 +01:00
|
|
|
});
|
2019-09-17 20:57:53 +02:00
|
|
|
|
|
|
|
type DatatableColumnsResult = DatatableColumns & { type: 'lens_datatable_columns' };
|
|
|
|
|
2020-02-11 19:47:36 +01:00
|
|
|
export const datatableColumns: ExpressionFunctionDefinition<
|
2019-09-17 20:57:53 +02:00
|
|
|
'lens_datatable_columns',
|
|
|
|
null,
|
|
|
|
DatatableColumns,
|
|
|
|
DatatableColumnsResult
|
|
|
|
> = {
|
|
|
|
name: 'lens_datatable_columns',
|
|
|
|
aliases: [],
|
|
|
|
type: 'lens_datatable_columns',
|
|
|
|
help: '',
|
2020-02-11 19:47:36 +01:00
|
|
|
inputTypes: ['null'],
|
2019-09-17 20:57:53 +02:00
|
|
|
args: {
|
2020-12-15 12:37:59 +01:00
|
|
|
sortBy: { types: ['string'], help: '' },
|
|
|
|
sortDirection: { types: ['string'], help: '' },
|
2019-09-17 20:57:53 +02:00
|
|
|
columnIds: {
|
|
|
|
types: ['string'],
|
|
|
|
multi: true,
|
|
|
|
help: '',
|
|
|
|
},
|
2021-01-29 12:09:26 +01:00
|
|
|
columnWidth: {
|
|
|
|
types: ['lens_datatable_column_width'],
|
|
|
|
multi: true,
|
|
|
|
help: '',
|
|
|
|
},
|
2019-09-17 20:57:53 +02:00
|
|
|
},
|
2020-02-11 19:47:36 +01:00
|
|
|
fn: function fn(input: unknown, args: DatatableColumns) {
|
2019-09-17 20:57:53 +02:00
|
|
|
return {
|
|
|
|
type: 'lens_datatable_columns',
|
|
|
|
...args,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-01-29 12:09:26 +01:00
|
|
|
export const datatableColumnWidth: ExpressionFunctionDefinition<
|
|
|
|
'lens_datatable_column_width',
|
|
|
|
null,
|
|
|
|
DatatableColumnWidth,
|
|
|
|
DatatableColumnWidthResult
|
|
|
|
> = {
|
|
|
|
name: 'lens_datatable_column_width',
|
|
|
|
aliases: [],
|
|
|
|
type: 'lens_datatable_column_width',
|
|
|
|
help: '',
|
|
|
|
inputTypes: ['null'],
|
|
|
|
args: {
|
|
|
|
columnId: {
|
|
|
|
types: ['string'],
|
|
|
|
help: '',
|
|
|
|
},
|
|
|
|
width: {
|
|
|
|
types: ['number'],
|
|
|
|
help: '',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
fn: function fn(input: unknown, args: DatatableColumnWidth) {
|
|
|
|
return {
|
|
|
|
type: 'lens_datatable_column_width',
|
|
|
|
...args,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-04-30 23:44:16 +02:00
|
|
|
export const getDatatableRenderer = (dependencies: {
|
2020-12-15 12:37:59 +01:00
|
|
|
formatFactory: FormatFactory;
|
2020-04-30 23:44:16 +02:00
|
|
|
getType: Promise<(name: string) => IAggType>;
|
|
|
|
}): ExpressionRenderDefinition<DatatableProps> => ({
|
2019-09-17 20:57:53 +02:00
|
|
|
name: 'lens_datatable_renderer',
|
|
|
|
displayName: i18n.translate('xpack.lens.datatable.visualizationName', {
|
|
|
|
defaultMessage: 'Datatable',
|
|
|
|
}),
|
|
|
|
help: '',
|
2020-02-11 19:47:36 +01:00
|
|
|
validate: () => undefined,
|
2019-09-17 20:57:53 +02:00
|
|
|
reuseDomNode: true,
|
2019-10-25 22:32:59 +02:00
|
|
|
render: async (
|
|
|
|
domNode: Element,
|
|
|
|
config: DatatableProps,
|
2020-05-15 12:01:27 +02:00
|
|
|
handlers: ILensInterpreterRenderHandlers
|
2019-10-25 22:32:59 +02:00
|
|
|
) => {
|
2020-04-30 23:44:16 +02:00
|
|
|
const resolvedGetType = await dependencies.getType;
|
2020-12-14 13:28:23 +01:00
|
|
|
const { hasCompatibleActions } = handlers;
|
|
|
|
|
|
|
|
// An entry for each table row, whether it has any actions attached to
|
|
|
|
// ROW_CLICK_TRIGGER trigger.
|
|
|
|
let rowHasRowClickTriggerActions: boolean[] = [];
|
|
|
|
if (hasCompatibleActions) {
|
|
|
|
const table = Object.values(config.data.tables)[0];
|
|
|
|
if (!!table) {
|
|
|
|
rowHasRowClickTriggerActions = await Promise.all(
|
|
|
|
table.rows.map(async (row, rowIndex) => {
|
|
|
|
try {
|
|
|
|
const hasActions = await hasCompatibleActions({
|
|
|
|
name: 'tableRowContextMenuClick',
|
|
|
|
data: {
|
|
|
|
rowIndex,
|
|
|
|
table,
|
|
|
|
columns: config.args.columns.columnIds,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
return hasActions;
|
|
|
|
} catch {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-25 22:32:59 +02:00
|
|
|
ReactDOM.render(
|
2020-04-30 23:44:16 +02:00
|
|
|
<I18nProvider>
|
|
|
|
<DatatableComponent
|
|
|
|
{...config}
|
2020-12-15 12:37:59 +01:00
|
|
|
formatFactory={dependencies.formatFactory}
|
2021-01-29 12:09:26 +01:00
|
|
|
dispatchEvent={handlers.event}
|
2020-12-15 12:37:59 +01:00
|
|
|
renderMode={handlers.getRenderMode()}
|
2020-04-30 23:44:16 +02:00
|
|
|
getType={resolvedGetType}
|
2020-12-14 13:28:23 +01:00
|
|
|
rowHasRowClickTriggerActions={rowHasRowClickTriggerActions}
|
2020-04-30 23:44:16 +02:00
|
|
|
/>
|
|
|
|
</I18nProvider>,
|
2019-10-25 22:32:59 +02:00
|
|
|
domNode,
|
|
|
|
() => {
|
|
|
|
handlers.done();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
2019-09-17 20:57:53 +02:00
|
|
|
},
|
|
|
|
});
|