mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[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:
parent
a6c7d968e5
commit
a7e0171dfa
30 changed files with 393 additions and 54 deletions
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 && (
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface SearchInput extends EmbeddableInput {
|
|||
hidePanelTitles?: boolean;
|
||||
columns?: string[];
|
||||
sort?: SortOrder[];
|
||||
rowHeight?: number;
|
||||
}
|
||||
|
||||
export interface SearchOutput extends EmbeddableOutput {
|
||||
|
|
|
@ -104,6 +104,7 @@ describe('getSavedSearch', () => {
|
|||
"hideAggregatedPreview": undefined,
|
||||
"hideChart": false,
|
||||
"id": "ccf1af80-2297-11ec-86e0-1155ffb9c7a7",
|
||||
"rowHeight": undefined,
|
||||
"searchSource": Object {
|
||||
"create": [MockFunction],
|
||||
"createChild": [MockFunction],
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
41
src/plugins/discover/public/utils/row_heights.ts
Normal file
41
src/plugins/discover/public/utils/row_heights.ts
Normal 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,
|
||||
});
|
||||
};
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
});
|
87
src/plugins/discover/public/utils/use_row_heights_options.ts
Normal file
87
src/plugins/discover/public/utils/use_row_heights_options.ts
Normal 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]);
|
||||
};
|
23
src/plugins/discover/public/utils/validate_row_height.ts
Normal file
23
src/plugins/discover/public/utils/validate_row_height.ts
Normal 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
|
||||
);
|
||||
};
|
|
@ -45,6 +45,7 @@ export const searchSavedObjectType: SavedObjectsType = {
|
|||
title: { type: 'text' },
|
||||
grid: { type: 'object', enabled: false },
|
||||
version: { type: 'integer' },
|
||||
rowHeight: { type: 'text' },
|
||||
},
|
||||
},
|
||||
migrations: searchMigrations,
|
||||
|
|
|
@ -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 }),
|
||||
},
|
||||
|
|
|
@ -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 () {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue