Skip to content

Commit

Permalink
[OneDiscover][UnifiedDocViewer] Add filtering for selected fields (el…
Browse files Browse the repository at this point in the history
…astic#191930)

- Closes elastic#191536

## Summary

This PR adds "Selected fields only" toggle to UnifiedDocViewer. The
selected state will be persisted under
`unifiedDocViewer:showOnlySelectedFields` in Local Storage.

<img width="1779" alt="Screenshot 2024-09-11 at 10 08 26"
src="https://github.com/user-attachments/assets/db68a64c-bb0a-4a83-9eba-d1497b35416e">




### 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 <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent 9150bfd commit 3a74700
Show file tree
Hide file tree
Showing 21 changed files with 759 additions and 346 deletions.
2 changes: 2 additions & 0 deletions packages/kbn-discover-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export {
LogLevelCoalescedValue,
LogLevelBadge,
getFieldValue,
getVisibleColumns,
canPrependTimeFieldColumn,
} from './src';

export type { LogsContextService } from './src';
Expand Down
6 changes: 6 additions & 0 deletions packages/kbn-discover-utils/src/__mocks__/data_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
12 changes: 12 additions & 0 deletions packages/kbn-discover-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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,
Expand Down
195 changes: 195 additions & 0 deletions packages/kbn-discover-utils/src/utils/get_visible_columns.test.tsx
Original file line number Diff line number Diff line change
@@ -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);
}
});
});
});
});
49 changes: 49 additions & 0 deletions packages/kbn-discover-utils/src/utils/get_visible_columns.ts
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions packages/kbn-discover-utils/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
3 changes: 2 additions & 1 deletion packages/kbn-discover-utils/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@kbn/field-types",
"@kbn/i18n",
"@kbn/core-ui-settings-browser",
"@kbn/ui-theme"
"@kbn/ui-theme",
"@kbn/expressions-plugin"
]
}
1 change: 1 addition & 0 deletions packages/kbn-discover-utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

export type {
DataTableRecord,
DataTableColumnsMeta,
EsHitRecord,
IgnoredReason,
ShouldShowFieldInTableHandler,
Expand Down
8 changes: 5 additions & 3 deletions packages/kbn-unified-data-table/src/components/data_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down
Loading

0 comments on commit 3a74700

Please sign in to comment.