From 3a747006cf54bbe4ca310b3c3869c3b102cce674 Mon Sep 17 00:00:00 2001
From: Julia Rechkunova
Date: Wed, 11 Sep 2024 13:46:36 +0300
Subject: [PATCH] [OneDiscover][UnifiedDocViewer] Add filtering for selected
fields (#191930)
- Closes https://github.com/elastic/kibana/issues/191536
## Summary
This PR adds "Selected fields only" toggle to UnifiedDocViewer. The
selected state will be persisted under
`unifiedDocViewer:showOnlySelectedFields` in Local Storage.
### Checklist
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
---------
Co-authored-by: Davis McPhee
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
packages/kbn-discover-utils/index.ts | 2 +
.../src/__mocks__/data_view.ts | 6 +
packages/kbn-discover-utils/src/types.ts | 12 +
.../src/utils/get_visible_columns.test.tsx | 195 ++++++++++++++
.../src/utils/get_visible_columns.ts | 49 ++++
.../kbn-discover-utils/src/utils/index.ts | 1 +
packages/kbn-discover-utils/tsconfig.json | 3 +-
packages/kbn-discover-utils/types.ts | 1 +
.../src/components/data_table.tsx | 8 +-
.../components/data_table_columns.test.tsx | 185 +------------
.../src/components/data_table_columns.tsx | 36 ---
packages/kbn-unified-data-table/src/types.ts | 13 +-
.../src/services/types.ts | 15 +-
packages/kbn-unified-doc-viewer/tsconfig.json | 1 -
.../esql_datagrid/public/data_grid.tsx | 11 +-
.../esql_datagrid/public/row_viewer.tsx | 3 +-
.../doc_viewer_flyout/doc_viewer_flyout.tsx | 3 +-
.../doc_viewer_table/table.test.tsx | 168 ++++++++++++
.../components/doc_viewer_table/table.tsx | 247 ++++++++++++------
src/plugins/unified_doc_viewer/tsconfig.json | 1 -
.../apps/discover/group3/_doc_viewer.ts | 145 ++++++++++
21 files changed, 759 insertions(+), 346 deletions(-)
create mode 100644 packages/kbn-discover-utils/src/utils/get_visible_columns.test.tsx
create mode 100644 packages/kbn-discover-utils/src/utils/get_visible_columns.ts
create mode 100644 src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.test.tsx
diff --git a/packages/kbn-discover-utils/index.ts b/packages/kbn-discover-utils/index.ts
index 7119ac31d5baf..fda3fbdd48ad0 100644
--- a/packages/kbn-discover-utils/index.ts
+++ b/packages/kbn-discover-utils/index.ts
@@ -52,6 +52,8 @@ export {
LogLevelCoalescedValue,
LogLevelBadge,
getFieldValue,
+ getVisibleColumns,
+ canPrependTimeFieldColumn,
} from './src';
export type { LogsContextService } from './src';
diff --git a/packages/kbn-discover-utils/src/__mocks__/data_view.ts b/packages/kbn-discover-utils/src/__mocks__/data_view.ts
index 5034ed9dc008e..daa8835e817c4 100644
--- a/packages/kbn-discover-utils/src/__mocks__/data_view.ts
+++ b/packages/kbn-discover-utils/src/__mocks__/data_view.ts
@@ -140,3 +140,9 @@ export const dataViewMock = buildDataViewMock({
name: 'the-data-view',
fields: shallowMockedFields,
});
+
+export const dataViewMockWithTimeField = buildDataViewMock({
+ name: 'the-data-view',
+ fields: shallowMockedFields,
+ timeFieldName: '@timestamp',
+});
diff --git a/packages/kbn-discover-utils/src/types.ts b/packages/kbn-discover-utils/src/types.ts
index a2afe245308bb..dc720c02dd4ea 100644
--- a/packages/kbn-discover-utils/src/types.ts
+++ b/packages/kbn-discover-utils/src/types.ts
@@ -8,6 +8,7 @@
*/
import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
+import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common';
export type { IgnoredReason, ShouldShowFieldInTableHandler } from './utils';
@@ -41,6 +42,17 @@ export interface DataTableRecord {
isAnchor?: boolean;
}
+/**
+ * Custom column types per column name
+ */
+export type DataTableColumnsMeta = Record<
+ string,
+ {
+ type: DatatableColumnMeta['type'];
+ esType?: DatatableColumnMeta['esType'];
+ }
+>;
+
type FormattedHitPair = readonly [
fieldDisplayName: string,
formattedValue: string,
diff --git a/packages/kbn-discover-utils/src/utils/get_visible_columns.test.tsx b/packages/kbn-discover-utils/src/utils/get_visible_columns.test.tsx
new file mode 100644
index 0000000000000..88877209d0528
--- /dev/null
+++ b/packages/kbn-discover-utils/src/utils/get_visible_columns.test.tsx
@@ -0,0 +1,195 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import type { DataView } from '@kbn/data-views-plugin/public';
+import type { DatatableColumnType } from '@kbn/expressions-plugin/common';
+import {
+ dataViewMock as dataViewMockWithoutTimeField,
+ dataViewMockWithTimeField,
+} from '../__mocks__';
+import { getVisibleColumns, canPrependTimeFieldColumn } from './get_visible_columns';
+
+describe('getVisibleColumns utils', function () {
+ describe('getVisibleColumns', () => {
+ it('returns grid columns without time column when data view has no timestamp field', () => {
+ const actual = getVisibleColumns(
+ ['extension', 'message'],
+ dataViewMockWithoutTimeField,
+ true
+ ) as string[];
+ expect(actual).toEqual(['extension', 'message']);
+ });
+
+ it('returns grid columns without time column when showTimeCol is falsy', () => {
+ const actual = getVisibleColumns(
+ ['extension', 'message'],
+ dataViewMockWithTimeField,
+ false
+ ) as string[];
+ expect(actual).toEqual(['extension', 'message']);
+ });
+
+ it('returns grid columns with time column when data view has timestamp field', () => {
+ const actual = getVisibleColumns(
+ ['extension', 'message'],
+ dataViewMockWithTimeField,
+ true
+ ) as string[];
+ expect(actual).toEqual(['@timestamp', 'extension', 'message']);
+ });
+ });
+
+ describe('canPrependTimeFieldColumn', () => {
+ function buildColumnTypes(dataView: DataView) {
+ const columnsMeta: Record<
+ string,
+ { type: DatatableColumnType; esType?: string | undefined }
+ > = {};
+ for (const field of dataView.fields) {
+ columnsMeta[field.name] = { type: field.type as DatatableColumnType };
+ }
+ return columnsMeta;
+ }
+
+ describe('dataView with timeField', () => {
+ it('should forward showTimeCol if no _source columns is passed', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['extension', 'message'],
+ dataViewMockWithTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithTimeField),
+ showTimeCol,
+ false
+ )
+ ).toBe(showTimeCol);
+ }
+ });
+
+ it('should return false if no _source columns is passed, text-based datasource', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['extension', 'message'],
+ dataViewMockWithTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithTimeField),
+ showTimeCol,
+ true
+ )
+ ).toBe(false);
+ }
+ });
+
+ it('should forward showTimeCol if _source column is passed', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['_source'],
+ dataViewMockWithTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithTimeField),
+ showTimeCol,
+ false
+ )
+ ).toBe(showTimeCol);
+ }
+ });
+
+ it('should forward showTimeCol if _source column is passed, text-based datasource', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['_source'],
+ dataViewMockWithTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithTimeField),
+ showTimeCol,
+ true
+ )
+ ).toBe(showTimeCol);
+ }
+ });
+
+ it('should return false if _source column is passed but time field is not returned, text-based datasource', () => {
+ // ... | DROP @timestamp test case
+ const columnsMeta = buildColumnTypes(dataViewMockWithTimeField);
+ if (dataViewMockWithTimeField.timeFieldName) {
+ delete columnsMeta[dataViewMockWithTimeField.timeFieldName];
+ }
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['_source'],
+ dataViewMockWithTimeField.timeFieldName,
+ columnsMeta,
+ showTimeCol,
+ true
+ )
+ ).toBe(false);
+ }
+ });
+ });
+
+ describe('dataView without timeField', () => {
+ it('should return false if no _source columns is passed', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['extension', 'message'],
+ dataViewMockWithoutTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithoutTimeField),
+ showTimeCol,
+ false
+ )
+ ).toBe(false);
+ }
+ });
+
+ it('should return false if no _source columns is passed, text-based datasource', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['extension', 'message'],
+ dataViewMockWithoutTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithoutTimeField),
+ showTimeCol,
+ true
+ )
+ ).toBe(false);
+ }
+ });
+
+ it('should return false if _source column is passed', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['_source'],
+ dataViewMockWithoutTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithoutTimeField),
+ showTimeCol,
+ false
+ )
+ ).toBe(false);
+ }
+ });
+
+ it('should return false if _source column is passed, text-based datasource', () => {
+ for (const showTimeCol of [true, false]) {
+ expect(
+ canPrependTimeFieldColumn(
+ ['_source'],
+ dataViewMockWithoutTimeField.timeFieldName,
+ buildColumnTypes(dataViewMockWithoutTimeField),
+ showTimeCol,
+ true
+ )
+ ).toBe(false);
+ }
+ });
+ });
+ });
+});
diff --git a/packages/kbn-discover-utils/src/utils/get_visible_columns.ts b/packages/kbn-discover-utils/src/utils/get_visible_columns.ts
new file mode 100644
index 0000000000000..69c4390d61e08
--- /dev/null
+++ b/packages/kbn-discover-utils/src/utils/get_visible_columns.ts
@@ -0,0 +1,49 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import type { DataView } from '@kbn/data-views-plugin/common';
+import type { DataTableColumnsMeta } from '../types';
+
+export function canPrependTimeFieldColumn(
+ columns: string[] | undefined,
+ timeFieldName: string | undefined,
+ columnsMeta: DataTableColumnsMeta | undefined,
+ showTimeCol: boolean, // based on Advanced Settings `doc_table:hideTimeColumn`
+ isESQLMode: boolean
+) {
+ if (!showTimeCol || !timeFieldName) {
+ return false;
+ }
+
+ if (isESQLMode) {
+ return (
+ !!columns && !!columnsMeta && timeFieldName in columnsMeta && columns.includes('_source')
+ );
+ }
+
+ return true;
+}
+
+export function getVisibleColumns(
+ columns: string[],
+ dataView: DataView,
+ shouldPrependTimeFieldColumn: boolean
+) {
+ const timeFieldName = dataView.timeFieldName;
+
+ if (
+ shouldPrependTimeFieldColumn &&
+ timeFieldName &&
+ !columns.find((col) => col === timeFieldName)
+ ) {
+ return [timeFieldName, ...columns];
+ }
+
+ return columns;
+}
diff --git a/packages/kbn-discover-utils/src/utils/index.ts b/packages/kbn-discover-utils/src/utils/index.ts
index 4066ce34bdb01..e408d7eb1c163 100644
--- a/packages/kbn-discover-utils/src/utils/index.ts
+++ b/packages/kbn-discover-utils/src/utils/index.ts
@@ -18,4 +18,5 @@ export * from './get_should_show_field_handler';
export * from './nested_fields';
export * from './get_field_value';
export * from './calc_field_counts';
+export * from './get_visible_columns';
export { isLegacyTableEnabled } from './is_legacy_table_enabled';
diff --git a/packages/kbn-discover-utils/tsconfig.json b/packages/kbn-discover-utils/tsconfig.json
index de0b747eda555..5a5fe4c69636e 100644
--- a/packages/kbn-discover-utils/tsconfig.json
+++ b/packages/kbn-discover-utils/tsconfig.json
@@ -25,6 +25,7 @@
"@kbn/field-types",
"@kbn/i18n",
"@kbn/core-ui-settings-browser",
- "@kbn/ui-theme"
+ "@kbn/ui-theme",
+ "@kbn/expressions-plugin"
]
}
diff --git a/packages/kbn-discover-utils/types.ts b/packages/kbn-discover-utils/types.ts
index 436d9617614ff..dfbb54f1f09ca 100644
--- a/packages/kbn-discover-utils/types.ts
+++ b/packages/kbn-discover-utils/types.ts
@@ -9,6 +9,7 @@
export type {
DataTableRecord,
+ DataTableColumnsMeta,
EsHitRecord,
IgnoredReason,
ShouldShowFieldInTableHandler,
diff --git a/packages/kbn-unified-data-table/src/components/data_table.tsx b/packages/kbn-unified-data-table/src/components/data_table.tsx
index 2b582b965892a..87931e96c4523 100644
--- a/packages/kbn-unified-data-table/src/components/data_table.tsx
+++ b/packages/kbn-unified-data-table/src/components/data_table.tsx
@@ -39,7 +39,11 @@ import {
import type { ToastsStart, IUiSettingsClient } from '@kbn/core/public';
import type { Serializable } from '@kbn/utility-types';
import type { DataTableRecord } from '@kbn/discover-utils/types';
-import { getShouldShowFieldHandler } from '@kbn/discover-utils';
+import {
+ getShouldShowFieldHandler,
+ canPrependTimeFieldColumn,
+ getVisibleColumns,
+} from '@kbn/discover-utils';
import type { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { ThemeServiceStart } from '@kbn/react-kibana-context-common';
@@ -62,8 +66,6 @@ import { getRenderCellValueFn } from '../utils/get_render_cell_value';
import {
getEuiGridColumns,
getLeadControlColumns,
- getVisibleColumns,
- canPrependTimeFieldColumn,
SELECT_ROW,
OPEN_DETAILS,
} from './data_table_columns';
diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx
index 23fcd85de1020..4853201af4b48 100644
--- a/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx
+++ b/packages/kbn-unified-data-table/src/components/data_table_columns.test.tsx
@@ -8,17 +8,9 @@
*/
import React from 'react';
-import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
-import type { DataView } from '@kbn/data-views-plugin/public';
-import type { DatatableColumnType } from '@kbn/expressions-plugin/common';
-import {
- deserializeHeaderRowHeight,
- getEuiGridColumns,
- getVisibleColumns,
- canPrependTimeFieldColumn,
-} from './data_table_columns';
+import { getVisibleColumns } from '@kbn/discover-utils';
+import { deserializeHeaderRowHeight, getEuiGridColumns } from './data_table_columns';
import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield';
-import { dataViewWithoutTimefieldMock } from '../../__mocks__/data_view_without_timefield';
import { dataTableContextMock } from '../../__mocks__/table_context';
import { servicesMock } from '../../__mocks__/services';
import { ROWS_HEIGHT_OPTIONS } from '../constants';
@@ -180,179 +172,6 @@ describe('Data table columns', function () {
});
});
- describe('getVisibleColumns', () => {
- it('returns grid columns without time column when data view has no timestamp field', () => {
- const actual = getVisibleColumns(['extension', 'message'], dataViewMock, true) as string[];
- expect(actual).toEqual(['extension', 'message']);
- });
-
- it('returns grid columns without time column when showTimeCol is falsy', () => {
- const actual = getVisibleColumns(
- ['extension', 'message'],
- dataViewWithTimefieldMock,
- false
- ) as string[];
- expect(actual).toEqual(['extension', 'message']);
- });
-
- it('returns grid columns with time column when data view has timestamp field', () => {
- const actual = getVisibleColumns(
- ['extension', 'message'],
- dataViewWithTimefieldMock,
- true
- ) as string[];
- expect(actual).toEqual(['timestamp', 'extension', 'message']);
- });
- });
-
- describe('canPrependTimeFieldColumn', () => {
- function buildColumnTypes(dataView: DataView) {
- const columnsMeta: Record<
- string,
- { type: DatatableColumnType; esType?: string | undefined }
- > = {};
- for (const field of dataView.fields) {
- columnsMeta[field.name] = { type: field.type as DatatableColumnType };
- }
- return columnsMeta;
- }
-
- describe('dataView with timeField', () => {
- it('should forward showTimeCol if no _source columns is passed', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['extension', 'message'],
- dataViewWithTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithTimefieldMock),
- showTimeCol,
- false
- )
- ).toBe(showTimeCol);
- }
- });
-
- it('should return false if no _source columns is passed, text-based datasource', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['extension', 'message'],
- dataViewWithTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithTimefieldMock),
- showTimeCol,
- true
- )
- ).toBe(false);
- }
- });
-
- it('should forward showTimeCol if _source column is passed', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['_source'],
- dataViewWithTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithTimefieldMock),
- showTimeCol,
- false
- )
- ).toBe(showTimeCol);
- }
- });
-
- it('should forward showTimeCol if _source column is passed, text-based datasource', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['_source'],
- dataViewWithTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithTimefieldMock),
- showTimeCol,
- true
- )
- ).toBe(showTimeCol);
- }
- });
-
- it('should return false if _source column is passed but time field is not returned, text-based datasource', () => {
- // ... | DROP @timestamp test case
- const columnsMeta = buildColumnTypes(dataViewWithTimefieldMock);
- if (dataViewWithTimefieldMock.timeFieldName) {
- delete columnsMeta[dataViewWithTimefieldMock.timeFieldName];
- }
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['_source'],
- dataViewWithTimefieldMock.timeFieldName,
- columnsMeta,
- showTimeCol,
- true
- )
- ).toBe(false);
- }
- });
- });
-
- describe('dataView without timeField', () => {
- it('should return false if no _source columns is passed', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['extension', 'message'],
- dataViewWithoutTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithoutTimefieldMock),
- showTimeCol,
- false
- )
- ).toBe(false);
- }
- });
-
- it('should return false if no _source columns is passed, text-based datasource', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['extension', 'message'],
- dataViewWithoutTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithoutTimefieldMock),
- showTimeCol,
- true
- )
- ).toBe(false);
- }
- });
-
- it('should return false if _source column is passed', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['_source'],
- dataViewWithoutTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithoutTimefieldMock),
- showTimeCol,
- false
- )
- ).toBe(false);
- }
- });
-
- it('should return false if _source column is passed, text-based datasource', () => {
- for (const showTimeCol of [true, false]) {
- expect(
- canPrependTimeFieldColumn(
- ['_source'],
- dataViewWithoutTimefieldMock.timeFieldName,
- buildColumnTypes(dataViewWithoutTimefieldMock),
- showTimeCol,
- true
- )
- ).toBe(false);
- }
- });
- });
- });
-
describe('column tokens', () => {
it('returns eui grid columns with tokens', async () => {
const actual = getEuiGridColumns({
diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx
index 10f55431faa71..4ef0636093f8d 100644
--- a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx
+++ b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx
@@ -358,39 +358,3 @@ export function getEuiGridColumns({
})
);
}
-
-export function canPrependTimeFieldColumn(
- columns: string[],
- timeFieldName: string | undefined,
- columnsMeta: DataTableColumnsMeta | undefined,
- showTimeCol: boolean,
- isPlainRecord: boolean
-) {
- if (!showTimeCol || !timeFieldName) {
- return false;
- }
-
- if (isPlainRecord) {
- return !!columnsMeta && timeFieldName in columnsMeta && columns.includes('_source');
- }
-
- return true;
-}
-
-export function getVisibleColumns(
- columns: string[],
- dataView: DataView,
- shouldPrependTimeFieldColumn: boolean
-) {
- const timeFieldName = dataView.timeFieldName;
-
- if (
- shouldPrependTimeFieldColumn &&
- timeFieldName &&
- !columns.find((col) => col === timeFieldName)
- ) {
- return [timeFieldName, ...columns];
- }
-
- return columns;
-}
diff --git a/packages/kbn-unified-data-table/src/types.ts b/packages/kbn-unified-data-table/src/types.ts
index a020a896130f3..77feaf8a5ef44 100644
--- a/packages/kbn-unified-data-table/src/types.ts
+++ b/packages/kbn-unified-data-table/src/types.ts
@@ -18,8 +18,8 @@ import type { DataTableRecord } from '@kbn/discover-utils/src/types';
import type { DataView } from '@kbn/data-views-plugin/common';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { EuiDataGridControlColumn } from '@elastic/eui/src/components/datagrid/data_grid_types';
-import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common';
+export type { DataTableColumnsMeta } from '@kbn/discover-utils/src/types';
export type { DataGridDensity } from './constants';
/**
@@ -45,17 +45,6 @@ export type ValueToStringConverter = (
options?: { compatibleWithCSV?: boolean }
) => { formattedString: string; withFormula: boolean };
-/**
- * Custom column types per column name
- */
-export type DataTableColumnsMeta = Record<
- string,
- {
- type: DatatableColumnMeta['type'];
- esType?: DatatableColumnMeta['esType'];
- }
->;
-
export type DataGridCellValueElementProps = EuiDataGridCellValueElementProps & {
row: DataTableRecord;
dataView: DataView;
diff --git a/packages/kbn-unified-doc-viewer/src/services/types.ts b/packages/kbn-unified-doc-viewer/src/services/types.ts
index 88b5824c6837f..04d88ba256a37 100644
--- a/packages/kbn-unified-doc-viewer/src/services/types.ts
+++ b/packages/kbn-unified-doc-viewer/src/services/types.ts
@@ -9,8 +9,11 @@
import type { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import type { AggregateQuery, Query } from '@kbn/es-query';
-import type { DataTableRecord, IgnoredReason } from '@kbn/discover-utils/types';
-import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common';
+import type {
+ DataTableRecord,
+ DataTableColumnsMeta,
+ IgnoredReason,
+} from '@kbn/discover-utils/types';
import { DocViewsRegistry } from './doc_views_registry';
export interface FieldMapping {
@@ -36,13 +39,7 @@ export interface DocViewRenderProps {
* If not provided, types will be derived by default from the dataView field types.
* For displaying text-based search results, define column types (which are available separately in the fetch request) here.
*/
- columnsMeta?: Record<
- string,
- {
- type: DatatableColumnMeta['type'];
- esType?: DatatableColumnMeta['esType'];
- }
- >;
+ columnsMeta?: DataTableColumnsMeta;
query?: Query | AggregateQuery;
textBasedHits?: DataTableRecord[];
hideActionsColumn?: boolean;
diff --git a/packages/kbn-unified-doc-viewer/tsconfig.json b/packages/kbn-unified-doc-viewer/tsconfig.json
index 05bb4f1539598..e9429af74bd74 100644
--- a/packages/kbn-unified-doc-viewer/tsconfig.json
+++ b/packages/kbn-unified-doc-viewer/tsconfig.json
@@ -24,6 +24,5 @@
"@kbn/i18n",
"@kbn/react-field",
"@kbn/field-utils",
- "@kbn/expressions-plugin",
]
}
diff --git a/src/plugins/esql_datagrid/public/data_grid.tsx b/src/plugins/esql_datagrid/public/data_grid.tsx
index 990d27891bf6c..58145627f139f 100644
--- a/src/plugins/esql_datagrid/public/data_grid.tsx
+++ b/src/plugins/esql_datagrid/public/data_grid.tsx
@@ -21,10 +21,10 @@ import { EuiLink, EuiText, EuiIcon } from '@elastic/eui';
import { css } from '@emotion/react';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import type { ESQLRow } from '@kbn/es-types';
-import type { DatatableColumn, DatatableColumnMeta } from '@kbn/expressions-plugin/common';
+import type { DatatableColumn } from '@kbn/expressions-plugin/common';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { AggregateQuery } from '@kbn/es-query';
-import type { DataTableRecord } from '@kbn/discover-utils/types';
+import type { DataTableRecord, DataTableColumnsMeta } from '@kbn/discover-utils/types';
import type { DataView } from '@kbn/data-views-plugin/common';
import type { CoreStart } from '@kbn/core/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
@@ -46,13 +46,6 @@ interface ESQLDataGridProps {
initialRowHeight?: number;
controlColumnIds?: string[];
}
-type DataTableColumnsMeta = Record<
- string,
- {
- type: DatatableColumnMeta['type'];
- esType?: DatatableColumnMeta['esType'];
- }
->;
const sortOrder: SortOrder[] = [];
const DEFAULT_INITIAL_ROW_HEIGHT = 5;
diff --git a/src/plugins/esql_datagrid/public/row_viewer.tsx b/src/plugins/esql_datagrid/public/row_viewer.tsx
index 8be9a67b36894..2578bee01e6cb 100644
--- a/src/plugins/esql_datagrid/public/row_viewer.tsx
+++ b/src/plugins/esql_datagrid/public/row_viewer.tsx
@@ -9,8 +9,7 @@
import React, { useMemo } from 'react';
import type { DataView } from '@kbn/data-views-plugin/public';
-import type { DataTableRecord } from '@kbn/discover-utils/types';
-import type { DataTableColumnsMeta } from '@kbn/unified-data-table';
+import type { DataTableRecord, DataTableColumnsMeta } from '@kbn/discover-utils/types';
import { UnifiedDocViewerFlyout } from '@kbn/unified-doc-viewer-plugin/public';
import { NotificationsStart } from '@kbn/core-notifications-browser';
diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx
index 754f9c4c05b6e..540c04a206c61 100644
--- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx
+++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx
@@ -29,8 +29,7 @@ import {
useIsWithinMinBreakpoint,
EuiFlyoutProps,
} from '@elastic/eui';
-import type { DataTableRecord } from '@kbn/discover-utils/types';
-import type { DataTableColumnsMeta } from '@kbn/unified-data-table';
+import type { DataTableRecord, DataTableColumnsMeta } from '@kbn/discover-utils/types';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import type { ToastsStart } from '@kbn/core-notifications-browser';
import type { DocViewFilterFn, DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.test.tsx
new file mode 100644
index 0000000000000..833b9db059975
--- /dev/null
+++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.test.tsx
@@ -0,0 +1,168 @@
+/*
+ * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+import React from 'react';
+import { render, screen, act } from '@testing-library/react';
+import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
+import { buildDataTableRecord } from '@kbn/discover-utils';
+import { createStubDataView } from '@kbn/data-views-plugin/common/data_view.stub';
+import { Storage } from '@kbn/kibana-utils-plugin/public';
+import { generateEsHits } from '@kbn/discover-utils/src/__mocks__';
+import { DocViewerTable, SHOW_ONLY_SELECTED_FIELDS } from './table';
+import { mockUnifiedDocViewerServices } from '../../__mocks__';
+import { setUnifiedDocViewerServices } from '../../plugin';
+
+const storage = new Storage(window.localStorage);
+
+setUnifiedDocViewerServices(mockUnifiedDocViewerServices);
+
+const dataView = createStubDataView({
+ spec: {
+ id: 'test',
+ title: 'test',
+ timeFieldName: '@timestamp',
+ fields: {
+ '@timestamp': {
+ name: '@timestamp',
+ type: 'date',
+ esTypes: ['date'],
+ aggregatable: true,
+ searchable: true,
+ count: 30,
+ readFromDocValues: true,
+ scripted: false,
+ isMapped: true,
+ },
+ bytes: {
+ name: 'bytes',
+ type: 'number',
+ esTypes: ['long'],
+ aggregatable: true,
+ searchable: true,
+ count: 10,
+ readFromDocValues: true,
+ scripted: false,
+ isMapped: true,
+ },
+ 'extension.keyword': {
+ name: 'extension.keyword',
+ type: 'string',
+ esTypes: ['keyword'],
+ aggregatable: true,
+ searchable: true,
+ count: 0,
+ readFromDocValues: true,
+ scripted: false,
+ subType: {
+ multi: {
+ parent: 'extension',
+ },
+ },
+ isMapped: true,
+ },
+ _id: {
+ name: '_id',
+ type: 'string',
+ esTypes: ['_id'],
+ aggregatable: false,
+ searchable: true,
+ readFromDocValues: true,
+ isMapped: true,
+ },
+ },
+ },
+});
+const hit = buildDataTableRecord(generateEsHits(dataView, 1)[0], dataView);
+
+describe('DocViewerTable', () => {
+ afterEach(() => {
+ storage.clear();
+ });
+
+ describe('switch - show only selected fields', () => {
+ it('should disable the switch if columns is empty', async () => {
+ render(
+
+
+
+ );
+
+ expect(screen.getByTestId('unifiedDocViewerShowOnlySelectedFieldsSwitch')).toBeDisabled();
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.getByText('bytes')).toBeInTheDocument();
+ expect(screen.getByText('extension.keyword')).toBeInTheDocument();
+ });
+
+ it('should disable the switch even if it was previously switched on', async () => {
+ storage.set(SHOW_ONLY_SELECTED_FIELDS, true);
+
+ render(
+
+
+
+ );
+
+ expect(screen.getByTestId('unifiedDocViewerShowOnlySelectedFieldsSwitch')).toBeDisabled();
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.getByText('bytes')).toBeInTheDocument();
+ expect(screen.getByText('extension.keyword')).toBeInTheDocument();
+ });
+
+ it('should show only selected fields if it was previously switched on', async () => {
+ storage.set(SHOW_ONLY_SELECTED_FIELDS, true);
+
+ render(
+
+
+
+ );
+
+ expect(screen.getByTestId('unifiedDocViewerShowOnlySelectedFieldsSwitch')).toBeEnabled();
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.queryByText('bytes')).toBeNull();
+ expect(screen.getByText('extension.keyword')).toBeInTheDocument();
+ });
+
+ it('should allow toggling the switch', async () => {
+ render(
+
+
+
+ );
+
+ const showOnlySelectedFieldsSwitch = screen.getByTestId(
+ 'unifiedDocViewerShowOnlySelectedFieldsSwitch'
+ );
+
+ expect(showOnlySelectedFieldsSwitch).toBeEnabled();
+ expect(showOnlySelectedFieldsSwitch).toHaveValue('');
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.getByText('bytes')).toBeInTheDocument();
+ expect(screen.getByText('extension.keyword')).toBeInTheDocument();
+
+ act(() => {
+ showOnlySelectedFieldsSwitch.click();
+ });
+
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.getByText('bytes')).toBeInTheDocument();
+ expect(screen.queryByText('extension.keyword')).toBeNull();
+ expect(storage.get(SHOW_ONLY_SELECTED_FIELDS)).toBe(true);
+
+ act(() => {
+ showOnlySelectedFieldsSwitch.click();
+ });
+
+ expect(screen.getByText('@timestamp')).toBeInTheDocument();
+ expect(screen.getByText('bytes')).toBeInTheDocument();
+ expect(screen.getByText('extension.keyword')).toBeInTheDocument();
+ expect(storage.get(SHOW_ONLY_SELECTED_FIELDS)).toBe(false);
+ });
+ });
+});
diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.tsx
index 76b1e0b02940d..e542a94c5fca8 100644
--- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.tsx
+++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_table/table.tsx
@@ -24,7 +24,6 @@ import {
EuiCallOut,
useResizeObserver,
EuiSwitch,
- useEuiTheme,
EuiSwitchEvent,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -33,11 +32,14 @@ import { Storage } from '@kbn/kibana-utils-plugin/public';
import { getFieldIconType } from '@kbn/field-utils/src/utils/get_field_icon_type';
import {
SHOW_MULTIFIELDS,
+ DOC_HIDE_TIME_COLUMN_SETTING,
formatFieldValue,
getIgnoredReason,
getShouldShowFieldHandler,
isNestedFieldParent,
usePager,
+ getVisibleColumns,
+ canPrependTimeFieldColumn,
} from '@kbn/discover-utils';
import { getTextBasedColumnIconType } from '@kbn/field-utils';
import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
@@ -72,6 +74,7 @@ const DEFAULT_PAGE_SIZE = 25;
const PINNED_FIELDS_KEY = 'discover:pinnedFields';
const PAGE_SIZE = 'discover:pageSize';
const HIDE_NULL_VALUES = 'unifiedDocViewer:hideNullValues';
+export const SHOW_ONLY_SELECTED_FIELDS = 'unifiedDocViewer:showOnlySelectedFields';
const GRID_COLUMN_FIELD_NAME = 'name';
const GRID_COLUMN_FIELD_VALUE = 'value';
@@ -143,8 +146,10 @@ export const DocViewerTable = ({
getPinnedFields(currentDataViewId, storage)
);
const [areNullValuesHidden, setAreNullValuesHidden] = useLocalStorage(HIDE_NULL_VALUES, false);
-
- const { euiTheme } = useEuiTheme();
+ const [showOnlySelectedFields, setShowOnlySelectedFields] = useLocalStorage(
+ SHOW_ONLY_SELECTED_FIELDS,
+ false
+ );
const flattened = hit.flattened;
const shouldShowFieldHandler = useMemo(
@@ -237,59 +242,97 @@ export const DocViewerTable = ({
]
);
+ const fieldsFromColumns = useMemo(
+ () => columns?.filter((column) => column !== '_source') || [],
+ [columns]
+ );
+
+ const isShowOnlySelectedFieldsDisabled = !fieldsFromColumns?.length;
+
+ const shouldShowOnlySelectedFields = useMemo(
+ () => showOnlySelectedFields && !isShowOnlySelectedFieldsDisabled,
+ [showOnlySelectedFields, isShowOnlySelectedFieldsDisabled]
+ );
+
+ const displayedFieldNames = useMemo(() => {
+ if (shouldShowOnlySelectedFields) {
+ return getVisibleColumns(
+ fieldsFromColumns,
+ dataView,
+ canPrependTimeFieldColumn(
+ columns,
+ dataView.timeFieldName,
+ columnsMeta,
+ !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false),
+ isEsqlMode
+ )
+ );
+ }
+ return Object.keys(flattened).sort((fieldA, fieldB) => {
+ const mappingA = mapping(fieldA);
+ const mappingB = mapping(fieldB);
+ const nameA = !mappingA || !mappingA.displayName ? fieldA : mappingA.displayName;
+ const nameB = !mappingB || !mappingB.displayName ? fieldB : mappingB.displayName;
+ return nameA.localeCompare(nameB);
+ });
+ }, [
+ fieldsFromColumns,
+ flattened,
+ shouldShowOnlySelectedFields,
+ mapping,
+ dataView,
+ columns,
+ columnsMeta,
+ isEsqlMode,
+ uiSettings,
+ ]);
+
const { pinnedItems, restItems, allFields } = useMemo(
() =>
- Object.keys(flattened)
- .sort((fieldA, fieldB) => {
- const mappingA = mapping(fieldA);
- const mappingB = mapping(fieldB);
- const nameA = !mappingA || !mappingA.displayName ? fieldA : mappingA.displayName;
- const nameB = !mappingB || !mappingB.displayName ? fieldB : mappingB.displayName;
- return nameA.localeCompare(nameB);
- })
- .reduce(
- (acc, curFieldName) => {
- if (!shouldShowFieldHandler(curFieldName)) {
- return acc;
- }
- const shouldHideNullValue =
- areNullValuesHidden && flattened[curFieldName] == null && isEsqlMode;
- if (shouldHideNullValue) {
- return acc;
- }
+ displayedFieldNames.reduce(
+ (acc, curFieldName) => {
+ if (!shouldShowOnlySelectedFields && !shouldShowFieldHandler(curFieldName)) {
+ return acc;
+ }
+ const shouldHideNullValue =
+ isEsqlMode && areNullValuesHidden && flattened[curFieldName] == null;
+ if (shouldHideNullValue) {
+ return acc;
+ }
- const isPinned = pinnedFields.includes(curFieldName);
- const row = fieldToItem(curFieldName, isPinned);
+ const isPinned = pinnedFields.includes(curFieldName);
+ const row = fieldToItem(curFieldName, isPinned);
- if (isPinned) {
- acc.pinnedItems.push(row);
- } else {
- if (onFilterField(curFieldName, row.field.displayName, row.field.fieldType)) {
- // filter only unpinned fields
- acc.restItems.push(row);
- }
+ if (isPinned) {
+ acc.pinnedItems.push(row);
+ } else {
+ if (onFilterField(curFieldName, row.field.displayName, row.field.fieldType)) {
+ // filter only unpinned fields
+ acc.restItems.push(row);
}
+ }
- acc.allFields.push({
- name: curFieldName,
- displayName: row.field.displayName,
- type: row.field.fieldType,
- });
+ acc.allFields.push({
+ name: curFieldName,
+ displayName: row.field.displayName,
+ type: row.field.fieldType,
+ });
- return acc;
- },
- {
- pinnedItems: [],
- restItems: [],
- allFields: [],
- }
- ),
+ return acc;
+ },
+ {
+ pinnedItems: [],
+ restItems: [],
+ allFields: [],
+ }
+ ),
[
+ displayedFieldNames,
areNullValuesHidden,
+ shouldShowOnlySelectedFields,
fieldToItem,
flattened,
isEsqlMode,
- mapping,
onFilterField,
pinnedFields,
shouldShowFieldHandler,
@@ -375,6 +418,13 @@ export const DocViewerTable = ({
[setAreNullValuesHidden]
);
+ const onShowOnlySelectedFieldsChange = useCallback(
+ (e: EuiSwitchEvent) => {
+ setShowOnlySelectedFields(e.target.checked);
+ },
+ [setShowOnlySelectedFields]
+ );
+
const renderCellValue: EuiDataGridProps['renderCellValue'] = useCallback(
({ rowIndex, columnId, isDetails }) => {
return (
@@ -446,31 +496,38 @@ export const DocViewerTable = ({
- {rows.length === 0 ? (
-
-
-
-
-
- ) : (
- <>
+
+
+
+
+
+
-
+
{isEsqlMode && (
-
+
)}
-
-
+
+
+
+
+
+
+ {rows.length === 0 ? (
+
+
+
-
- >
+
+
+ ) : (
+
+
+
)}
);
diff --git a/src/plugins/unified_doc_viewer/tsconfig.json b/src/plugins/unified_doc_viewer/tsconfig.json
index ef3a7a91153ac..eab6884b972ec 100644
--- a/src/plugins/unified_doc_viewer/tsconfig.json
+++ b/src/plugins/unified_doc_viewer/tsconfig.json
@@ -31,7 +31,6 @@
"@kbn/ui-theme",
"@kbn/discover-shared-plugin",
"@kbn/fields-metadata-plugin",
- "@kbn/unified-data-table",
"@kbn/core-notifications-browser",
"@kbn/deeplinks-observability",
"@kbn/share-plugin",
diff --git a/test/functional/apps/discover/group3/_doc_viewer.ts b/test/functional/apps/discover/group3/_doc_viewer.ts
index 6da0725978afe..3d3562e10beb4 100644
--- a/test/functional/apps/discover/group3/_doc_viewer.ts
+++ b/test/functional/apps/discover/group3/_doc_viewer.ts
@@ -233,6 +233,151 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
+ describe('show only selected fields in ES|QL mode', function () {
+ beforeEach(async () => {
+ await discover.selectTextBaseLang();
+ await header.waitUntilLoadingHasFinished();
+ await discover.waitUntilSearchingHasFinished();
+ });
+
+ it('should disable the switch when no fields are selected', async function () {
+ const testQuery = 'from logstash-* | sort @timestamp | limit 10';
+ await monacoEditor.setCodeEditorValue(testQuery);
+ await testSubjects.click('querySubmitButton');
+ await header.waitUntilLoadingHasFinished();
+ await discover.waitUntilSearchingHasFinished();
+ await dataGrid.clickRowToggle();
+ await discover.isShowingDocViewer();
+
+ const showOnlySelectedFieldsSwitch = await testSubjects.find(
+ 'unifiedDocViewerShowOnlySelectedFieldsSwitch'
+ );
+ expect(await showOnlySelectedFieldsSwitch.getAttribute('disabled')).to.be('true');
+
+ const fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ const fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+
+ expect(
+ fieldNames.join(',').startsWith('@message,@tags,@timestamp,agent,bytes,clientip')
+ ).to.be(true);
+ });
+
+ it('should allow toggling the switch', async function () {
+ const testQuery = 'from logstash-* | sort @timestamp | limit 10';
+ await monacoEditor.setCodeEditorValue(testQuery);
+ await testSubjects.click('querySubmitButton');
+ await header.waitUntilLoadingHasFinished();
+ await discover.waitUntilSearchingHasFinished();
+
+ await unifiedFieldList.clickFieldListItemAdd('agent.raw');
+ await header.waitUntilLoadingHasFinished();
+ await unifiedFieldList.clickFieldListItemAdd('agent');
+ await header.waitUntilLoadingHasFinished();
+ await discover.waitUntilSearchingHasFinished();
+
+ await dataGrid.clickRowToggle();
+ await discover.isShowingDocViewer();
+
+ const showOnlySelectedFieldsSwitch = await testSubjects.find(
+ 'unifiedDocViewerShowOnlySelectedFieldsSwitch'
+ );
+ expect(await showOnlySelectedFieldsSwitch.getAttribute('disabled')).to.be(null);
+
+ let fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ let fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+
+ expect(
+ fieldNames.join(',').startsWith('@message,@tags,@timestamp,agent,bytes,clientip')
+ ).to.be(true);
+
+ await showOnlySelectedFieldsSwitch.click();
+
+ await retry.waitFor('updates after switching to show only selected', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',') === 'agent.raw,agent';
+ });
+
+ await dataGrid.togglePinActionInFlyout('agent');
+
+ await retry.waitFor('updates after pinning the last field', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',') === 'agent,agent.raw';
+ });
+
+ await showOnlySelectedFieldsSwitch.click();
+
+ await retry.waitFor('updates after switching from showing only selected', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',').startsWith('agent,@message,@tags');
+ });
+ });
+ });
+
+ describe('show only selected fields in data view mode', function () {
+ it('should disable the switch when no fields are selected', async function () {
+ await dataGrid.clickRowToggle();
+ await discover.isShowingDocViewer();
+
+ const showOnlySelectedFieldsSwitch = await testSubjects.find(
+ 'unifiedDocViewerShowOnlySelectedFieldsSwitch'
+ );
+ expect(await showOnlySelectedFieldsSwitch.getAttribute('disabled')).to.be('true');
+
+ const fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ const fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+
+ expect(fieldNames.join(',').startsWith('_id,_ignored,_index,_score,@message')).to.be(true);
+ });
+
+ it('should allow toggling the switch', async function () {
+ await unifiedFieldList.clickFieldListItemAdd('bytes');
+ await header.waitUntilLoadingHasFinished();
+ await unifiedFieldList.clickFieldListItemAdd('@tags');
+ await header.waitUntilLoadingHasFinished();
+ await discover.waitUntilSearchingHasFinished();
+
+ await dataGrid.clickRowToggle();
+ await discover.isShowingDocViewer();
+
+ const showOnlySelectedFieldsSwitch = await testSubjects.find(
+ 'unifiedDocViewerShowOnlySelectedFieldsSwitch'
+ );
+ expect(await showOnlySelectedFieldsSwitch.getAttribute('disabled')).to.be(null);
+
+ let fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ let fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+
+ expect(fieldNames.join(',').startsWith('_id,_ignored,_index,_score,@message')).to.be(true);
+
+ await showOnlySelectedFieldsSwitch.click();
+
+ await retry.waitFor('updates after switching to show only selected', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',') === '@timestamp,bytes,@tags';
+ });
+
+ await dataGrid.togglePinActionInFlyout('bytes');
+
+ await retry.waitFor('updates after pinning the last field', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',') === 'bytes,@timestamp,@tags';
+ });
+
+ await showOnlySelectedFieldsSwitch.click();
+
+ await retry.waitFor('updates after switching from showing only selected', async () => {
+ fieldNameCells = await find.allByCssSelector('.kbnDocViewer__fieldName');
+ fieldNames = await Promise.all(fieldNameCells.map((cell) => cell.getVisibleText()));
+ return fieldNames.join(',').startsWith('bytes,_id,_ignored,_index,_score,@message');
+ });
+ });
+ });
+
describe('pinning fields', function () {
it('should be able to pin and unpin fields', async function () {
await dataGrid.clickRowToggle();