Skip to content

Commit

Permalink
[Data table] Reactify & EUIficate the visualization (#70801) (#85936)
Browse files Browse the repository at this point in the history
* Use data grid for table vis

* Create basic table template

* Add table_vis_split component

* Apply cell filtering

* Add aria-label attributes

* Use field formatters for values

* Add no results component

* Remove legacy dependencies

* Add usePagination

* Create usePagination util

* Use percentage column and total row

* Use csv export button

* Update labels

* Fix merge conflicts

* Use split table

* Fix functional tests

* Fix dashboard tests

* Update data table functional tests

* Fix missed test

* Introduce showToolbar option

* Remove useless package

* Fix merge conflicts

* Return back kibanaUtils required bundle

* Revert "Remove useless package"

This reverts commit 144a7cd.

* Use feature flag for legacy vis

* Add footer row

* Remove lock files

* Revert "Remove lock files"

This reverts commit 5c5acd7.

* Minor fixes

* Use common no result message

* Fix broken tests

* Use ui state sorting

* Fix error

* Fix merge conflicts

* Add legacy functional tests

* Pull pagination footer up to keep with table

and fix column split growing continuously in dashboard

* Comments fixes

* Use cell actions for filtering

* Fix translations

* Fix comments

* Reduce legacy tests amount

* Update sorting

* Update split column layout

* Add telemetry for legacy vis

* Apply latest changes for split table

* Fix eslint errors

* Use aria labels with values

* Use aria label for export btn

* Fix functional test

* Update translations

* Cleanup

* Truncate cells content

* Enhance types in table_vis_response_handler.ts

* Persist columns width on change

* Fix sorting history

* Add a migration script for toolbar

* Export sorted table

* Use reportUiCounter instead of reportUiStats

* Fix integration tests

* Fix typos

* Adjust FieldFormat type

* Hide the density selector

* Update docs

* Fix pagination

* Restrict hiding the toolbar

* Fix column index on filter

* Add closePopover action

Co-authored-by: cchaos <caroline.horn@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
# Conflicts:
#	scripts/functional_tests.js

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
Daniil and kibanamachine authored Dec 16, 2020
1 parent 6acb510 commit 9816da3
Show file tree
Hide file tree
Showing 68 changed files with 2,665 additions and 394 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) &gt; [allowsNumericalAggregations](./kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md)

## FieldFormat.allowsNumericalAggregations property

<b>Signature:</b>

