diff --git a/packages/kbn-unified-data-table/README.md b/packages/kbn-unified-data-table/README.md index 7a2db17781ad1..0049949f74490 100644 --- a/packages/kbn-unified-data-table/README.md +++ b/packages/kbn-unified-data-table/README.md @@ -50,7 +50,8 @@ Props description: | **visibleCellActions** | (optional)number | An optional value for a custom number of the visible cell actions in the table. By default is up to 3. | | **externalCustomRenderers** | (optional)Record React.ReactNode>; | An optional settings for a specified fields rendering like links. Applied only for the listed fields rendering. | | **consumer** | (optional)string | Name of the UnifiedDataTable consumer component or application. | -| **componentsTourSteps** | (optional)Record | Optional key/value pairs to set guided onboarding steps ids for a data table components included to guided tour. | +| **componentsTourSteps** | (optional)Record | Optional key/value pairs to set guided onboarding steps ids for a data table components included to guided tour. |~~~~ +| **onUpdateDataGridDensity** | (optional)(DataGridDensity) => void; | Optional callback when the data grid density configuration is modified. | *Required **services** list: ``` diff --git a/packages/kbn-unified-data-table/index.ts b/packages/kbn-unified-data-table/index.ts index 7dace83c3774e..9c62ef1a28abe 100644 --- a/packages/kbn-unified-data-table/index.ts +++ b/packages/kbn-unified-data-table/index.ts @@ -14,7 +14,7 @@ export { } from './src/components/row_height_settings'; export { getDisplayedColumns } from './src/utils/columns'; export { getTextBasedColumnsMeta } from './src/utils/get_columns_meta'; -export { ROWS_HEIGHT_OPTIONS } from './src/constants'; +export { ROWS_HEIGHT_OPTIONS, DataGridDensity } from './src/constants'; export { JSONCodeEditorCommonMemoized } from './src/components/json_code_editor/json_code_editor_common'; diff --git a/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.test.tsx b/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.test.tsx index 63856a2c82ac2..92f072c2da472 100644 --- a/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.test.tsx +++ b/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.test.tsx @@ -108,7 +108,7 @@ describe('CompareDocuments', () => { "data-test-subj": "unifiedDataTableCompareDocuments", "gridStyle": Object { "border": "horizontal", - "cellPadding": "l", + "cellPadding": "s", "fontSize": "s", "header": "underline", "rowHover": "highlight", diff --git a/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.tsx b/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.tsx index 5679ac0f236b9..e636a88d1f841 100644 --- a/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.tsx +++ b/packages/kbn-unified-data-table/src/components/compare_documents/compare_documents.tsx @@ -24,7 +24,7 @@ import { AdditionalFieldGroups } from '@kbn/unified-field-list'; import { memoize } from 'lodash'; import React, { useMemo, useState } from 'react'; import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { GRID_STYLE } from '../../constants'; +import { DATA_GRID_STYLE_DEFAULT } from '../../constants'; import { ComparisonControls } from './comparison_controls'; import { renderComparisonToolbar } from './comparison_toolbar'; import { useComparisonCellValue } from './hooks/use_comparison_cell_value'; @@ -55,7 +55,7 @@ export interface CompareDocumentsProps { const COMPARISON_ROW_HEIGHT: EuiDataGridRowHeightsOptions = { defaultHeight: 'auto' }; const COMPARISON_IN_MEMORY: EuiDataGridInMemory = { level: 'sorting' }; -const COMPARISON_GRID_STYLE: EuiDataGridStyle = { ...GRID_STYLE, stripes: undefined }; +const COMPARISON_GRID_STYLE: EuiDataGridStyle = { ...DATA_GRID_STYLE_DEFAULT, stripes: undefined }; const getStorageKey = (consumer: string, key: string) => `${consumer}:dataGridComparison${key}`; diff --git a/packages/kbn-unified-data-table/src/components/data_table.test.tsx b/packages/kbn-unified-data-table/src/components/data_table.test.tsx index f5749eaee4bf0..2538d3df38778 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.test.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table.test.tsx @@ -423,16 +423,18 @@ describe('UnifiedDataTable', () => { "additionalControls": null, "showColumnSelector": false, "showDisplaySelector": Object { - "additionalDisplaySettings": , + "additionalDisplaySettings": + + , "allowDensity": false, "allowResetButton": false, "allowRowHeight": false, @@ -455,15 +457,17 @@ describe('UnifiedDataTable', () => { "additionalControls": null, "showColumnSelector": false, "showDisplaySelector": Object { - "additionalDisplaySettings": , + "additionalDisplaySettings": + + , "allowDensity": false, "allowResetButton": false, "allowRowHeight": false, @@ -741,7 +745,7 @@ describe('UnifiedDataTable', () => { expect(grid.hasClass('euiDataGrid--bordersHorizontal')).toBeTruthy(); expect(grid.hasClass('euiDataGrid--fontSizeSmall')).toBeTruthy(); - expect(grid.hasClass('euiDataGrid--paddingLarge')).toBeTruthy(); + expect(grid.hasClass('euiDataGrid--paddingSmall')).toBeTruthy(); expect(grid.hasClass('euiDataGrid--rowHoverHighlight')).toBeTruthy(); expect(grid.hasClass('euiDataGrid--headerUnderline')).toBeTruthy(); expect(grid.hasClass('euiDataGrid--stripes')).toBeTruthy(); 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 5068115fed76d..778c41c6f5365 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table.tsx @@ -26,9 +26,10 @@ import { EuiDataGridInMemory, EuiDataGridControlColumn, EuiDataGridCustomBodyProps, - EuiDataGridToolBarVisibilityDisplaySelectorOptions, EuiDataGridStyle, EuiDataGridProps, + EuiHorizontalRule, + EuiDataGridToolBarVisibilityDisplaySelectorOptions, } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import { @@ -45,6 +46,7 @@ import type { ThemeServiceStart } from '@kbn/react-kibana-context-common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { AdditionalFieldGroups } from '@kbn/unified-field-list'; +import { DATA_GRID_DENSITY_STYLE_MAP, useDataGridDensity } from '../hooks/use_data_grid_density'; import { UnifiedDataTableSettings, ValueToStringConverter, @@ -70,10 +72,11 @@ import { getSchemaDetectors } from './data_table_schema'; import { DataTableDocumentToolbarBtn } from './data_table_document_selection'; import { useRowHeightsOptions } from '../hooks/use_row_heights_options'; import { + DATA_GRID_STYLE_DEFAULT, DEFAULT_ROWS_PER_PAGE, - GRID_STYLE, ROWS_HEIGHT_OPTIONS, toolbarVisibility as toolbarVisibilityDefaults, + DataGridDensity, } from '../constants'; import { UnifiedDataTableFooter } from './data_table_footer'; import { UnifiedDataTableAdditionalDisplaySettings } from './data_table_additional_display_settings'; @@ -232,6 +235,14 @@ export interface UnifiedDataTableProps { * Update row height state */ onUpdateRowHeight?: (rowHeight: number) => void; + /** + * Density from state + */ + dataGridDensityState?: DataGridDensity; + /** + * Callback when the data grid density configuration is modified + */ + onUpdateDataGridDensity?: (dataGridDensity: DataGridDensity) => void; /** * Is text base lang mode enabled */ @@ -468,6 +479,8 @@ export const UnifiedDataTable = ({ cellContext, renderCellPopover, getRowIndicator, + dataGridDensityState, + onUpdateDataGridDensity, }: UnifiedDataTableProps) => { const { fieldFormats, toastNotifications, dataViewFieldEditor, uiSettings, storage, data } = services; @@ -610,6 +623,23 @@ export const UnifiedDataTable = ({ return getShouldShowFieldHandler(dataViewFields, dataView, showMultiFields); }, [dataView, showMultiFields]); + const { dataGridDensity, onChangeDataGridDensity } = useDataGridDensity({ + storage, + consumer, + dataGridDensityState, + onUpdateDataGridDensity, + }); + + const gridStyle = useMemo( + () => ({ + ...DATA_GRID_STYLE_DEFAULT, + ...DATA_GRID_DENSITY_STYLE_MAP[dataGridDensity], + onChange: onChangeDataGridDensity, + ...gridStyleOverride, + }), + [dataGridDensity, onChangeDataGridDensity, gridStyleOverride] + ); + /** * Cell rendering */ @@ -625,6 +655,7 @@ export const UnifiedDataTable = ({ maxEntries: maxDocFieldsDisplayed, externalCustomRenderers, isPlainRecord, + isCompressed: dataGridDensity === DataGridDensity.COMPACT, }), [ dataView, @@ -635,6 +666,7 @@ export const UnifiedDataTable = ({ fieldFormats, externalCustomRenderers, isPlainRecord, + dataGridDensity, ] ); @@ -950,34 +982,41 @@ export const UnifiedDataTable = ({ [renderCustomToolbar, additionalControls] ); - const showDisplaySelector = useMemo(() => { - const options: EuiDataGridToolBarVisibilityDisplaySelectorOptions = {}; - - if (onUpdateRowHeight) { - options.allowDensity = false; - } - - if (onUpdateRowHeight || onUpdateHeaderRowHeight || onUpdateSampleSize) { - options.allowRowHeight = false; - options.allowResetButton = false; - options.additionalDisplaySettings = ( - - ); + const showDisplaySelector = useMemo((): + | EuiDataGridToolBarVisibilityDisplaySelectorOptions + | undefined => { + if ( + !onUpdateDataGridDensity && + !onUpdateRowHeight && + !onUpdateHeaderRowHeight && + !onUpdateSampleSize + ) { + return; } - return Object.keys(options).length ? options : undefined; + return { + allowDensity: Boolean(onUpdateDataGridDensity), + allowRowHeight: false, + allowResetButton: false, + additionalDisplaySettings: ( + <> + {onUpdateDataGridDensity ? : null} + + + ), + }; }, [ headerRowHeight, headerRowHeightLines, @@ -992,6 +1031,7 @@ export const UnifiedDataTable = ({ rowHeight, rowHeightLines, sampleSizeState, + onUpdateDataGridDensity, ]); const inMemory = useMemo(() => { @@ -1101,6 +1141,8 @@ export const UnifiedDataTable = ({ /> ) : ( diff --git a/packages/kbn-unified-data-table/src/constants.ts b/packages/kbn-unified-data-table/src/constants.ts index c7cf1793039a5..e795b170dbc0b 100644 --- a/packages/kbn-unified-data-table/src/constants.ts +++ b/packages/kbn-unified-data-table/src/constants.ts @@ -5,13 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { EuiDataGridStyle } from '@elastic/eui'; +import type { EuiDataGridStyle } from '@elastic/eui'; export const DEFAULT_CONTROL_COLUMN_WIDTH = 24; export const DEFAULT_ROWS_PER_PAGE = 100; export const MAX_LOADED_GRID_ROWS = 10000; - export const ROWS_PER_PAGE_OPTIONS = [10, 25, 50, DEFAULT_ROWS_PER_PAGE, 250, 500]; /** * Row height might be a value from -1 to 20 @@ -29,13 +28,27 @@ export const defaultMonacoEditorWidth = 370; export const defaultTimeColumnWidth = 212; export const kibanaJSON = 'kibana-json'; -export const GRID_STYLE: EuiDataGridStyle = { - border: 'horizontal', +export const DATA_GRID_STYLE_COMPACT: EuiDataGridStyle = { + cellPadding: 's', fontSize: 's', +}; + +export const DATA_GRID_STYLE_NORMAL: EuiDataGridStyle = { + cellPadding: 'm', + fontSize: 'm', +}; + +export const DATA_GRID_STYLE_EXPANDED: EuiDataGridStyle = { cellPadding: 'l', + fontSize: 'l', +}; + +export const DATA_GRID_STYLE_DEFAULT: EuiDataGridStyle = { + ...DATA_GRID_STYLE_COMPACT, + border: 'horizontal', + stripes: true, rowHover: 'highlight', header: 'underline', - stripes: true, }; export const toolbarVisibility = { @@ -44,3 +57,9 @@ export const toolbarVisibility = { allowReorder: true, }, }; + +export enum DataGridDensity { + COMPACT = 'compact', + NORMAL = 'normal', + EXPANDED = 'expanded', +} diff --git a/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.test.tsx b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.test.tsx new file mode 100644 index 0000000000000..8a6e3c96477ea --- /dev/null +++ b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.test.tsx @@ -0,0 +1,75 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDataGridDensity } from './use_data_grid_density'; +import { DATA_GRID_STYLE_EXPANDED, DataGridDensity } from '../constants'; + +const localStorageMock = { + get: jest.fn(), + set: jest.fn(), +}; + +describe('useDataGridDensity', () => { + beforeEach(() => { + localStorageMock.get.mockClear(); + localStorageMock.set.mockClear(); + }); + + it('should read from local storage', () => { + localStorageMock.get.mockReturnValue(DataGridDensity.NORMAL); + const { result } = renderHook(() => + useDataGridDensity({ + storage: localStorageMock as unknown as Storage, + consumer: 'discover', + }) + ); + const { + current: { dataGridDensity }, + } = result; + expect(dataGridDensity).toBe(DataGridDensity.NORMAL); + }); + + it('should update local storage when onChangeDataGridDensity is called', () => { + const { result } = renderHook(() => + useDataGridDensity({ + storage: localStorageMock as unknown as Storage, + consumer: 'discover', + }) + ); + const { + current: { onChangeDataGridDensity }, + } = result; + + onChangeDataGridDensity(DATA_GRID_STYLE_EXPANDED); + + expect(localStorageMock.set).toBeCalledWith( + 'discover:dataGridDensity', + DataGridDensity.EXPANDED + ); + }); + + it('should call provided onUpdateDataGridDensity with the updated value', () => { + const onUpdateDataGridDensity = jest.fn(); + const { result } = renderHook(() => + useDataGridDensity({ + storage: localStorageMock as unknown as Storage, + consumer: 'discover', + onUpdateDataGridDensity, + }) + ); + const { + current: { onChangeDataGridDensity }, + } = result; + + onChangeDataGridDensity(DATA_GRID_STYLE_EXPANDED); + + expect(onUpdateDataGridDensity).toBeCalledWith(DataGridDensity.EXPANDED); + }); +}); diff --git a/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts new file mode 100644 index 0000000000000..a53f95e23fb85 --- /dev/null +++ b/packages/kbn-unified-data-table/src/hooks/use_data_grid_density.ts @@ -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 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 or the Server + * Side Public License, v 1. + */ + +import type { EuiDataGridStyle } from '@elastic/eui'; +import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import { useCallback, useMemo } from 'react'; +import { + DATA_GRID_STYLE_COMPACT, + DATA_GRID_STYLE_EXPANDED, + DATA_GRID_STYLE_NORMAL, + DataGridDensity, +} from '../constants'; + +export const DATA_GRID_DENSITY_STYLE_MAP = { + [DataGridDensity.COMPACT]: DATA_GRID_STYLE_COMPACT, + [DataGridDensity.NORMAL]: DATA_GRID_STYLE_NORMAL, + [DataGridDensity.EXPANDED]: DATA_GRID_STYLE_EXPANDED, +}; + +interface UseDataGridDensityProps { + storage: Storage; + consumer: string; + dataGridDensityState?: DataGridDensity; + onUpdateDataGridDensity?: (density: DataGridDensity) => void; +} + +export const DATA_GRID_DENSITY_STORAGE_KEY = 'dataGridDensity'; + +export function getDensityFromStyle(style: EuiDataGridStyle) { + return style.cellPadding === DATA_GRID_STYLE_COMPACT.cellPadding && + style.fontSize === DATA_GRID_STYLE_COMPACT.fontSize + ? DataGridDensity.COMPACT + : style.cellPadding === DATA_GRID_STYLE_NORMAL.cellPadding && + style.fontSize === DATA_GRID_STYLE_NORMAL.fontSize + ? DataGridDensity.NORMAL + : DataGridDensity.EXPANDED; +} + +export const useDataGridDensity = ({ + storage, + consumer, + dataGridDensityState, + onUpdateDataGridDensity, +}: UseDataGridDensityProps) => { + const storageKey = `${consumer}:${DATA_GRID_DENSITY_STORAGE_KEY}`; + const dataGridDensity = useMemo(() => { + return dataGridDensityState ?? storage.get(storageKey) ?? DataGridDensity.COMPACT; + }, [dataGridDensityState, storage, storageKey]); + + const onChangeDataGridDensity = useCallback( + (gridStyle: EuiDataGridStyle) => { + const newDensity = getDensityFromStyle(gridStyle); + storage.set(storageKey, newDensity); + onUpdateDataGridDensity?.(newDensity); + }, + [storageKey, storage, onUpdateDataGridDensity] + ); + + return { + dataGridDensity, + onChangeDataGridDensity, + }; +}; diff --git a/packages/kbn-unified-data-table/src/types.ts b/packages/kbn-unified-data-table/src/types.ts index b08818e1a861a..f06c4c4096c0c 100644 --- a/packages/kbn-unified-data-table/src/types.ts +++ b/packages/kbn-unified-data-table/src/types.ts @@ -19,6 +19,8 @@ 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 { DataGridDensity } from './constants'; + /** * User configurable state of data grid, persisted in saved search */ @@ -58,6 +60,7 @@ export type DataGridCellValueElementProps = EuiDataGridCellValueElementProps & { dataView: DataView; fieldFormats: FieldFormatsStart; closePopover: () => void; + isCompressed?: boolean; }; export type CustomCellRenderer = Record< diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx index 04d160bef5c2e..5d9894e9bcd2c 100644 --- a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx +++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx @@ -224,6 +224,7 @@ describe('Unified data table cell rendering', function () { maxEntries: 100, shouldShowFieldHandler: showFieldHandler, row: rows[0], + isCompressed: true, }); }); @@ -329,6 +330,7 @@ describe('Unified data table cell rendering', function () { shouldShowFieldHandler: showFieldHandler, row: rows[0], isPlainRecord: true, + isCompressed: true, }); }); @@ -367,6 +369,7 @@ describe('Unified data table cell rendering', function () { maxEntries: 100, shouldShowFieldHandler: showFieldHandler, row: rows[0], + isCompressed: true, }); }); @@ -406,6 +409,7 @@ describe('Unified data table cell rendering', function () { maxEntries: 1, shouldShowFieldHandler: showFieldHandler, row: rows[0], + isCompressed: true, }); }); @@ -519,6 +523,7 @@ describe('Unified data table cell rendering', function () { shouldShowFieldHandler: showFieldHandler, useTopLevelObjectColumns: true, row: rows[0], + isCompressed: true, }); }); @@ -559,6 +564,7 @@ describe('Unified data table cell rendering', function () { shouldShowFieldHandler: showFieldHandler, useTopLevelObjectColumns: true, row: rows[0], + isCompressed: true, }); }); diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx index 6a10b0950946d..1b856d665d301 100644 --- a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx +++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.tsx @@ -36,6 +36,7 @@ export const getRenderCellValueFn = ({ maxEntries, externalCustomRenderers, isPlainRecord, + isCompressed = true, }: { dataView: DataView; rows: DataTableRecord[] | undefined; @@ -46,6 +47,7 @@ export const getRenderCellValueFn = ({ maxEntries: number; externalCustomRenderers?: CustomCellRenderer; isPlainRecord?: boolean; + isCompressed?: boolean; }) => { return function UnifiedDataTableRenderCellValue({ rowIndex, @@ -95,6 +97,7 @@ export const getRenderCellValueFn = ({ dataView={dataView} fieldFormats={fieldFormats} closePopover={closePopover} + isCompressed={isCompressed} /> ); @@ -134,6 +137,7 @@ export const getRenderCellValueFn = ({ shouldShowFieldHandler={shouldShowFieldHandler} maxEntries={maxEntries} isPlainRecord={isPlainRecord} + isCompressed={isCompressed} /> ); } diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index d01051ed5fc78..bcc7d33f38c39 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -147,7 +147,7 @@ describe('checking migration metadata changes on all registered SO types', () => "risk-engine-configuration": "aea0c371a462e6d07c3ceb3aff11891b47feb09d", "rules-settings": "892a2918ebaeba809a612b8d97cec0b07c800b5f", "sample-data-telemetry": "37441b12f5b0159c2d6d5138a494c9f440e950b5", - "search": "4579401660a4089d5122b2fc8624825cb97b0480", + "search": "0aa6eefb37edd3145be340a8b67779c2ca578b22", "search-session": "b2fcd840e12a45039ada50b1355faeafa39876d1", "search-telemetry": "b568601618744720b5662946d3103e3fb75fe8ee", "security-rule": "07abb4d7e707d91675ec0495c73816394c7b521f", diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index e1b5636d010b1..ea8b8ead62455 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -27,6 +27,7 @@ import { type DataTableColumnsMeta, getTextBasedColumnsMeta, getRenderCustomToolbarWithElements, + type DataGridDensity, } from '@kbn/unified-data-table'; import { DOC_HIDE_TIME_COLUMN_SETTING, @@ -110,19 +111,29 @@ function DiscoverDocumentsComponent({ const documents$ = stateContainer.dataState.data$.documents$; const savedSearch = useSavedSearchInitial(); const { dataViews, capabilities, uiSettings, uiActions } = services; - const [query, sort, rowHeight, headerRowHeight, rowsPerPage, grid, columns, sampleSizeState] = - useAppStateSelector((state) => { - return [ - state.query, - state.sort, - state.rowHeight, - state.headerRowHeight, - state.rowsPerPage, - state.grid, - state.columns, - state.sampleSize, - ]; - }); + const [ + query, + sort, + rowHeight, + headerRowHeight, + rowsPerPage, + grid, + columns, + sampleSizeState, + density, + ] = useAppStateSelector((state) => { + return [ + state.query, + state.sort, + state.rowHeight, + state.headerRowHeight, + state.rowsPerPage, + state.grid, + state.columns, + state.sampleSize, + state.density, + ]; + }); const expandedDoc = useInternalStateSelector((state) => state.expandedDoc); const isEsqlMode = useIsEsqlMode(); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); @@ -219,6 +230,13 @@ function DiscoverDocumentsComponent({ [stateContainer] ); + const onUpdateDensity = useCallback( + (newDensity: DataGridDensity) => { + stateContainer.appState.update({ density: newDensity }); + }, + [stateContainer] + ); + // should be aligned with embeddable `showTimeCol` prop const showTimeCol = useMemo( () => !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), @@ -437,6 +455,8 @@ function DiscoverDocumentsComponent({ customGridColumnsConfiguration={customGridColumnsConfiguration} rowAdditionalLeadingControls={rowAdditionalLeadingControls} additionalFieldGroups={additionalFieldGroups} + dataGridDensityState={density} + onUpdateDataGridDensity={onUpdateDensity} /> diff --git a/src/plugins/discover/public/application/main/state_management/discover_app_state_container.ts b/src/plugins/discover/public/application/main/state_management/discover_app_state_container.ts index f364530faba5e..35463c091109d 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_app_state_container.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_app_state_container.ts @@ -26,6 +26,7 @@ import { IKbnUrlStateStorage, ISyncStateRef, syncState } from '@kbn/kibana-utils import { isEqual, omit } from 'lodash'; import { connectToQueryState, syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; +import type { DataGridDensity } from '@kbn/unified-data-table'; import type { DiscoverServices } from '../../../build_services'; import { addLog } from '../../../utils/add_log'; import { cleanupUrlState } from './utils/cleanup_url_state'; @@ -155,6 +156,10 @@ export interface DiscoverAppState { * Breakdown field of chart */ breakdownField?: string; + /** + * Density of table + */ + density?: DataGridDensity; } export interface AppStateUrl extends Omit { diff --git a/src/plugins/discover/public/application/main/state_management/discover_state.test.ts b/src/plugins/discover/public/application/main/state_management/discover_state.test.ts index d9acde1645fcf..cd8e2c47bd02f 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_state.test.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_state.test.ts @@ -456,6 +456,7 @@ describe('Test discover state actions', () => { "columns": Array [ "default_column", ], + "density": undefined, "headerRowHeight": undefined, "hideAggregatedPreview": undefined, "hideChart": undefined, diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts index 86d9ffe99c244..8e264ea779c33 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts @@ -32,6 +32,7 @@ describe('getStateDefaults', () => { "dataViewId": "index-pattern-with-timefield-id", "type": "dataView", }, + "density": undefined, "filters": undefined, "grid": undefined, "headerRowHeight": undefined, @@ -71,6 +72,7 @@ describe('getStateDefaults', () => { "dataViewId": "the-data-view-id", "type": "dataView", }, + "density": undefined, "filters": undefined, "grid": undefined, "headerRowHeight": undefined, diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts index 7e44adf64dbc1..81ec433da249a 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts @@ -81,6 +81,7 @@ export function getStateDefaults({ sampleSize: undefined, grid: undefined, breakdownField: undefined, + density: undefined, }; if (savedSearch.grid) { @@ -113,6 +114,9 @@ export function getStateDefaults({ if (savedSearch.breakdownField) { defaultState.breakdownField = savedSearch.breakdownField; } + if (savedSearch.density) { + defaultState.density = savedSearch.density; + } return defaultState; } diff --git a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts index 6638e511f6b2f..b767cde6c4a92 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts @@ -69,6 +69,7 @@ export function updateSavedSearch({ savedSearch.headerRowHeight = state.headerRowHeight; savedSearch.rowsPerPage = state.rowsPerPage; savedSearch.sampleSize = state.sampleSize; + savedSearch.density = state.density; if (state.viewMode) { savedSearch.viewMode = state.viewMode; diff --git a/src/plugins/discover/public/embeddable/__mocks__/get_mocked_api.ts b/src/plugins/discover/public/embeddable/__mocks__/get_mocked_api.ts index 0c324930e1f85..ea5ba55d8eccf 100644 --- a/src/plugins/discover/public/embeddable/__mocks__/get_mocked_api.ts +++ b/src/plugins/discover/public/embeddable/__mocks__/get_mocked_api.ts @@ -17,7 +17,7 @@ import { DatatableColumnMeta } from '@kbn/expressions-plugin/common'; import { FetchContext } from '@kbn/presentation-publishing'; import { DiscoverGridSettings, SavedSearch, VIEW_MODE } from '@kbn/saved-search-plugin/common'; import { SearchResponseIncompleteWarning } from '@kbn/search-response-warnings/src/types'; -import { SortOrder } from '@kbn/unified-data-table'; +import type { SortOrder, DataGridDensity } from '@kbn/unified-data-table'; export const getMockedSearchApi = ({ searchSource, @@ -52,6 +52,7 @@ export const getMockedSearchApi = ({ headerRowHeight: new BehaviorSubject(savedSearch.headerRowHeight), rowsPerPage: new BehaviorSubject(savedSearch.rowsPerPage), sampleSize: new BehaviorSubject(savedSearch.sampleSize), + density: new BehaviorSubject(savedSearch.density), grid: new BehaviorSubject(savedSearch.grid), rows: new BehaviorSubject([]), totalHitCount: new BehaviorSubject(0), diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx index 55ed59d30f1ae..6f6ce57a78fd0 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx @@ -22,7 +22,7 @@ import { } from '@kbn/presentation-publishing'; import { SortOrder } from '@kbn/saved-search-plugin/public'; import { SearchResponseIncompleteWarning } from '@kbn/search-response-warnings/src/types'; -import { columnActions, DataLoadingState } from '@kbn/unified-data-table'; +import { columnActions, DataGridDensity, DataLoadingState } from '@kbn/unified-data-table'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { DiscoverDocTableEmbeddable } from '../../components/doc_table/create_doc_table_embeddable'; @@ -137,6 +137,9 @@ export function SearchEmbeddableGridComponent({ onUpdateSampleSize: (newSampleSize: number | undefined) => { stateManager.sampleSize.next(newSampleSize); }, + onUpdateDataGridDensity: (newDensity: DataGridDensity | undefined) => { + stateManager.density.next(newDensity); + }, }), [stateManager, savedSearch.columns] ); @@ -194,6 +197,7 @@ export function SearchEmbeddableGridComponent({ searchTitle={panelTitle || savedSearchTitle} services={discoverServices} showTimeCol={!discoverServices.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false)} + dataGridDensityState={savedSearch.density} /> ); } diff --git a/src/plugins/discover/public/embeddable/constants.ts b/src/plugins/discover/public/embeddable/constants.ts index 7ada36e3a005d..2574c7abf2a8e 100644 --- a/src/plugins/discover/public/embeddable/constants.ts +++ b/src/plugins/discover/public/embeddable/constants.ts @@ -31,6 +31,7 @@ export const EDITABLE_SAVED_SEARCH_KEYS: Readonly(initialState.rowHeight); const rowsPerPage$ = new BehaviorSubject(initialState.rowsPerPage); const sampleSize$ = new BehaviorSubject(initialState.sampleSize); + const density$ = new BehaviorSubject(initialState.density); const sort$ = new BehaviorSubject(initialState.sort); const savedSearchViewMode$ = new BehaviorSubject(initialState.viewMode); @@ -129,6 +130,7 @@ export const initializeSearchEmbeddableApi = async ( sort: sort$, totalHitCount: totalHitCount$, viewMode: savedSearchViewMode$, + density: density$, }; /** The saved search should be the source of truth for all state */ @@ -197,6 +199,7 @@ export const initializeSearchEmbeddableApi = async ( ], viewMode: [savedSearchViewMode$, (value) => savedSearchViewMode$.next(value)], grid: [grid$, (value) => grid$.next(value)], + density: [density$, (value) => density$.next(value)], }, }; }; diff --git a/src/plugins/discover/public/embeddable/types.ts b/src/plugins/discover/public/embeddable/types.ts index 8ff429e6476f9..70a929d1dee60 100644 --- a/src/plugins/discover/public/embeddable/types.ts +++ b/src/plugins/discover/public/embeddable/types.ts @@ -41,6 +41,7 @@ export type SearchEmbeddableState = Pick< | 'sampleSize' | 'viewMode' | 'grid' + | 'density' > & { rows: DataTableRecord[]; columnsMeta: DataTableColumnsMeta | undefined; diff --git a/src/plugins/saved_search/common/content_management/v1/cm_services.ts b/src/plugins/saved_search/common/content_management/v1/cm_services.ts index ef9d24bb8722d..7a3f8eb6d5e9d 100644 --- a/src/plugins/saved_search/common/content_management/v1/cm_services.ts +++ b/src/plugins/saved_search/common/content_management/v1/cm_services.ts @@ -72,6 +72,13 @@ const savedSearchAttributesSchema = schema.object( max: MAX_SAVED_SEARCH_SAMPLE_SIZE, }) ), + density: schema.maybe( + schema.oneOf([ + schema.literal('compact'), + schema.literal('normal'), + schema.literal('expanded'), + ]) + ), breakdownField: schema.maybe(schema.string()), visContext: schema.maybe( schema.oneOf([ diff --git a/src/plugins/saved_search/common/saved_searches_utils.ts b/src/plugins/saved_search/common/saved_searches_utils.ts index 232d5286ba32a..1151830f0d8ab 100644 --- a/src/plugins/saved_search/common/saved_searches_utils.ts +++ b/src/plugins/saved_search/common/saved_searches_utils.ts @@ -46,5 +46,6 @@ export const fromSavedSearchAttributes = < sampleSize: attributes.sampleSize, breakdownField: attributes.breakdownField, visContext: attributes.visContext, + density: attributes.density, managed, } as ReturnType); diff --git a/src/plugins/saved_search/common/service/get_saved_searches.test.ts b/src/plugins/saved_search/common/service/get_saved_searches.test.ts index 6572d6addbbbb..4f7d288e185d5 100644 --- a/src/plugins/saved_search/common/service/get_saved_searches.test.ts +++ b/src/plugins/saved_search/common/service/get_saved_searches.test.ts @@ -88,6 +88,7 @@ describe('getSavedSearch', () => { "columns": Array [ "_source", ], + "density": undefined, "description": "description", "grid": Object {}, "headerRowHeight": undefined, @@ -197,6 +198,7 @@ describe('getSavedSearch', () => { "columns": Array [ "_source", ], + "density": undefined, "description": "description", "grid": Object {}, "headerRowHeight": undefined, diff --git a/src/plugins/saved_search/common/service/saved_searches_utils.test.ts b/src/plugins/saved_search/common/service/saved_searches_utils.test.ts index ce8c073dd27c9..96845d7fea7fb 100644 --- a/src/plugins/saved_search/common/service/saved_searches_utils.test.ts +++ b/src/plugins/saved_search/common/service/saved_searches_utils.test.ts @@ -47,6 +47,7 @@ describe('saved_searches_utils', () => { "a", "b", ], + "density": undefined, "description": "foo", "grid": Object {}, "headerRowHeight": undefined, @@ -124,6 +125,7 @@ describe('saved_searches_utils', () => { "c", "d", ], + "density": undefined, "description": "description", "grid": Object {}, "headerRowHeight": undefined, diff --git a/src/plugins/saved_search/common/service/saved_searches_utils.ts b/src/plugins/saved_search/common/service/saved_searches_utils.ts index 866b9876d4dd4..24fcda3255a8c 100644 --- a/src/plugins/saved_search/common/service/saved_searches_utils.ts +++ b/src/plugins/saved_search/common/service/saved_searches_utils.ts @@ -52,6 +52,7 @@ export const toSavedSearchAttributes = ( refreshInterval: savedSearch.refreshInterval, rowsPerPage: savedSearch.rowsPerPage, sampleSize: savedSearch.sampleSize, + density: savedSearch.density, breakdownField: savedSearch.breakdownField, visContext: savedSearch.visContext, }); diff --git a/src/plugins/saved_search/common/types.ts b/src/plugins/saved_search/common/types.ts index fe41fd66042fe..4c5bab04705e5 100644 --- a/src/plugins/saved_search/common/types.ts +++ b/src/plugins/saved_search/common/types.ts @@ -15,6 +15,7 @@ import type { import type { SavedObjectReference } from '@kbn/core-saved-objects-server'; import type { SavedObjectsResolveResponse } from '@kbn/core/server'; import type { SerializableRecord } from '@kbn/utility-types'; +import type { DataGridDensity } from '@kbn/unified-data-table'; import { VIEW_MODE } from '.'; export interface DiscoverGridSettings extends SerializableRecord { @@ -64,6 +65,7 @@ export interface SavedSearchAttributes { rowsPerPage?: number; sampleSize?: number; breakdownField?: string; + density?: DataGridDensity; visContext?: VisContextUnmapped; } diff --git a/src/plugins/saved_search/public/services/saved_searches/to_saved_search.test.ts b/src/plugins/saved_search/public/services/saved_searches/to_saved_search.test.ts index a319188a49f63..968c29831e73b 100644 --- a/src/plugins/saved_search/public/services/saved_searches/to_saved_search.test.ts +++ b/src/plugins/saved_search/public/services/saved_searches/to_saved_search.test.ts @@ -52,72 +52,73 @@ describe('toSavedSearch', () => { }; const savedSearch = await byValueToSavedSearch({ attributes }, mockServices); expect(savedSearch).toMatchInlineSnapshot(` - Object { - "breakdownField": undefined, - "columns": Array [ - "message", - "extension", - ], - "description": "", - "grid": Object {}, - "headerRowHeight": undefined, - "hideAggregatedPreview": undefined, - "hideChart": false, - "id": undefined, - "isTextBasedQuery": false, - "managed": false, - "references": Array [ - Object { - "id": "1", - "name": "ref_0", - "type": "index-pattern", - }, - ], - "refreshInterval": undefined, - "rowHeight": undefined, - "rowsPerPage": undefined, - "sampleSize": undefined, - "searchSource": Object { - "create": [MockFunction], - "createChild": [MockFunction], - "createCopy": [MockFunction], - "destroy": [MockFunction], - "fetch": [MockFunction], - "fetch$": [MockFunction], - "getActiveIndexFilter": [MockFunction], - "getField": [MockFunction], - "getFields": [MockFunction], - "getId": [MockFunction], - "getOwnField": [MockFunction], - "getParent": [MockFunction], - "getSearchRequestBody": [MockFunction], - "getSerializedFields": [MockFunction], - "history": Array [], - "loadDataViewFields": [MockFunction], - "onRequestStart": [MockFunction], - "parseActiveIndexPatternFromQueryString": [MockFunction], - "removeField": [MockFunction], - "serialize": [MockFunction], - "setField": [MockFunction], - "setOverwriteDataViewType": [MockFunction], - "setParent": [MockFunction], - "toExpressionAst": [MockFunction], + Object { + "breakdownField": undefined, + "columns": Array [ + "message", + "extension", + ], + "density": undefined, + "description": "", + "grid": Object {}, + "headerRowHeight": undefined, + "hideAggregatedPreview": undefined, + "hideChart": false, + "id": undefined, + "isTextBasedQuery": false, + "managed": false, + "references": Array [ + Object { + "id": "1", + "name": "ref_0", + "type": "index-pattern", }, - "sharingSavedObjectProps": undefined, - "sort": Array [ - Array [ - "@timestamp", - "desc", - ], + ], + "refreshInterval": undefined, + "rowHeight": undefined, + "rowsPerPage": undefined, + "sampleSize": undefined, + "searchSource": Object { + "create": [MockFunction], + "createChild": [MockFunction], + "createCopy": [MockFunction], + "destroy": [MockFunction], + "fetch": [MockFunction], + "fetch$": [MockFunction], + "getActiveIndexFilter": [MockFunction], + "getField": [MockFunction], + "getFields": [MockFunction], + "getId": [MockFunction], + "getOwnField": [MockFunction], + "getParent": [MockFunction], + "getSearchRequestBody": [MockFunction], + "getSerializedFields": [MockFunction], + "history": Array [], + "loadDataViewFields": [MockFunction], + "onRequestStart": [MockFunction], + "parseActiveIndexPatternFromQueryString": [MockFunction], + "removeField": [MockFunction], + "serialize": [MockFunction], + "setField": [MockFunction], + "setOverwriteDataViewType": [MockFunction], + "setParent": [MockFunction], + "toExpressionAst": [MockFunction], + }, + "sharingSavedObjectProps": undefined, + "sort": Array [ + Array [ + "@timestamp", + "desc", ], - "tags": undefined, - "timeRange": undefined, - "timeRestore": undefined, - "title": "saved-search-title", - "usesAdHocDataView": undefined, - "viewMode": undefined, - "visContext": undefined, - } - `); + ], + "tags": undefined, + "timeRange": undefined, + "timeRestore": undefined, + "title": "saved-search-title", + "usesAdHocDataView": undefined, + "viewMode": undefined, + "visContext": undefined, + } + `); }); }); diff --git a/src/plugins/saved_search/server/content_management/saved_search_storage.ts b/src/plugins/saved_search/server/content_management/saved_search_storage.ts index d3ff8633637d2..3982bca7cf932 100644 --- a/src/plugins/saved_search/server/content_management/saved_search_storage.ts +++ b/src/plugins/saved_search/server/content_management/saved_search_storage.ts @@ -45,6 +45,7 @@ export class SavedSearchStorage extends SOContentStorage { 'rowsPerPage', 'breakdownField', 'sampleSize', + 'density', 'visContext', ], logger, diff --git a/src/plugins/saved_search/server/saved_objects/schema.ts b/src/plugins/saved_search/server/saved_objects/schema.ts index 125ddcceb320c..effbc6f7eeb24 100644 --- a/src/plugins/saved_search/server/saved_objects/schema.ts +++ b/src/plugins/saved_search/server/saved_objects/schema.ts @@ -129,3 +129,9 @@ export const SCHEMA_SEARCH_MODEL_VERSION_4 = SCHEMA_SEARCH_MODEL_VERSION_3.exten ]) ), }); + +export const SCHEMA_SEARCH_MODEL_VERSION_5 = SCHEMA_SEARCH_MODEL_VERSION_4.extends({ + density: schema.maybe( + schema.oneOf([schema.literal('compact'), schema.literal('normal'), schema.literal('expanded')]) + ), +}); diff --git a/src/plugins/saved_search/server/saved_objects/search.ts b/src/plugins/saved_search/server/saved_objects/search.ts index 925a4dd66b180..1295b4b378d98 100644 --- a/src/plugins/saved_search/server/saved_objects/search.ts +++ b/src/plugins/saved_search/server/saved_objects/search.ts @@ -16,6 +16,7 @@ import { SCHEMA_SEARCH_MODEL_VERSION_2, SCHEMA_SEARCH_MODEL_VERSION_3, SCHEMA_SEARCH_MODEL_VERSION_4, + SCHEMA_SEARCH_MODEL_VERSION_5, } from './schema'; export function getSavedSearchObjectType( @@ -70,6 +71,13 @@ export function getSavedSearchObjectType( create: SCHEMA_SEARCH_MODEL_VERSION_4, }, }, + 5: { + changes: [], + schemas: { + forwardCompatibility: SCHEMA_SEARCH_MODEL_VERSION_5.extends({}, { unknowns: 'ignore' }), + create: SCHEMA_SEARCH_MODEL_VERSION_5, + }, + }, }, mappings: { dynamic: false, diff --git a/src/plugins/saved_search/tsconfig.json b/src/plugins/saved_search/tsconfig.json index 7100affb2e0c3..f96d7b385aa2c 100644 --- a/src/plugins/saved_search/tsconfig.json +++ b/src/plugins/saved_search/tsconfig.json @@ -28,6 +28,7 @@ "@kbn/utility-types", "@kbn/search-types", "@kbn/discover-utils", + "@kbn/unified-data-table", ], "exclude": ["target/**/*"] } diff --git a/test/functional/apps/discover/group2_data_grid3/_data_grid_density.ts b/test/functional/apps/discover/group2_data_grid3/_data_grid_density.ts new file mode 100644 index 0000000000000..45105fe907a0f --- /dev/null +++ b/test/functional/apps/discover/group2_data_grid3/_data_grid_density.ts @@ -0,0 +1,96 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['settings', 'common', 'discover', 'header', 'timePicker']); + const defaultSettings = { defaultIndex: 'logstash-*' }; + const security = getService('security'); + + describe('discover data grid density', function () { + before(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); + await browser.setWindowSize(1200, 2000); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + }); + + after(async () => { + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.uiSettings.replace({}); + await kibanaServer.savedObjects.cleanStandardList(); + }); + + beforeEach(async function () { + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + }); + + it('should use the default density', async () => { + await dataGrid.clickGridSettings(); + const selected = await dataGrid.getCurrentDensityValue(); + expect(selected).to.be('Compact'); + }); + + it('should allow to change density', async () => { + await dataGrid.clickGridSettings(); + await dataGrid.changeDensityValue('Normal'); + + // toggle the popover + // Right now changing the density closes the popover (see packages/kbn-unified-data-table/src/components/data_table.tsx:1144) + // When that workaround is removed we will need to uncomment this next line + // await dataGrid.clickGridSettings(); + await dataGrid.clickGridSettings(); + expect(await dataGrid.getCurrentDensityValue()).to.be('Normal'); + }); + + it('should persist the density selection after reloading the page', async () => { + await dataGrid.clickGridSettings(); + await dataGrid.changeDensityValue('Expanded'); + await dataGrid.clickGridSettings(); + expect(await dataGrid.getCurrentDensityValue()).to.be('Expanded'); + + await browser.refresh(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await dataGrid.clickGridSettings(); + expect(await dataGrid.getCurrentDensityValue()).to.be('Expanded'); + }); + + it('should save and revert unsaved densities properly', async () => { + // Open saved search + await PageObjects.discover.loadSavedSearch('A Saved Search'); + + // Change density + await dataGrid.clickGridSettings(); + await dataGrid.changeDensityValue('Expanded'); + await PageObjects.discover.saveUnsavedChanges(); + + // Change density + await dataGrid.clickGridSettings(); + await dataGrid.changeDensityValue('Normal'); + await testSubjects.existOrFail('unsavedChangesBadge'); + + // Revert change + await PageObjects.discover.revertUnsavedChanges(); + + // Verify density reset + await dataGrid.clickGridSettings(); + expect(await dataGrid.getCurrentDensityValue()).to.be('Expanded'); + }); + }); +} diff --git a/test/functional/apps/discover/group2_data_grid3/index.ts b/test/functional/apps/discover/group2_data_grid3/index.ts index b7a5e25a491b5..e3e0fd5b9d2be 100644 --- a/test/functional/apps/discover/group2_data_grid3/index.ts +++ b/test/functional/apps/discover/group2_data_grid3/index.ts @@ -25,5 +25,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_data_grid_row_selection')); loadTestFile(require.resolve('./_data_grid_sample_size')); loadTestFile(require.resolve('./_data_grid_pagination')); + loadTestFile(require.resolve('./_data_grid_density')); }); } diff --git a/test/functional/apps/discover/group5/_url_state.ts b/test/functional/apps/discover/group5/_url_state.ts index 099fbed1589a3..2516f2ed08659 100644 --- a/test/functional/apps/discover/group5/_url_state.ts +++ b/test/functional/apps/discover/group5/_url_state.ts @@ -179,6 +179,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Sep 22, 2015 @ 20:40:22.952jpg1,576', 'Sep 22, 2015 @ 20:11:39.532png1,708', 'Sep 22, 2015 @ 19:45:13.813php1,406', + 'Sep 22, 2015 @ 19:40:17.903jpg1,557', ]); expect(await PageObjects.discover.getHitCount()).to.be(totalHitsForOneFilter); @@ -204,6 +205,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Sep 22, 2015 @ 18:50:22.335css1,841', 'Sep 22, 2015 @ 18:40:32.329css1,945', 'Sep 22, 2015 @ 18:13:35.361css1,752', + 'Sep 22, 2015 @ 17:22:12.782css1,583', ]; expect(await dataGrid.getRowsText()).to.eql(filteredRows); diff --git a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png index 94de1a1c3cc4f..2f5e437ade253 100644 Binary files a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png and b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png differ diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index fc6ed65631eaa..aa79b36260a96 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -512,6 +512,18 @@ export class DataGridService extends FtrService { await option.click(); } + public async getCurrentDensityValue() { + const buttonGroup = await this.testSubjects.find('densityButtonGroup'); + const selectedButton = await buttonGroup.findByCssSelector('[aria-pressed=true]'); + return selectedButton.getVisibleText(); + } + + public async changeDensityValue(newValue: string) { + const buttonGroup = await this.testSubjects.find('densityButtonGroup'); + const option = await buttonGroup.findByCssSelector(`[data-text="${newValue}"]`); + await option.click(); + } + private async findSampleSizeInput() { return await this.find.byCssSelector( 'input[type="number"][data-test-subj="unifiedDataTableSampleSizeInput"]' diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.test.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.test.ts index af346e9addabd..08a62bea9275a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.test.ts @@ -68,7 +68,7 @@ describe('utils', () => { expect(tableStylesOverride).toMatchInlineSnapshot(` Object { "border": "horizontal", - "cellPadding": "l", + "cellPadding": "s", "fontSize": "s", "header": "underline", "rowClasses": Object { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.ts index f76b3c45c303e..3679d9a251ebc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/utils.ts @@ -9,7 +9,7 @@ import type { EuiDataGridStyle } from '@elastic/eui'; import { flattenHit } from '@kbn/data-service'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DataTableRecord } from '@kbn/discover-utils/types'; -import { GRID_STYLE } from '@kbn/unified-data-table/src/constants'; +import { DATA_GRID_STYLE_DEFAULT } from '@kbn/unified-data-table/src/constants'; import type { TimelineItem } from '../../../../../common/search_strategy'; import { getEventTypeRowClassName } from './data_table/get_event_type_row_classname'; @@ -56,5 +56,8 @@ export function transformTimelineItemToUnifiedRows( }; }); - return { tableRows: unifiedDataTableRows, tableStylesOverride: { ...GRID_STYLE, rowClasses } }; + return { + tableRows: unifiedDataTableRows, + tableStylesOverride: { ...DATA_GRID_STYLE_DEFAULT, rowClasses }, + }; }