[Discover] Add row height options for document explorer (#122087)

Enable configuring of the row height using document explorer in Discover

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: andreadelrio <andrea.delrio@elastic.co>
This commit is contained in:
Dmitry Tomashevich 2022-02-02 09:24:46 +03:00 committed by GitHub
parent a6c7d968e5
commit a7e0171dfa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 393 additions and 54 deletions

View file

@ -22,4 +22,5 @@ export const MAX_DOC_FIELDS_DISPLAYED = 'discover:maxDocFieldsDisplayed';
export const SHOW_FIELD_STATISTICS = 'discover:showFieldStatistics';
export const SHOW_MULTIFIELDS = 'discover:showMultiFields';
export const TRUNCATE_MAX_HEIGHT = 'truncate:maxHeight';
export const ROW_HEIGHT_OPTION = 'discover:rowHeightOption';
export const SEARCH_EMBEDDABLE_TYPE = 'search';

View file

@ -20,6 +20,7 @@ import {
import { UI_SETTINGS } from '../../../data/common';
import { TopNavMenu } from '../../../navigation/public';
import { FORMATS_UI_SETTINGS } from 'src/plugins/field_formats/common';
import { LocalStorageMock } from './local_storage_mock';
const dataPlugin = dataPluginMock.createStartContract();
export const discoverServiceMock = {
@ -94,8 +95,6 @@ export const discoverServiceMock = {
useChartsTheme: jest.fn(() => EUI_CHARTS_THEME_LIGHT.theme),
useChartsBaseTheme: jest.fn(() => EUI_CHARTS_THEME_LIGHT.theme),
},
storage: {
get: jest.fn(),
},
storage: new LocalStorageMock({}) as unknown as Storage,
addBasePath: jest.fn(),
} as unknown as DiscoverServices;

View file

@ -14,6 +14,7 @@ import {
SAMPLE_SIZE_SETTING,
SHOW_MULTIFIELDS,
SEARCH_FIELDS_FROM_SOURCE,
ROW_HEIGHT_OPTION,
} from '../../common';
export const uiSettingsMock = {
@ -30,6 +31,8 @@ export const uiSettingsMock = {
return false;
} else if (key === SHOW_MULTIFIELDS) {
return false;
} else if (key === ROW_HEIGHT_OPTION) {
return 3;
}
},
} as unknown as IUiSettingsClient;

View file

@ -19,6 +19,7 @@ import { indexPatternsMock } from '../../__mocks__/index_patterns';
import { act } from 'react-dom/test-utils';
import { uiSettingsMock } from '../../__mocks__/ui_settings';
import { themeServiceMock } from '../../../../../core/public/mocks';
import { LocalStorageMock } from '../../__mocks__/local_storage_mock';
import { KibanaContextProvider } from '../../../../kibana_react/public';
const mockFilterManager = createFilterManagerMock();
@ -55,6 +56,7 @@ describe('ContextApp test', () => {
},
filterManager: mockFilterManager,
uiSettings: uiSettingsMock,
storage: new LocalStorageMock({}),
} as unknown as DiscoverServices;
const defaultProps = {

View file

@ -10,7 +10,7 @@ import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { findTestSubject } from '@elastic/eui/lib/test';
import { ActionBar } from './components/action_bar/action_bar';
import { AppState, GetStateReturn } from './services/context_state';
import { GetStateReturn } from './services/context_state';
import { SortDirection } from 'src/plugins/data/common';
import { ContextAppContent, ContextAppContentProps } from './context_app_content';
import { LoadingStatus } from './services/context_query_state';
@ -52,7 +52,6 @@ describe('ContextAppContent test', () => {
const props = {
columns: ['order_date', '_source'],
indexPattern: indexPatternMock,
appState: {} as unknown as AppState,
stateContainer: {} as unknown as GetStateReturn,
anchorStatus: anchorStatus || LoadingStatus.LOADED,
predecessorsStatus: LoadingStatus.LOADED,

View file

@ -43,7 +43,7 @@ function mountComponent(fetchStatus: FetchStatus, hits: ElasticSearchHit[]) {
searchSource: documents$,
setExpandedDoc: jest.fn(),
state: { columns: [] },
stateContainer: {} as GetStateReturn,
stateContainer: { setAppState: () => {} } as unknown as GetStateReturn,
navigateTo: jest.fn(),
};

View file

@ -99,6 +99,13 @@ function DiscoverDocumentsComponent({
[stateContainer]
);
const onUpdateRowHeight = useCallback(
(newRowHeight: number) => {
stateContainer.setAppState({ rowHeight: newRowHeight });
},
[stateContainer]
);
const showTimeCol = useMemo(
() => !uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false) && !!indexPattern.timeFieldName,
[uiSettings, indexPattern.timeFieldName]
@ -170,6 +177,8 @@ function DiscoverDocumentsComponent({
onSort={onSort}
onResize={onResize}
useNewFieldsApi={useNewFieldsApi}
rowHeightState={state.rowHeight}
onUpdateRowHeight={onUpdateRowHeight}
/>
</div>
)}

View file

@ -33,31 +33,27 @@ import { RequestAdapter } from '../../../../../../inspector';
import { Chart } from '../chart/point_series';
import { DiscoverSidebar } from '../sidebar/discover_sidebar';
import { ElasticSearchHit } from '../../../../types';
import { LocalStorageMock } from 'src/plugins/discover/public/__mocks__/local_storage_mock';
import { KibanaContextProvider } from '../../../../../../kibana_react/public';
import { FieldFormatsStart } from '../../../../../../field_formats/public';
import { IUiSettingsClient } from 'kibana/public';
import { DiscoverServices } from '../../../../build_services';
setHeaderActionMenuMounter(jest.fn());
function mountComponent(indexPattern: DataView, prevSidebarClosed?: boolean) {
const searchSourceMock = createSearchSourceMock({});
const services = discoverServiceMock;
const services = {
...discoverServiceMock,
fieldFormats: {
getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })),
getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })),
},
storage: new LocalStorageMock({
[SIDEBAR_CLOSED_KEY]: prevSidebarClosed,
}) as unknown as Storage,
} as unknown as DiscoverServices;
services.data.query.timefilter.timefilter.getAbsoluteTime = () => {
return { from: '2020-05-14T11:05:13.590', to: '2020-05-14T11:20:13.590' };
};
services.storage.get = (key: string) => {
if (key === SIDEBAR_CLOSED_KEY) {
return prevSidebarClosed;
}
};
services.fieldFormats = {
getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })),
getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })),
} as unknown as FieldFormatsStart;
services.uiSettings = {
...services.uiSettings,
get: jest.fn((key: string) => key === 'discover:maxDocFieldsDisplayed' && 50),
} as unknown as IUiSettingsClient;
const indexPatternList = [indexPattern].map((ip) => {
return { ...ip, ...{ attributes: { title: ip.title } } };
@ -153,7 +149,7 @@ function mountComponent(indexPattern: DataView, prevSidebarClosed?: boolean) {
savedSearchRefetch$: new Subject(),
searchSource: searchSourceMock,
state: { columns: [] },
stateContainer: {} as GetStateReturn,
stateContainer: { setAppState: () => {} } as unknown as GetStateReturn,
setExpandedDoc: jest.fn(),
};

View file

@ -82,6 +82,10 @@ export interface AppState {
* Hide mini distribution/preview charts when in Field Statistics mode
*/
hideAggregatedPreview?: boolean;
/**
* Document explorer row height option
*/
rowHeight?: number;
}
interface GetStateParams {

View file

@ -36,6 +36,7 @@ describe('getStateDefaults', () => {
"index": "index-pattern-with-timefield-id",
"interval": "auto",
"query": undefined,
"rowHeight": undefined,
"savedQuery": undefined,
"sort": Array [
Array [
@ -68,6 +69,7 @@ describe('getStateDefaults', () => {
"index": "the-index-pattern-id",
"interval": "auto",
"query": undefined,
"rowHeight": undefined,
"savedQuery": undefined,
"sort": Array [],
"viewMode": undefined,

View file

@ -63,6 +63,7 @@ export function getStateDefaults({
viewMode: undefined,
hideAggregatedPreview: undefined,
savedQuery: undefined,
rowHeight: undefined,
} as AppState;
if (savedSearch.grid) {
defaultState.grid = savedSearch.grid;
@ -70,6 +71,9 @@ export function getStateDefaults({
if (savedSearch.hideChart !== undefined) {
defaultState.hideChart = savedSearch.hideChart;
}
if (savedSearch.rowHeight !== undefined) {
defaultState.rowHeight = savedSearch.rowHeight;
}
if (savedSearch.viewMode) {
defaultState.viewMode = savedSearch.viewMode;
}

View file

@ -51,6 +51,9 @@ export async function persistSavedSearch(
if (typeof state.hideChart !== 'undefined') {
savedSearch.hideChart = state.hideChart;
}
if (typeof state.rowHeight !== 'undefined') {
savedSearch.rowHeight = state.rowHeight;
}
if (state.viewMode) {
savedSearch.viewMode = state.viewMode;

View file

@ -99,6 +99,11 @@ export function useDiscoverState({
useNewFieldsApi,
});
/**
* Reset to display loading spinner when savedSearch is changing
*/
useEffect(() => reset(), [savedSearch.id, reset]);
/**
* Sync URL state with local app state on saved search load
* or dataView / savedSearch switch

View file

@ -6,14 +6,16 @@
* Side Public License, v 1.
*/
import { EuiDataGridStyle } from '@elastic/eui';
// data types
export const kibanaJSON = 'kibana-json';
export const gridStyle = {
export const GRID_STYLE = {
border: 'all',
fontSize: 's',
cellPadding: 's',
rowHover: 'none',
};
} as EuiDataGridStyle;
export const pageSizeArr = [25, 50, 100, 250];
export const defaultPageSize = 100;
@ -23,7 +25,6 @@ export const toolbarVisibility = {
allowHide: false,
allowReorder: true,
},
showDisplaySelector: false,
};
export const defaultMonacoEditorWidth = 370;

View file

@ -11,7 +11,6 @@ import { FormattedMessage } from '@kbn/i18n-react';
import './discover_grid.scss';
import {
EuiDataGridSorting,
EuiDataGridStyle,
EuiDataGridProps,
EuiDataGrid,
EuiScreenReaderOnly,
@ -35,7 +34,7 @@ import {
} from './discover_grid_columns';
import {
defaultPageSize,
gridStyle,
GRID_STYLE,
pageSizeArr,
toolbarVisibility as toolbarVisibilityDefaults,
} from './constants';
@ -49,6 +48,7 @@ import { DiscoverGridDocumentToolbarBtn, getDocId } from './discover_grid_docume
import { SortPairArr } from '../doc_table/lib/get_sort';
import { getFieldsToShow } from '../../utils/get_fields_to_show';
import { ElasticSearchHit } from '../../types';
import { useRowHeightsOptions } from '../../utils/use_row_heights_options';
import { useDiscoverServices } from '../../utils/use_discover_services';
interface SortObj {
@ -154,6 +154,14 @@ export interface DiscoverGridProps {
* List of used control columns (available: 'openDetails', 'select')
*/
controlColumnIds?: string[];
/**
* Row height from state
*/
rowHeightState?: number;
/**
* Update row height state
*/
onUpdateRowHeight?: (rowHeight: number) => void;
}
export const EuiDataGridMemoized = React.memo((props: EuiDataGridProps) => {
@ -187,6 +195,8 @@ export const DiscoverGrid = ({
isPaginationEnabled = true,
controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT,
className,
rowHeightState,
onUpdateRowHeight,
}: DiscoverGridProps) => {
const services = useDiscoverServices();
const [selectedDocs, setSelectedDocs] = useState<string[]>([]);
@ -349,6 +359,17 @@ export const DiscoverGrid = ({
[usedSelectedDocs, isFilterActive, rows, setIsFilterActive]
);
const showDisplaySelector = useMemo(
() =>
!!onUpdateRowHeight
? {
allowDensity: false,
allowRowHeight: true,
}
: undefined,
[onUpdateRowHeight]
);
const toolbarVisibility = useMemo(
() =>
defaultColumns
@ -357,15 +378,22 @@ export const DiscoverGrid = ({
showColumnSelector: false,
showSortSelector: isSortEnabled,
additionalControls,
showDisplaySelector,
}
: {
...toolbarVisibilityDefaults,
showSortSelector: isSortEnabled,
additionalControls,
showDisplaySelector,
},
[defaultColumns, additionalControls, isSortEnabled]
[showDisplaySelector, defaultColumns, additionalControls, isSortEnabled]
);
const rowHeightsOptions = useRowHeightsOptions({
rowHeightState,
onUpdateRowHeight,
});
if (!rowCount && isLoading) {
return (
<div className="euiDataGrid__loading">
@ -423,7 +451,6 @@ export const DiscoverGrid = ({
columns={euiGridColumns}
columnVisibility={columnsVisibility}
data-test-subj="docTable"
gridStyle={gridStyle as EuiDataGridStyle}
leadingControlColumns={lead}
onColumnResize={onResize}
pagination={paginationObj}
@ -432,6 +459,8 @@ export const DiscoverGrid = ({
schemaDetectors={schemaDetectors}
sorting={sorting as EuiDataGridSorting}
toolbarVisibility={toolbarVisibility}
rowHeightsOptions={rowHeightsOptions}
gridStyle={GRID_STYLE}
/>
{showDisclaimer && (

View file

@ -62,6 +62,7 @@ export type SearchProps = Partial<DiscoverGridProps> &
hits?: ElasticSearchHit[];
totalHitCount?: number;
onMoveColumn?: (column: string, index: number) => void;
onUpdateRowHeight?: (rowHeight?: number) => void;
};
interface SearchEmbeddableConfig {
@ -293,6 +294,10 @@ export class SavedSearchEmbeddable
useNewFieldsApi: !this.services.uiSettings.get(SEARCH_FIELDS_FROM_SOURCE, false),
showTimeCol: !this.services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false),
ariaLabelledBy: 'documentsAriaLabel',
rowHeightState: this.input.rowHeight || this.savedSearch.rowHeight,
onUpdateRowHeight: (rowHeight) => {
this.updateInput({ rowHeight });
},
};
const timeRangeSearchSource = searchSource.create();
@ -342,6 +347,7 @@ export class SavedSearchEmbeddable
);
searchProps.sort = this.input.sort || savedSearchSort;
searchProps.sharedItemTitle = this.panelTitle;
searchProps.rowHeightState = this.input.rowHeight || this.savedSearch.rowHeight;
if (forceFetch || isFetchRequired) {
this.filtersSearchSource.setField('filter', this.input.filters);
this.filtersSearchSource.setField('query', this.input.query);
@ -414,9 +420,9 @@ export class SavedSearchEmbeddable
}
const useLegacyTable = this.services.uiSettings.get(DOC_TABLE_LEGACY);
const props = {
savedSearch: this.savedSearch,
searchProps,
useLegacyTable,
refs: domNode,
};
if (searchProps.services) {
ReactDOM.render(

View file

@ -16,7 +16,6 @@ import { SearchProps } from './saved_search_embeddable';
interface SavedSearchEmbeddableComponentProps {
searchProps: SearchProps;
useLegacyTable: boolean;
refs: HTMLElement;
}
const DiscoverDocTableEmbeddableMemoized = React.memo(DiscoverDocTableEmbeddable);
@ -25,15 +24,14 @@ const DiscoverGridEmbeddableMemoized = React.memo(DiscoverGridEmbeddable);
export function SavedSearchEmbeddableComponent({
searchProps,
useLegacyTable,
refs,
}: SavedSearchEmbeddableComponentProps) {
if (useLegacyTable) {
const docTableProps = {
...searchProps,
refs,
};
return <DiscoverDocTableEmbeddableMemoized {...(docTableProps as DocTableEmbeddableProps)} />;
return <DiscoverDocTableEmbeddableMemoized {...(searchProps as DocTableEmbeddableProps)} />;
}
const discoverGridProps = searchProps as DiscoverGridEmbeddableProps;
return <DiscoverGridEmbeddableMemoized {...discoverGridProps} className="dscDiscoverGrid" />;
return (
<DiscoverGridEmbeddableMemoized
{...(searchProps as DiscoverGridEmbeddableProps)}
className="dscDiscoverGrid"
/>
);
}

View file

@ -7,7 +7,6 @@
*/
import React, { useState, memo } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n-react';
import { DiscoverGrid, DiscoverGridProps } from '../components/discover_grid/discover_grid';
import { TotalDocuments } from '../application/main/components/total_documents/total_documents';
import { ElasticSearchHit } from '../types';
@ -22,17 +21,15 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) {
const [expandedDoc, setExpandedDoc] = useState<ElasticSearchHit | undefined>(undefined);
return (
<I18nProvider>
<EuiFlexGroup style={{ width: '100%' }} direction="column" gutterSize="xs" responsive={false}>
{props.totalHitCount !== 0 && (
<EuiFlexItem grow={false} style={{ alignSelf: 'flex-end' }}>
<TotalDocuments totalHitCount={props.totalHitCount} />
</EuiFlexItem>
)}
<EuiFlexItem style={{ minHeight: 0 }}>
<DataGridMemoized {...props} setExpandedDoc={setExpandedDoc} expandedDoc={expandedDoc} />
<EuiFlexGroup style={{ width: '100%' }} direction="column" gutterSize="xs" responsive={false}>
{props.totalHitCount !== 0 && (
<EuiFlexItem grow={false} style={{ alignSelf: 'flex-end' }}>
<TotalDocuments totalHitCount={props.totalHitCount} />
</EuiFlexItem>
</EuiFlexGroup>
</I18nProvider>
)}
<EuiFlexItem style={{ minHeight: 0 }}>
<DataGridMemoized {...props} setExpandedDoc={setExpandedDoc} expandedDoc={expandedDoc} />
</EuiFlexItem>
</EuiFlexGroup>
);
}

View file

@ -23,6 +23,7 @@ export interface SearchInput extends EmbeddableInput {
hidePanelTitles?: boolean;
columns?: string[];
sort?: SortOrder[];
rowHeight?: number;
}
export interface SearchOutput extends EmbeddableOutput {

View file

@ -104,6 +104,7 @@ describe('getSavedSearch', () => {
"hideAggregatedPreview": undefined,
"hideChart": false,
"id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7",
"rowHeight": undefined,
"searchSource": Object {
"create": [MockFunction],
"createChild": [MockFunction],

View file

@ -57,6 +57,7 @@ describe('saved_searches_utils', () => {
"hideAggregatedPreview": undefined,
"hideChart": true,
"id": "id",
"rowHeight": undefined,
"searchSource": SearchSource {
"dependencies": Object {
"getConfig": [MockFunction],
@ -130,6 +131,7 @@ describe('saved_searches_utils', () => {
"kibanaSavedObjectMeta": Object {
"searchSourceJSON": "{}",
},
"rowHeight": undefined,
"sort": Array [
Array [
"a",

View file

@ -43,6 +43,7 @@ export const fromSavedSearchAttributes = (
hideChart: attributes.hideChart,
viewMode: attributes.viewMode,
hideAggregatedPreview: attributes.hideAggregatedPreview,
rowHeight: attributes.rowHeight,
});
export const toSavedSearchAttributes = (
@ -58,4 +59,5 @@ export const toSavedSearchAttributes = (
hideChart: savedSearch.hideChart ?? false,
viewMode: savedSearch.viewMode,
hideAggregatedPreview: savedSearch.hideAggregatedPreview,
rowHeight: savedSearch.rowHeight,
});

View file

@ -25,6 +25,7 @@ export interface SavedSearchAttributes {
};
viewMode?: VIEW_MODE;
hideAggregatedPreview?: boolean;
rowHeight?: number;
}
/** @internal **/
@ -49,4 +50,5 @@ export interface SavedSearch {
};
viewMode?: VIEW_MODE;
hideAggregatedPreview?: boolean;
rowHeight?: number;
}

View file

@ -0,0 +1,41 @@
/*
* 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 { Storage } from '../../../kibana_utils/public';
import { isValidRowHeight } from './validate_row_height';
export interface DataGridOptionsRecord {
previousRowHeight: number;
previousConfigRowHeight: number;
}
const ROW_HEIGHT_KEY = 'discover:dataGridRowHeight';
export const getStoredRowHeight = (storage: Storage): DataGridOptionsRecord | null => {
const entry = storage.get(ROW_HEIGHT_KEY);
if (
typeof entry === 'object' &&
entry !== null &&
isValidRowHeight(entry.previousRowHeight) &&
isValidRowHeight(entry.previousConfigRowHeight)
) {
return entry;
}
return null;
};
export const updateStoredRowHeight = (
newRowHeight: number,
configRowHeight: number,
storage: Storage
) => {
storage.set(ROW_HEIGHT_KEY, {
previousRowHeight: newRowHeight,
previousConfigRowHeight: configRowHeight,
});
};

View file

@ -0,0 +1,107 @@
/*
* 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 React, { ReactNode } from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { Storage } from '../../../kibana_utils/public';
import { DiscoverServices } from '../build_services';
import { LocalStorageMock } from '../__mocks__/local_storage_mock';
import { uiSettingsMock } from '../__mocks__/ui_settings';
import { useRowHeightsOptions } from './use_row_heights_options';
import { KibanaContextProvider } from '../../../kibana_react/public';
const CONFIG_ROW_HEIGHT = 3;
const getWrapper = (services: DiscoverServices) => {
return ({ children }: { children: ReactNode }) => (
<KibanaContextProvider services={services}>{children}</KibanaContextProvider>
);
};
describe('useRowHeightsOptions', () => {
test('should apply rowHeight from savedSearch', () => {
const { result } = renderHook(
() => {
return useRowHeightsOptions({
rowHeightState: 2,
});
},
{
wrapper: getWrapper({
uiSettings: uiSettingsMock,
storage: new LocalStorageMock({}) as unknown as Storage,
} as DiscoverServices),
}
);
expect(result.current.defaultHeight).toEqual({ lineCount: 2 });
});
test('should apply rowHeight from local storage', () => {
const { result } = renderHook(
() => {
return useRowHeightsOptions({});
},
{
wrapper: getWrapper({
uiSettings: uiSettingsMock,
storage: new LocalStorageMock({
['discover:dataGridRowHeight']: {
previousRowHeight: 5,
previousConfigRowHeight: 3,
},
}) as unknown as Storage,
} as DiscoverServices),
}
);
expect(result.current.defaultHeight).toEqual({ lineCount: 5 });
});
test('should apply rowHeight from uiSettings', () => {
const { result } = renderHook(
() => {
return useRowHeightsOptions({});
},
{
wrapper: getWrapper({
uiSettings: uiSettingsMock,
storage: new LocalStorageMock({}) as unknown as Storage,
} as unknown as DiscoverServices),
}
);
expect(result.current.defaultHeight).toEqual({
lineCount: CONFIG_ROW_HEIGHT,
});
});
test('should apply rowHeight from uiSettings instead of local storage value, since uiSettings has been changed', () => {
const { result } = renderHook(
() => {
return useRowHeightsOptions({});
},
{
wrapper: getWrapper({
uiSettings: uiSettingsMock,
storage: new LocalStorageMock({
['discover:dataGridRowHeight']: {
previousRowHeight: 4,
// different from uiSettings (config), now user changed it to 3, but prev was 4
previousConfigRowHeight: 4,
},
}) as unknown as Storage,
} as unknown as DiscoverServices),
}
);
expect(result.current.defaultHeight).toEqual({
lineCount: CONFIG_ROW_HEIGHT,
});
});
});

View file

@ -0,0 +1,87 @@
/*
* 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 { EuiDataGridRowHeightOption, EuiDataGridRowHeightsOptions } from '@elastic/eui';
import { useMemo } from 'react';
import { ROW_HEIGHT_OPTION } from '../../common';
import { isValidRowHeight } from './validate_row_height';
import { useDiscoverServices } from './use_discover_services';
import { DataGridOptionsRecord, getStoredRowHeight, updateStoredRowHeight } from './row_heights';
interface UseRowHeightProps {
rowHeightState?: number;
onUpdateRowHeight?: (rowHeight: number) => void;
}
/**
* Row height might be a value from -1 to 20
* A value of -1 automatically adjusts the row height to fit the contents.
* A value of 0 displays the content in a single line.
* A value from 1 to 20 represents number of lines of Document explorer row to display.
*/
const SINGLE_ROW_HEIGHT_OPTION = 0;
const AUTO_ROW_HEIGHT_OPTION = -1;
/**
* Converts rowHeight of EuiDataGrid to rowHeight number (-1 to 20)
*/
const serializeRowHeight = (rowHeight?: EuiDataGridRowHeightOption): number => {
if (rowHeight === 'auto') {
return AUTO_ROW_HEIGHT_OPTION;
} else if (typeof rowHeight === 'object' && rowHeight.lineCount) {
return rowHeight.lineCount; // custom
}
return SINGLE_ROW_HEIGHT_OPTION;
};
/**
* Converts rowHeight number (-1 to 20) of EuiDataGrid rowHeight
*/
const deserializeRowHeight = (number: number): EuiDataGridRowHeightOption | undefined => {
if (number === AUTO_ROW_HEIGHT_OPTION) {
return 'auto';
} else if (number === SINGLE_ROW_HEIGHT_OPTION) {
return undefined;
}
return { lineCount: number }; // custom
};
export const useRowHeightsOptions = ({ rowHeightState, onUpdateRowHeight }: UseRowHeightProps) => {
const { storage, uiSettings } = useDiscoverServices();
return useMemo((): EuiDataGridRowHeightsOptions => {
const rowHeightFromLS = getStoredRowHeight(storage);
const configRowHeight = uiSettings.get(ROW_HEIGHT_OPTION);
const configHasNotChanged = (
localStorageRecord: DataGridOptionsRecord | null
): localStorageRecord is DataGridOptionsRecord =>
localStorageRecord !== null && configRowHeight === localStorageRecord.previousConfigRowHeight;
let rowHeight;
if (isValidRowHeight(rowHeightState)) {
rowHeight = rowHeightState;
} else if (configHasNotChanged(rowHeightFromLS)) {
rowHeight = rowHeightFromLS.previousRowHeight;
} else {
rowHeight = configRowHeight;
}
return {
defaultHeight: deserializeRowHeight(rowHeight),
lineHeight: '1.6em',
onChange: ({ defaultHeight: newRowHeight }: EuiDataGridRowHeightsOptions) => {
const newSerializedRowHeight = serializeRowHeight(newRowHeight);
updateStoredRowHeight(newSerializedRowHeight, configRowHeight, storage);
onUpdateRowHeight?.(newSerializedRowHeight);
},
};
}, [rowHeightState, uiSettings, storage, onUpdateRowHeight]);
};

View file

@ -0,0 +1,23 @@
/*
* 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.
*/
const MIN_ROW_HEIGHT = -1;
const MAX_ROW_HEIGHT = 20;
export const isValidRowHeight = (rowHeight?: number): rowHeight is number => {
return (
// is number
typeof rowHeight === 'number' &&
!Number.isNaN(rowHeight) &&
// is integer
Math.floor(rowHeight) === rowHeight &&
// does it fit the range
rowHeight >= MIN_ROW_HEIGHT &&
rowHeight <= MAX_ROW_HEIGHT
);
};

View file

@ -45,6 +45,7 @@ export const searchSavedObjectType: SavedObjectsType = {
title: { type: 'text' },
grid: { type: 'object', enabled: false },
version: { type: 'integer' },
rowHeight: { type: 'text' },
},
},
migrations: searchMigrations,

View file

@ -28,6 +28,7 @@ import {
SHOW_MULTIFIELDS,
TRUNCATE_MAX_HEIGHT,
SHOW_FIELD_STATISTICS,
ROW_HEIGHT_OPTION,
} from '../common';
export const getUiSettings: () => Record<string, UiSettingsParams> = () => ({
@ -251,15 +252,27 @@ export const getUiSettings: () => Record<string, UiSettingsParams> = () => ({
category: ['discover'],
schema: schema.boolean(),
},
[ROW_HEIGHT_OPTION]: {
name: i18n.translate('discover.advancedSettings.params.rowHeightTitle', {
defaultMessage: 'Row height in the Document Explorer',
}),
value: 3,
category: ['discover'],
description: i18n.translate('discover.advancedSettings.params.rowHeightText', {
defaultMessage:
'The number of lines to allow in a row. A value of -1 automatically adjusts the row height to fit the contents. A value of 0 displays the content in a single line.',
}),
schema: schema.number({ min: -1 }),
},
[TRUNCATE_MAX_HEIGHT]: {
name: i18n.translate('discover.advancedSettings.params.maxCellHeightTitle', {
defaultMessage: 'Maximum table cell height',
defaultMessage: 'Maximum cell height in the classic table',
}),
value: 115,
category: ['discover'],
description: i18n.translate('discover.advancedSettings.params.maxCellHeightText', {
defaultMessage:
'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation',
'The maximum height that a cell in a table should occupy. Set to 0 to disable truncation.',
}),
schema: schema.number({ min: 0 }),
},

View file

@ -36,6 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.clickNewDashboard();
await PageObjects.timePicker.setDefaultDataRange();
await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search');
await PageObjects.header.waitUntilLoadingHasFinished();
});
it('should expand the detail row when the toggle arrow is clicked', async function () {