```typescript
allowsNumericalAggregations?: boolean;
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export declare abstract class FieldFormat
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [\_params](./kibana-plugin-plugins-data-public.fieldformat._params.md) | | <code>any</code> | |
| [allowsNumericalAggregations](./kibana-plugin-plugins-data-public.fieldformat.allowsnumericalaggregations.md) | | <code>boolean</code> | |
| [convertObject](./kibana-plugin-plugins-data-public.fieldformat.convertobject.md) | | <code>FieldFormatConvert &#124; undefined</code> | {<!-- -->FieldFormatConvert<!-- -->} have to remove the private because of https://github.com/Microsoft/TypeScript/issues/17293 |
| [fieldType](./kibana-plugin-plugins-data-public.fieldformat.fieldtype.md) | <code>static</code> | <code>string &#124; string[]</code> | {<!-- -->string<!-- -->} - Field Format Type |
| [getConfig](./kibana-plugin-plugins-data-public.fieldformat.getconfig.md) | | <code>FieldFormatsGetConfigFn &#124; undefined</code> | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
<b>Signature:</b>

```typescript
export declare type IFieldFormat = PublicMethodsOf<FieldFormat>;
export declare type IFieldFormat = FieldFormat;
```
1 change: 1 addition & 0 deletions scripts/functional_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ require('@kbn/test').runTestsCli([
require.resolve('../test/examples/config.js'),
require.resolve('../test/new_visualize_flow/config.ts'),
require.resolve('../test/security_functional/config.ts'),
require.resolve('../test/functional/config.legacy.ts'),
]);
1 change: 1 addition & 0 deletions src/plugins/data/common/field_formats/field_format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export abstract class FieldFormat {
* @private
*/
public type: any = this.constructor;
public allowsNumericalAggregations?: boolean;

protected readonly _params: any;
protected getConfig: FieldFormatsGetConfigFn | undefined;
Expand Down
3 changes: 1 addition & 2 deletions src/plugins/data/common/field_formats/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
import { PublicMethodsOf } from '@kbn/utility-types';
import { GetConfigFn } from '../types';
import { FieldFormat } from './field_format';
import { FieldFormatsRegistry } from './field_formats_registry';
Expand Down Expand Up @@ -77,7 +76,7 @@ export interface FieldFormatConfig {

export type FieldFormatsGetConfigFn = GetConfigFn;

export type IFieldFormat = PublicMethodsOf<FieldFormat>;
export type IFieldFormat = FieldFormat;

/**
* @string id type is needed for creating custom converters.
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,8 @@ export const extractSearchSourceReferences: (state: SearchSourceFields) => [Sear
export abstract class FieldFormat {
// Warning: (ae-forgotten-export) The symbol "IFieldFormatMetaParams" needs to be exported by the entry point index.d.ts
constructor(_params?: IFieldFormatMetaParams, getConfig?: FieldFormatsGetConfigFn);
// (undocumented)
allowsNumericalAggregations?: boolean;
// Warning: (ae-forgotten-export) The symbol "HtmlContextTypeOptions" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "TextContextTypeOptions" needs to be exported by the entry point index.d.ts
convert(value: any, contentType?: FieldFormatsContentType, options?: HtmlContextTypeOptions | TextContextTypeOptions): string;
Expand Down Expand Up @@ -1092,7 +1094,7 @@ export type IEsSearchResponse<Source = any> = IKibanaSearchResponse<SearchRespon
// Warning: (ae-missing-release-tag) "IFieldFormat" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type IFieldFormat = PublicMethodsOf<FieldFormat>;
export type IFieldFormat = FieldFormat;

// Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
Expand Down
9 changes: 8 additions & 1 deletion src/plugins/vis_type_table/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
Contains the data table visualization, that allows presenting data in a simple table format.
Contains the data table visualization, that allows presenting data in a simple table format.

By default a new version of visualization will be used. To use the previous version of visualization the config must have the `vis_type_table.legacyVisEnabled: true` setting
configured in `kibana.dev.yml` or `kibana.yml`, as shown in the example below:

```yaml
vis_type_table.legacyVisEnabled: true
```
1 change: 1 addition & 0 deletions src/plugins/vis_type_table/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { schema, TypeOf } from '@kbn/config-schema';

export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: true }),
legacyVisEnabled: schema.boolean({ defaultValue: false }),
});

export type ConfigSchema = TypeOf<typeof configSchema>;
4 changes: 3 additions & 1 deletion src/plugins/vis_type_table/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
],
"requiredBundles": [
"kibanaUtils",
"kibanaReact",
"share",
"charts",
"visDefaultEditor"
]
],
"optionalPlugins": ["usageCollection"]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions src/plugins/vis_type_table/public/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export { TableOptions } from './table_vis_options_lazy';
164 changes: 164 additions & 0 deletions src/plugins/vis_type_table/public/components/table_vis_basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React, { memo, useCallback, useMemo } from 'react';
import { EuiDataGrid, EuiDataGridProps, EuiDataGridSorting, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { orderBy } from 'lodash';

import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { createTableVisCell } from './table_vis_cell';
import { Table } from '../table_vis_response_handler';
import { TableVisConfig, TableVisUseUiStateProps } from '../types';
import { useFormattedColumnsAndRows, usePagination } from '../utils';
import { TableVisControls } from './table_vis_controls';
import { createGridColumns } from './table_vis_columns';

interface TableVisBasicProps {
fireEvent: IInterpreterRenderHandlers['event'];
table: Table;
visConfig: TableVisConfig;
title?: string;
uiStateProps: TableVisUseUiStateProps;
}

export const TableVisBasic = memo(
({
fireEvent,
table,
visConfig,
title,
uiStateProps: { columnsWidth, sort, setColumnsWidth, setSort },
}: TableVisBasicProps) => {
const { columns, rows } = useFormattedColumnsAndRows(table, visConfig);

// custom sorting is in place until the EuiDataGrid sorting gets rid of flaws -> https://github.com/elastic/eui/issues/4108
const sortedRows = useMemo(
() =>
sort.columnIndex !== null && sort.direction
? orderBy(rows, columns[sort.columnIndex]?.id, sort.direction)
: rows,
[columns, rows, sort]
);

// renderCellValue is a component which renders a cell based on column and row indexes
const renderCellValue = useMemo(() => createTableVisCell(columns, sortedRows), [
columns,
sortedRows,
]);

// Columns config
const gridColumns = createGridColumns(table, columns, columnsWidth, sortedRows, fireEvent);

// Pagination config
const pagination = usePagination(visConfig, rows.length);
// Sorting config
const sortingColumns = useMemo(
() =>
sort.columnIndex !== null && sort.direction
? [{ id: columns[sort.columnIndex]?.id, direction: sort.direction }]
: [],
[columns, sort]
);
const onSort = useCallback(
(sortingCols: EuiDataGridSorting['columns'] | []) => {
// data table vis sorting now only handles one column sorting
// if data grid provides more columns to sort, pick only the next column to sort
const newSortValue = sortingCols.length <= 1 ? sortingCols[0] : sortingCols[1];
setSort(
newSortValue && {
columnIndex: columns.findIndex((c) => c.id === newSortValue.id),
direction: newSortValue.direction,
}
);
},
[columns, setSort]
);

const dataGridAriaLabel =
title ||
visConfig.title ||
i18n.translate('visTypeTable.defaultAriaLabel', {
defaultMessage: 'Data table visualization',
});

const onColumnResize: EuiDataGridProps['onColumnResize'] = useCallback(
({ columnId, width }) => {
const colIndex = columns.findIndex((c) => c.id === columnId);
setColumnsWidth({
colIndex,
width,
});
},
[columns, setColumnsWidth]
);

return (
<>
{title && (
<EuiTitle size="xs">
<h3>{title}</h3>
</EuiTitle>
)}
<EuiDataGrid
aria-label={dataGridAriaLabel}
columns={gridColumns}
gridStyle={{
border: 'horizontal',
header: 'underline',
}}
rowCount={rows.length}
columnVisibility={{
visibleColumns: columns.map(({ id }) => id),
setVisibleColumns: () => {},
}}
toolbarVisibility={
visConfig.showToolbar && {
showColumnSelector: false,
showFullScreenSelector: false,
showSortSelector: false,
showStyleSelector: false,
additionalControls: (
<TableVisControls
dataGridAriaLabel={dataGridAriaLabel}
cols={columns}
// csv exports sorted table
rows={sortedRows}
table={table}
filename={visConfig.title}
/>
),
}
}
renderCellValue={renderCellValue}
renderFooterCellValue={
visConfig.showTotal
? // @ts-expect-error
({ colIndex }) => columns[colIndex].formattedTotal || null
: undefined
}
pagination={pagination}
sorting={{ columns: sortingColumns, onSort }}
onColumnResize={onColumnResize}
minSizeForControls={1}
/>
</>
);
}
);
50 changes: 50 additions & 0 deletions src/plugins/vis_type_table/public/components/table_vis_cell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { EuiDataGridCellValueElementProps } from '@elastic/eui';

import { Table } from '../table_vis_response_handler';
import { FormattedColumn } from '../types';

export const createTableVisCell = (formattedColumns: FormattedColumn[], rows: Table['rows']) => ({
// @ts-expect-error
colIndex,
rowIndex,
columnId,
}: EuiDataGridCellValueElementProps) => {
const rowValue = rows[rowIndex][columnId];
const column = formattedColumns[colIndex];
const content = column.formatter.convert(rowValue, 'html');

const cellContent = (
<div
/*
* Justification for dangerouslySetInnerHTML:
* The Data table visualization can "enrich" cell contents by applying a field formatter,
* which we want to do if possible.
*/
dangerouslySetInnerHTML={{ __html: content }} // eslint-disable-line react/no-danger
data-test-subj="tbvChartCellContent"
className="tbvChartCellContent"
/>
);

return cellContent;
};
Loading

0 comments on commit 9816da3

Please sign in to comment.