mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[UnifiedDataTable] Add density configuration (#188495)
## Summary Resolves https://github.com/elastic/kibana/issues/186007. Adds a density configuration for the `UnifiedDataTable`. By default, this configuration will not be shown unless an `onUpdateDataGridDensity`handler is passed to the `UnifiedDataTable`. It defaults to `compact`. It persists to `localStorage` when updated. https://github.com/user-attachments/assets/8523301c-7cf5-4854-bbbc-b6767d996c32 ### Checklist Delete any items that are not applicable to this PR. - [ ] 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) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] 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)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Bhavya RM <bhavya@elastic.co>
This commit is contained in:
parent
a0474aec39
commit
22dc10c7d0
43 changed files with 558 additions and 145 deletions
|
@ -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<string,(props: EuiDataGridCellValueElementProps) => 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<string,string> | Optional key/value pairs to set guided onboarding steps ids for a data table components included to guided tour. |
|
||||
| **componentsTourSteps** | (optional)Record<string,string> | 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:
|
||||
```
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ describe('CompareDocuments', () => {
|
|||
"data-test-subj": "unifiedDataTableCompareDocuments",
|
||||
"gridStyle": Object {
|
||||
"border": "horizontal",
|
||||
"cellPadding": "l",
|
||||
"cellPadding": "s",
|
||||
"fontSize": "s",
|
||||
"header": "underline",
|
||||
"rowHover": "highlight",
|
||||
|
|
|
@ -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}`;
|
||||
|
||||
|
|
|
@ -423,16 +423,18 @@ describe('UnifiedDataTable', () => {
|
|||
"additionalControls": null,
|
||||
"showColumnSelector": false,
|
||||
"showDisplaySelector": Object {
|
||||
"additionalDisplaySettings": <UnifiedDataTableAdditionalDisplaySettings
|
||||
headerRowHeight="custom"
|
||||
headerRowHeightLines={1}
|
||||
onChangeRowHeight={[Function]}
|
||||
onChangeRowHeightLines={[Function]}
|
||||
onChangeSampleSize={[MockFunction]}
|
||||
rowHeight="custom"
|
||||
rowHeightLines={3}
|
||||
sampleSize={150}
|
||||
/>,
|
||||
"additionalDisplaySettings": <React.Fragment>
|
||||
<UnifiedDataTableAdditionalDisplaySettings
|
||||
headerRowHeight="custom"
|
||||
headerRowHeightLines={1}
|
||||
onChangeRowHeight={[Function]}
|
||||
onChangeRowHeightLines={[Function]}
|
||||
onChangeSampleSize={[MockFunction]}
|
||||
rowHeight="custom"
|
||||
rowHeightLines={3}
|
||||
sampleSize={150}
|
||||
/>
|
||||
</React.Fragment>,
|
||||
"allowDensity": false,
|
||||
"allowResetButton": false,
|
||||
"allowRowHeight": false,
|
||||
|
@ -455,15 +457,17 @@ describe('UnifiedDataTable', () => {
|
|||
"additionalControls": null,
|
||||
"showColumnSelector": false,
|
||||
"showDisplaySelector": Object {
|
||||
"additionalDisplaySettings": <UnifiedDataTableAdditionalDisplaySettings
|
||||
headerRowHeight="custom"
|
||||
headerRowHeightLines={1}
|
||||
onChangeRowHeight={[Function]}
|
||||
onChangeRowHeightLines={[Function]}
|
||||
rowHeight="custom"
|
||||
rowHeightLines={3}
|
||||
sampleSize={200}
|
||||
/>,
|
||||
"additionalDisplaySettings": <React.Fragment>
|
||||
<UnifiedDataTableAdditionalDisplaySettings
|
||||
headerRowHeight="custom"
|
||||
headerRowHeightLines={1}
|
||||
onChangeRowHeight={[Function]}
|
||||
onChangeRowHeightLines={[Function]}
|
||||
rowHeight="custom"
|
||||
rowHeightLines={3}
|
||||
sampleSize={200}
|
||||
/>
|
||||
</React.Fragment>,
|
||||
"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();
|
||||
|
|
|
@ -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<EuiDataGridStyle>(
|
||||
() => ({
|
||||
...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;
|
||||
const showDisplaySelector = useMemo(():
|
||||
| EuiDataGridToolBarVisibilityDisplaySelectorOptions
|
||||
| undefined => {
|
||||
if (
|
||||
!onUpdateDataGridDensity &&
|
||||
!onUpdateRowHeight &&
|
||||
!onUpdateHeaderRowHeight &&
|
||||
!onUpdateSampleSize
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (onUpdateRowHeight || onUpdateHeaderRowHeight || onUpdateSampleSize) {
|
||||
options.allowRowHeight = false;
|
||||
options.allowResetButton = false;
|
||||
options.additionalDisplaySettings = (
|
||||
<UnifiedDataTableAdditionalDisplaySettings
|
||||
rowHeight={rowHeight}
|
||||
rowHeightLines={rowHeightLines}
|
||||
onChangeRowHeight={onChangeRowHeight}
|
||||
onChangeRowHeightLines={onChangeRowHeightLines}
|
||||
headerRowHeight={headerRowHeight}
|
||||
headerRowHeightLines={headerRowHeightLines}
|
||||
onChangeHeaderRowHeight={onChangeHeaderRowHeight}
|
||||
onChangeHeaderRowHeightLines={onChangeHeaderRowHeightLines}
|
||||
maxAllowedSampleSize={maxAllowedSampleSize}
|
||||
sampleSize={sampleSizeState}
|
||||
onChangeSampleSize={onUpdateSampleSize}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return Object.keys(options).length ? options : undefined;
|
||||
return {
|
||||
allowDensity: Boolean(onUpdateDataGridDensity),
|
||||
allowRowHeight: false,
|
||||
allowResetButton: false,
|
||||
additionalDisplaySettings: (
|
||||
<>
|
||||
{onUpdateDataGridDensity ? <EuiHorizontalRule margin="s" /> : null}
|
||||
<UnifiedDataTableAdditionalDisplaySettings
|
||||
rowHeight={rowHeight}
|
||||
rowHeightLines={rowHeightLines}
|
||||
onChangeRowHeight={onChangeRowHeight}
|
||||
onChangeRowHeightLines={onChangeRowHeightLines}
|
||||
headerRowHeight={headerRowHeight}
|
||||
headerRowHeightLines={headerRowHeightLines}
|
||||
onChangeHeaderRowHeight={onChangeHeaderRowHeight}
|
||||
onChangeHeaderRowHeightLines={onChangeHeaderRowHeightLines}
|
||||
maxAllowedSampleSize={maxAllowedSampleSize}
|
||||
sampleSize={sampleSizeState}
|
||||
onChangeSampleSize={onUpdateSampleSize}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
};
|
||||
}, [
|
||||
headerRowHeight,
|
||||
headerRowHeightLines,
|
||||
|
@ -992,6 +1031,7 @@ export const UnifiedDataTable = ({
|
|||
rowHeight,
|
||||
rowHeightLines,
|
||||
sampleSizeState,
|
||||
onUpdateDataGridDensity,
|
||||
]);
|
||||
|
||||
const inMemory = useMemo(() => {
|
||||
|
@ -1101,6 +1141,8 @@ export const UnifiedDataTable = ({
|
|||
/>
|
||||
) : (
|
||||
<EuiDataGridMemoized
|
||||
// Using this as the `key` is a workaround for https://github.com/elastic/eui/issues/7962. This forces a re-render if the density is changed.
|
||||
key={dataGridDensity}
|
||||
id={dataGridId}
|
||||
aria-describedby={randomId}
|
||||
aria-labelledby={ariaLabelledBy}
|
||||
|
@ -1118,7 +1160,7 @@ export const UnifiedDataTable = ({
|
|||
toolbarVisibility={toolbarVisibility}
|
||||
rowHeightsOptions={rowHeightsOptions}
|
||||
inMemory={inMemory}
|
||||
gridStyle={gridStyleOverride ?? GRID_STYLE}
|
||||
gridStyle={gridStyle}
|
||||
renderCustomGridBody={renderCustomGridBody}
|
||||
renderCustomToolbar={renderCustomToolbarFn}
|
||||
trailingControlColumns={trailingControlColumns}
|
||||
|
|
|
@ -37,6 +37,7 @@ export function SourceDocument({
|
|||
fieldFormats,
|
||||
dataTestSubj = 'discoverCellDescriptionList',
|
||||
className,
|
||||
isCompressed = true,
|
||||
}: {
|
||||
useTopLevelObjectColumns: boolean;
|
||||
row: DataTableRecord;
|
||||
|
@ -48,6 +49,7 @@ export function SourceDocument({
|
|||
fieldFormats: FieldFormatsStart;
|
||||
dataTestSubj?: string;
|
||||
className?: string;
|
||||
isCompressed?: boolean;
|
||||
}) {
|
||||
const pairs: FormattedHit = useTopLevelObjectColumns
|
||||
? getTopLevelObjectPairs(row.raw, columnId, dataView, shouldShowFieldHandler).slice(
|
||||
|
@ -59,7 +61,7 @@ export function SourceDocument({
|
|||
return (
|
||||
<EuiDescriptionList
|
||||
type="inline"
|
||||
compressed
|
||||
compressed={isCompressed}
|
||||
className={classnames('unifiedDataTable__descriptionList', CELL_CLASS, className)}
|
||||
data-test-subj={dataTestSubj}
|
||||
>
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
|
@ -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<DataGridDensity>(() => {
|
||||
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,
|
||||
};
|
||||
};
|
|
@ -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<
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
@ -134,6 +137,7 @@ export const getRenderCellValueFn = ({
|
|||
shouldShowFieldHandler={shouldShowFieldHandler}
|
||||
maxEntries={maxEntries}
|
||||
isPlainRecord={isPlainRecord}
|
||||
isCompressed={isCompressed}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</CellActionsProvider>
|
||||
</div>
|
||||
|
|
|
@ -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<DiscoverAppState, 'sort'> {
|
||||
|
|
|
@ -456,6 +456,7 @@ describe('Test discover state actions', () => {
|
|||
"columns": Array [
|
||||
"default_column",
|
||||
],
|
||||
"density": undefined,
|
||||
"headerRowHeight": undefined,
|
||||
"hideAggregatedPreview": undefined,
|
||||
"hideChart": undefined,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<number | undefined>(savedSearch.headerRowHeight),
|
||||
rowsPerPage: new BehaviorSubject<number | undefined>(savedSearch.rowsPerPage),
|
||||
sampleSize: new BehaviorSubject<number | undefined>(savedSearch.sampleSize),
|
||||
density: new BehaviorSubject<DataGridDensity | undefined>(savedSearch.density),
|
||||
grid: new BehaviorSubject<DiscoverGridSettings | undefined>(savedSearch.grid),
|
||||
rows: new BehaviorSubject<DataTableRecord[]>([]),
|
||||
totalHitCount: new BehaviorSubject<number | undefined>(0),
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ export const EDITABLE_SAVED_SEARCH_KEYS: Readonly<Array<keyof SavedSearchAttribu
|
|||
'sampleSize',
|
||||
'rowsPerPage',
|
||||
'headerRowHeight',
|
||||
'density',
|
||||
] as const;
|
||||
|
||||
/** This constant refers to the dashboard panel specific state */
|
||||
|
|
|
@ -20,7 +20,7 @@ import type {
|
|||
} from '@kbn/presentation-publishing';
|
||||
import { DiscoverGridSettings, SavedSearch } from '@kbn/saved-search-plugin/common';
|
||||
import { SortOrder, VIEW_MODE } from '@kbn/saved-search-plugin/public';
|
||||
import { DataTableColumnsMeta } from '@kbn/unified-data-table';
|
||||
import { DataGridDensity, DataTableColumnsMeta } from '@kbn/unified-data-table';
|
||||
|
||||
import { AggregateQuery, Filter, Query } from '@kbn/es-query';
|
||||
import { DiscoverServices } from '../build_services';
|
||||
|
@ -93,6 +93,7 @@ export const initializeSearchEmbeddableApi = async (
|
|||
const rowHeight$ = new BehaviorSubject<number | undefined>(initialState.rowHeight);
|
||||
const rowsPerPage$ = new BehaviorSubject<number | undefined>(initialState.rowsPerPage);
|
||||
const sampleSize$ = new BehaviorSubject<number | undefined>(initialState.sampleSize);
|
||||
const density$ = new BehaviorSubject<DataGridDensity | undefined>(initialState.density);
|
||||
const sort$ = new BehaviorSubject<SortOrder[] | undefined>(initialState.sort);
|
||||
const savedSearchViewMode$ = new BehaviorSubject<VIEW_MODE | undefined>(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)],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ export type SearchEmbeddableState = Pick<
|
|||
| 'sampleSize'
|
||||
| 'viewMode'
|
||||
| 'grid'
|
||||
| 'density'
|
||||
> & {
|
||||
rows: DataTableRecord[];
|
||||
columnsMeta: DataTableColumnsMeta | undefined;
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -46,5 +46,6 @@ export const fromSavedSearchAttributes = <
|
|||
sampleSize: attributes.sampleSize,
|
||||
breakdownField: attributes.breakdownField,
|
||||
visContext: attributes.visContext,
|
||||
density: attributes.density,
|
||||
managed,
|
||||
} as ReturnType);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,6 +45,7 @@ export class SavedSearchStorage extends SOContentStorage<SavedSearchCrudTypes> {
|
|||
'rowsPerPage',
|
||||
'breakdownField',
|
||||
'sampleSize',
|
||||
'density',
|
||||
'visContext',
|
||||
],
|
||||
logger,
|
||||
|
|
|
@ -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')])
|
||||
),
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"@kbn/utility-types",
|
||||
"@kbn/search-types",
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/unified-data-table",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 84 KiB |
|
@ -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"]'
|
||||
|
|
|
@ -68,7 +68,7 @@ describe('utils', () => {
|
|||
expect(tableStylesOverride).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"border": "horizontal",
|
||||
"cellPadding": "l",
|
||||
"cellPadding": "s",
|
||||
"fontSize": "s",
|
||||
"header": "underline",
|
||||
"rowClasses": Object {
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue