[EuiInMemoryTable] Persist table rows per page and sort (#198297)

This commit is contained in:
Sébastien Loix 2024-11-18 13:35:26 +00:00 committed by GitHub
parent ab0375f56f
commit 020acbeaa3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 798 additions and 186 deletions

View file

@ -23,6 +23,7 @@ export const FinderApp = (props: {
<ContentClientProvider contentClient={props.contentClient}>
<I18nProvider>
<SavedObjectFinder
id="cmFinderApp"
showFilter={true}
services={{
savedObjectsTagging: props.savedObjectsTagging.getTaggingApi(),

View file

@ -9,6 +9,7 @@
import React from 'react';
import { from } from 'rxjs';
import type { IStorage } from '@kbn/kibana-utils-plugin/public';
import type { Services, TagListProps } from './services';
@ -149,3 +150,22 @@ export const getStoryArgTypes = () => ({
defaultValue: false,
},
});
export const localStorageMock = (): IStorage => {
let store: Record<string, unknown> = {};
return {
getItem: (key: string) => {
return store[key] || null;
},
setItem: (key: string, value: unknown) => {
store[key] = value;
},
clear() {
store = {};
},
removeItem(key: string) {
delete store[key];
},
};
};

View file

@ -18,7 +18,7 @@ import type { LocationDescriptor, History } from 'history';
import type { UserContentCommonSchema } from '@kbn/content-management-table-list-view-common';
import { WithServices } from './__jest__';
import { getTagList } from './mocks';
import { getTagList, localStorageMock } from './mocks';
import { TableListViewTable, type TableListViewTableProps } from './table_list_view_table';
import { getActions } from './table_list_view.test.helpers';
import type { Services } from './services';
@ -335,6 +335,12 @@ describe('TableListView', () => {
const totalItems = 30;
const updatedAt = new Date().toISOString();
beforeEach(() => {
Object.defineProperty(window, 'localStorage', {
value: localStorageMock(),
});
});
const hits: UserContentCommonSchema[] = [...Array(totalItems)].map((_, i) => ({
id: `item${i}`,
type: 'dashboard',
@ -429,6 +435,54 @@ describe('TableListView', () => {
expect(firstRowTitle).toBe('Item 20');
expect(lastRowTitle).toBe('Item 29');
});
test('should persist the number of rows in the table', async () => {
let testBed: TestBed;
const tableId = 'myTable';
await act(async () => {
testBed = await setup({
initialPageSize,
findItems: jest.fn().mockResolvedValue({ total: hits.length, hits: [...hits] }),
id: tableId,
});
});
{
const { component, table, find } = testBed!;
component.update();
const { tableCellsValues } = table.getMetaData('itemsInMemTable');
expect(tableCellsValues.length).toBe(20); // 20 by default
let storageValue = localStorage.getItem(`tablePersist:${tableId}`);
expect(storageValue).toBe(null);
find('tablePaginationPopoverButton').simulate('click');
find('tablePagination-10-rows').simulate('click');
storageValue = localStorage.getItem(`tablePersist:${tableId}`);
expect(storageValue).not.toBe(null);
expect(JSON.parse(storageValue!).pageSize).toBe(10);
}
// Mount a second table and verify that is shows only 10 rows
{
await act(async () => {
testBed = await setup({
initialPageSize,
findItems: jest.fn().mockResolvedValue({ total: hits.length, hits: [...hits] }),
id: tableId,
});
});
const { component, table } = testBed!;
component.update();
const { tableCellsValues } = table.getMetaData('itemsInMemTable');
expect(tableCellsValues.length).toBe(10); // 10 items this time
}
});
});
describe('column sorting', () => {

View file

@ -43,6 +43,7 @@ import {
ContentInsightsProvider,
useContentInsightsServices,
} from '@kbn/content-management-content-insights-public';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
import {
Table,
@ -443,7 +444,7 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
hasUpdatedAtMetadata,
hasCreatedByMetadata,
hasRecentlyAccessedMetadata,
pagination,
pagination: _pagination,
tableSort,
tableFilter,
} = state;
@ -903,7 +904,7 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
[updateTableSortFilterAndPagination]
);
const onTableChange = useCallback(
const customOnTableChange = useCallback(
(criteria: CriteriaWithPagination<T>) => {
const data: {
sort?: State<T>['tableSort'];
@ -1038,6 +1039,20 @@ function TableListViewTableComp<T extends UserContentCommonSchema>({
);
}, [entityName, fetchError]);
const { pageSize, onTableChange } = useEuiTablePersist({
tableId: listingId,
initialPageSize,
customOnTableChange,
pageSizeOptions: uniq([10, 20, 50, initialPageSize]).sort(),
});
const pagination = useMemo<Pagination>(() => {
return {
..._pagination,
pageSize,
};
}, [_pagination, pageSize]);
// ------------
// Effects
// ------------

View file

@ -37,7 +37,9 @@
"@kbn/content-management-user-profiles",
"@kbn/recently-accessed",
"@kbn/content-management-content-insights-public",
"@kbn/content-management-favorites-public"
"@kbn/content-management-favorites-public",
"@kbn/kibana-utils-plugin",
"@kbn/shared-ux-table-persist"
],
"exclude": [
"target/**/*"

View file

@ -13,12 +13,13 @@ import {
EuiTabbedContent,
EuiTabbedContentProps,
useEuiOverflowScroll,
EuiBasicTableColumn,
} from '@elastic/eui';
import { css } from '@emotion/react';
import React, { memo, useCallback, useMemo, useState } from 'react';
import React, { memo, useMemo } from 'react';
import { Alert } from '@kbn/alerting-types';
import { euiThemeVars } from '@kbn/ui-theme';
import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
export const search = {
box: {
@ -66,28 +67,6 @@ export const ScrollableFlyoutTabbedContent = (props: EuiTabbedContentProps) => (
const COUNT_PER_PAGE_OPTIONS = [25, 50, 100];
const useFieldBrowserPagination = () => {
const [pagination, setPagination] = useState<{ pageIndex: number }>({
pageIndex: 0,
});
const onTableChange = useCallback(({ page: { index } }: { page: { index: number } }) => {
setPagination({ pageIndex: index });
}, []);
const paginationTableProp = useMemo(
() => ({
...pagination,
pageSizeOptions: COUNT_PER_PAGE_OPTIONS,
}),
[pagination]
);
return {
onTableChange,
paginationTableProp,
};
};
type AlertField = Exclude<
{
[K in keyof Alert]: { key: K; value: Alert[K] };
@ -111,7 +90,11 @@ export interface AlertFieldsTableProps {
* A paginated, filterable table to show alert object fields
*/
export const AlertFieldsTable = memo(({ alert, fields }: AlertFieldsTableProps) => {
const { onTableChange, paginationTableProp } = useFieldBrowserPagination();
const { pageSize, sorting, onTableChange } = useEuiTablePersist<AlertField>({
tableId: 'obltAlertFields',
initialPageSize: 25,
});
const items = useMemo(() => {
let _items = Object.entries(alert).map(
([key, value]) =>
@ -131,7 +114,11 @@ export const AlertFieldsTable = memo(({ alert, fields }: AlertFieldsTableProps)
itemId="key"
columns={columns}
onTableChange={onTableChange}
pagination={paginationTableProp}
pagination={{
pageSize,
pageSizeOptions: COUNT_PER_PAGE_OPTIONS,
}}
sorting={sorting}
search={search}
css={css`
& .euiTableRow {

View file

@ -49,6 +49,7 @@
"@kbn/core-ui-settings-browser",
"@kbn/core-http-browser-mocks",
"@kbn/core-notifications-browser-mocks",
"@kbn/kibana-react-plugin"
"@kbn/kibana-react-plugin",
"@kbn/shared-ux-table-persist"
]
}

View file

@ -17,7 +17,6 @@ import {
EuiInMemoryTable,
EuiBasicTableColumn,
EuiButtonEmpty,
Criteria,
EuiButtonIcon,
CustomItemAction,
EuiCopy,
@ -25,6 +24,7 @@ import {
euiScrollBarStyles,
} from '@elastic/eui';
import { css, Interpolation, Theme } from '@emotion/react';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
import { type QueryHistoryItem, getHistoryItems } from '../history_local_storage';
import { getReducedSpaceStyling, swapArrayElements } from './query_history_helpers';
@ -212,8 +212,16 @@ export function QueryHistory({
}) {
const theme = useEuiTheme();
const scrollBarStyles = euiScrollBarStyles(theme);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const historyItems: QueryHistoryItem[] = getHistoryItems(sortDirection);
const { sorting, onTableChange } = useEuiTablePersist<QueryHistoryItem>({
tableId: 'esqlQueryHistory',
initialSort: {
field: 'timeRan',
direction: 'desc',
},
});
const historyItems: QueryHistoryItem[] = getHistoryItems(sorting.sort.direction);
const actions: Array<CustomItemAction<QueryHistoryItem>> = useMemo(() => {
return [
@ -276,19 +284,6 @@ export function QueryHistory({
return getTableColumns(containerWidth, isOnReducedSpaceLayout, actions);
}, [actions, containerWidth, isOnReducedSpaceLayout]);
const onTableChange = ({ page, sort }: Criteria<QueryHistoryItem>) => {
if (sort) {
const { direction } = sort;
setSortDirection(direction);
}
};
const sorting = {
sort: {
field: 'timeRan',
direction: sortDirection,
},
};
const { euiTheme } = theme;
const extraStyling = isOnReducedSpaceLayout ? getReducedSpaceStyling() : '';

View file

@ -28,6 +28,7 @@
"@kbn/fields-metadata-plugin",
"@kbn/esql-validation-autocomplete",
"@kbn/esql-utils",
"@kbn/shared-ux-table-persist",
],
"exclude": [
"target/**/*",

View file

@ -100,6 +100,7 @@ export const EventAnnotationGroupSavedObjectFinder = ({
) : (
<SavedObjectFinder
key="searchSavedObjectFinder"
id="eventAnnotationGroup"
fixedPageSize={fixedPageSize}
onChoose={(id, type, fullName, savedObject) => {
onChoose({ id, type, fullName, savedObject });

View file

@ -7,4 +7,5 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export { useEuiTablePersist, DEFAULT_PAGE_SIZE_OPTIONS } from './src';
export { useEuiTablePersist, DEFAULT_PAGE_SIZE_OPTIONS, withEuiTablePersist } from './src';
export type { EuiTablePersistInjectedProps, EuiTablePersistPropsGetter, HOCProps } from './src';

View file

@ -9,3 +9,9 @@
export { useEuiTablePersist } from './use_table_persist';
export { DEFAULT_PAGE_SIZE_OPTIONS } from './constants';
export { withEuiTablePersist } from './table_persist_hoc';
export type {
EuiTablePersistInjectedProps,
EuiTablePersistPropsGetter,
HOCProps,
} from './table_persist_hoc';

View file

@ -0,0 +1,113 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React, { PureComponent } from 'react';
import { render, screen } from '@testing-library/react';
import { withEuiTablePersist, type EuiTablePersistInjectedProps } from './table_persist_hoc';
const mockUseEuiTablePersist = jest.fn().mockReturnValue({
pageSize: 'mockPageSize',
sorting: 'mockSorting',
onTableChange: 'mockOnTableChange',
});
jest.mock('./use_table_persist', () => {
const original = jest.requireActual('./use_table_persist');
return {
...original,
useEuiTablePersist: (...args: unknown[]) => mockUseEuiTablePersist(...args),
};
});
class TestComponent extends PureComponent<EuiTablePersistInjectedProps<any>> {
constructor(props: EuiTablePersistInjectedProps<any>) {
super(props);
}
render() {
return <div data-test-subj="value">{JSON.stringify(this.props.euiTablePersist)}</div>;
}
}
describe('withEuiTablePersist', () => {
it('should call useEuiTablePersist and return its values', () => {
const customOnTableChange = jest.fn();
const pageSizeOptions = [5, 10, 25, 50];
const WrappedComponent = withEuiTablePersist(TestComponent, {
tableId: 'testTableId',
initialPageSize: 10,
initialSort: { field: 'testField', direction: 'asc' },
customOnTableChange,
pageSizeOptions,
});
render(<WrappedComponent />);
expect(mockUseEuiTablePersist).toHaveBeenCalledWith({
tableId: 'testTableId',
customOnTableChange,
initialPageSize: 10,
initialSort: { field: 'testField', direction: 'asc' },
pageSizeOptions,
});
expect(screen.getByTestId('value').textContent).toBe(
JSON.stringify({
pageSize: 'mockPageSize',
sorting: 'mockSorting',
onTableChange: 'mockOnTableChange',
})
);
});
it('should allow override through props', () => {
const customOnTableChangeDefault = jest.fn();
const customOnTableChangeProp = jest.fn();
const pageSizeOptions = [5, 10, 25, 50];
const WrappedComponent = withEuiTablePersist(TestComponent, {
tableId: 'testTableId',
initialPageSize: 10,
initialSort: { field: 'testField', direction: 'asc' },
customOnTableChange: customOnTableChangeDefault,
pageSizeOptions,
});
render(
<WrappedComponent
euiTablePersistProps={{
tableId: 'testTableIdChanged',
initialPageSize: 20,
initialSort: { field: 'testFieldChanged', direction: 'desc' },
customOnTableChange: customOnTableChangeProp,
pageSizeOptions: [5],
}}
/>
);
expect(mockUseEuiTablePersist).toHaveBeenCalledWith({
tableId: 'testTableIdChanged',
customOnTableChange: customOnTableChangeProp,
initialPageSize: 20,
initialSort: { field: 'testFieldChanged', direction: 'desc' },
pageSizeOptions: [5],
});
expect(screen.getByTestId('value').textContent).toBe(
JSON.stringify({
pageSize: 'mockPageSize',
sorting: 'mockSorting',
onTableChange: 'mockOnTableChange',
})
);
});
});

View file

@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import { type CriteriaWithPagination } from '@elastic/eui';
import { EuiTablePersistProps, useEuiTablePersist } from './use_table_persist';
import { PropertySort } from './types';
export interface EuiTablePersistInjectedProps<T> {
euiTablePersist: {
/** The EuiInMemoryTable onTableChange prop */
onTableChange: (change: CriteriaWithPagination<T>) => void;
/** The EuiInMemoryTable sorting prop */
sorting: { sort: PropertySort<T> } | true;
/** The EuiInMemoryTable pagination.pageSize value */
pageSize: number;
};
}
export type EuiTablePersistPropsGetter<T extends object, P extends object> = (
props: Omit<P, keyof EuiTablePersistInjectedProps<T>>
) => EuiTablePersistProps<T>;
export type HOCProps<T extends object, P extends object> = P & {
/** Custom value for the EuiTablePersist HOC */
euiTablePersistProps?: Partial<EuiTablePersistProps<T>>;
};
export function withEuiTablePersist<T extends object, Props extends object>(
WrappedComponent: React.ComponentClass<Props & EuiTablePersistInjectedProps<T>>,
euiTablePersistDefault:
| (EuiTablePersistProps<T> & { get?: undefined })
| {
get: EuiTablePersistPropsGetter<T, Props>;
}
) {
const HOC: React.FC<HOCProps<T, Omit<Props, keyof EuiTablePersistInjectedProps<T>>>> = (
props
) => {
const getterOverride = euiTablePersistDefault.get ? euiTablePersistDefault.get(props) : {};
const mergedProps = {
...euiTablePersistDefault,
...props.euiTablePersistProps,
...getterOverride, // Getter override other props
};
const { tableId, customOnTableChange, initialSort, initialPageSize, pageSizeOptions } =
mergedProps;
if (!tableId) {
throw new Error('tableId is required');
}
const euiTablePersist = useEuiTablePersist<T>({
tableId,
customOnTableChange,
initialSort,
initialPageSize,
pageSizeOptions,
});
const { euiTablePersistProps, ...rest } = props;
return <WrappedComponent {...(rest as Props)} euiTablePersist={euiTablePersist} />;
};
return HOC;
}

View file

@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { Criteria } from '@elastic/eui';
import { CriteriaWithPagination } from '@elastic/eui';
import { renderHook, act } from '@testing-library/react-hooks';
import { useEuiTablePersist } from './use_table_persist';
import { createStorage } from './storage'; // Mock this if it's external
@ -58,7 +58,7 @@ describe('useEuiTablePersist', () => {
};
act(() => {
result.current.onTableChange(nextCriteria as Criteria<any>);
result.current.onTableChange(nextCriteria as CriteriaWithPagination<any>);
});
expect(result.current.pageSize).toBe(100);
@ -85,7 +85,7 @@ describe('useEuiTablePersist', () => {
};
act(() => {
result.current.onTableChange(nextCriteria as Criteria<any>);
result.current.onTableChange(nextCriteria as CriteriaWithPagination<any>);
});
expect(customOnTableChange).toHaveBeenCalledWith(nextCriteria);
@ -98,7 +98,7 @@ describe('useEuiTablePersist', () => {
const { result } = renderHook(() => useEuiTablePersist({ tableId: 'testTable' }));
act(() => {
result.current.onTableChange({}); // Empty change
result.current.onTableChange({} as CriteriaWithPagination<any>); // Empty change
});
expect(result.current.pageSize).toBe(25);
@ -118,7 +118,7 @@ describe('useEuiTablePersist', () => {
};
act(() => {
result.current.onTableChange(nextCriteria as Criteria<any>);
result.current.onTableChange(nextCriteria as CriteriaWithPagination<any>);
});
expect(result.current.pageSize).toBe(100);

View file

@ -8,7 +8,7 @@
*/
import { useState, useCallback } from 'react';
import { Criteria } from '@elastic/eui';
import type { CriteriaWithPagination } from '@elastic/eui';
import { DEFAULT_INITIAL_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS } from './constants';
import { createStorage } from './storage';
import { validatePersistData } from './validate_persist_data';
@ -18,7 +18,7 @@ export interface EuiTablePersistProps<T> {
/** A unique id that will be included in the local storage variable for this table. */
tableId: string;
/** (Optional) Specifies a custom onTableChange handler. */
customOnTableChange?: (change: Criteria<T>) => void;
customOnTableChange?: (change: CriteriaWithPagination<T>) => void;
/** (Optional) Specifies a custom initial table sorting. */
initialSort?: PropertySort<T>;
/** (Optional) Specifies a custom initial page size for the table. Defaults to 50. */
@ -33,13 +33,37 @@ export interface EuiTablePersistProps<T> {
* Returns the persisting page size and sort and the onTableChange handler that should be passed
* as props to an Eui table component.
*/
export const useEuiTablePersist = <T extends object>({
export function useEuiTablePersist<T extends object>(
props: EuiTablePersistProps<T> & { initialSort: PropertySort<T> }
): {
sorting: { sort: PropertySort<T> };
pageSize: number;
onTableChange: (nextValues: CriteriaWithPagination<T>) => void;
};
export function useEuiTablePersist<T extends object>(
props: EuiTablePersistProps<T> & { initialSort?: undefined }
): {
sorting: true;
pageSize: number;
onTableChange: (nextValues: CriteriaWithPagination<T>) => void;
};
export function useEuiTablePersist<T extends object>(
props: EuiTablePersistProps<T>
): {
sorting: true | { sort: PropertySort<T> };
pageSize: number;
onTableChange: (nextValues: CriteriaWithPagination<T>) => void;
};
export function useEuiTablePersist<T extends object>({
tableId,
customOnTableChange,
initialSort,
initialPageSize,
pageSizeOptions,
}: EuiTablePersistProps<T>) => {
}: EuiTablePersistProps<T>) {
const storage = createStorage();
const storedPersistData = storage.get(tableId, undefined);
@ -55,7 +79,7 @@ export const useEuiTablePersist = <T extends object>({
const sorting = sort ? { sort } : true; // If sort is undefined, return true to allow sorting
const onTableChange = useCallback(
(nextValues: Criteria<T>) => {
(nextValues: CriteriaWithPagination<T>) => {
if (customOnTableChange) {
customOnTableChange(nextValues);
}
@ -92,4 +116,4 @@ export const useEuiTablePersist = <T extends object>({
);
return { pageSize, sorting, onTableChange };
};
}

View file

@ -13,6 +13,7 @@ import { CoreStart } from '@kbn/core/public';
import moment from 'moment';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useDebounce from 'react-use/lib/useDebounce';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
import { TableText } from '..';
import { SEARCH_SESSIONS_TABLE_ID } from '../../../../../../common';
import { SearchSessionsMgmtAPI } from '../../lib/api';
@ -45,7 +46,6 @@ export function SearchSessionsMgmtTable({
const [tableData, setTableData] = useState<UISession[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [debouncedIsLoading, setDebouncedIsLoading] = useState(false);
const [pagination, setPagination] = useState({ pageIndex: 0 });
const showLatestResultsHandler = useRef<Function>();
const refreshTimeoutRef = useRef<number | null>(null);
const refreshInterval = useMemo(
@ -53,6 +53,14 @@ export function SearchSessionsMgmtTable({
[config.management.refreshInterval]
);
const { pageSize, sorting, onTableChange } = useEuiTablePersist<UISession>({
tableId: 'searchSessionsMgmt',
initialSort: {
field: 'created',
direction: 'desc',
},
});
// Debounce rendering the state of the Refresh button
useDebounce(
() => {
@ -148,12 +156,12 @@ export function SearchSessionsMgmtTable({
searchUsageCollector
)}
items={tableData}
pagination={pagination}
search={search}
sorting={{ sort: { field: 'created', direction: 'desc' } }}
onTableChange={({ page: { index } }) => {
setPagination({ pageIndex: index });
pagination={{
pageSize,
}}
search={search}
sorting={sorting}
onTableChange={onTableChange}
tableLayout="auto"
/>
);

View file

@ -22,11 +22,17 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { IUiSettingsClient } from '@kbn/core/public';
import { Datatable, DatatableColumn } from '@kbn/expressions-plugin/public';
import { Datatable, DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist/src';
import { DataViewRow, DataViewColumn } from '../types';
const PAGE_SIZE_OPTIONS = [10, 20, 50];
interface DataTableFormatState {
columns: DataViewColumn[];
rows: DataViewRow[];
@ -49,7 +55,10 @@ interface RenderCellArguments {
isFilterable: boolean;
}
export class DataTableFormat extends Component<DataTableFormatProps, DataTableFormatState> {
class DataTableFormatClass extends Component<
DataTableFormatProps & EuiTablePersistInjectedProps<DatatableRow>,
DataTableFormatState
> {
static propTypes = {
data: PropTypes.object.isRequired,
uiSettings: PropTypes.object.isRequired,
@ -169,7 +178,7 @@ export class DataTableFormat extends Component<DataTableFormatProps, DataTableFo
const formattedValue = fieldFormatter.convert(value);
const rowIndex = data.rows.findIndex((row) => row[dataColumn.id] === value) || 0;
return DataTableFormat.renderCell({
return DataTableFormatClass.renderCell({
table: data,
columnIndex: index,
rowIndex,
@ -186,9 +195,10 @@ export class DataTableFormat extends Component<DataTableFormatProps, DataTableFo
render() {
const { columns, rows } = this.state;
const { pageSize, sorting, onTableChange } = this.props.euiTablePersist;
const pagination = {
pageSizeOptions: [10, 20, 50],
initialPageSize: 20,
pageSizeOptions: PAGE_SIZE_OPTIONS,
pageSize,
};
return (
@ -198,8 +208,9 @@ export class DataTableFormat extends Component<DataTableFormatProps, DataTableFo
data-test-subj="inspectorTable"
columns={columns}
items={rows}
sorting={true}
sorting={sorting}
pagination={pagination}
onChange={onTableChange}
css={css`
// Set a min width on each column - you can use [data-test-subj] to target specific columns
.euiTableHeaderCell {
@ -216,3 +227,9 @@ export class DataTableFormat extends Component<DataTableFormatProps, DataTableFo
);
}
}
export const DataTableFormat = withEuiTablePersist(DataTableFormatClass, {
tableId: 'inspectorDataTable',
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialPageSize: 20,
});

View file

@ -54,7 +54,8 @@
"@kbn/react-kibana-mount",
"@kbn/search-types",
"@kbn/safer-lodash-set",
"@kbn/esql-utils"
"@kbn/esql-utils",
"@kbn/shared-ux-table-persist"
],
"exclude": [
"target/**/*",

View file

@ -290,9 +290,10 @@ exports[`Table should render normally 1`] = `
},
]
}
onTableChange={[Function]}
pagination={
Object {
"initialPageSize": 10,
"pageSize": 10,
"pageSizeOptions": Array [
5,
10,
@ -306,7 +307,7 @@ exports[`Table should render normally 1`] = `
Object {
"sort": Object {
"direction": "asc",
"field": "displayName",
"field": "name",
},
}
}

View file

@ -11,7 +11,12 @@ import React from 'react';
import { shallow } from 'enzyme';
import { DataView } from '@kbn/data-views-plugin/public';
import { IndexedFieldItem } from '../../types';
import { Table, renderFieldName, getConflictModalContent, showDelete } from './table';
import {
TableWithoutPersist as Table,
renderFieldName,
getConflictModalContent,
showDelete,
} from './table';
import { coreMock, overlayServiceMock } from '@kbn/core/public/mocks';
const coreStart = coreMock.createStart();
@ -80,6 +85,14 @@ const items: IndexedFieldItem[] = [
},
];
const baseProps = {
euiTablePersist: {
pageSize: 10,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'name' as const } },
},
};
const renderTable = (
{ editField } = {
editField: () => {},
@ -87,6 +100,7 @@ const renderTable = (
) =>
shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={editField}

View file

@ -31,6 +31,10 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { toMountPoint } from '@kbn/react-kibana-mount';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist';
import { DataView } from '@kbn/data-views-plugin/public';
import { StartServices } from '../../../../../types';
@ -379,7 +383,11 @@ const getConflictBtn = (
);
};
export class Table extends PureComponent<IndexedFieldProps> {
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
class TableClass extends PureComponent<
IndexedFieldProps & EuiTablePersistInjectedProps<IndexedFieldItem>
> {
renderBooleanTemplate(value: string, arialLabel: string) {
return value ? <EuiIcon type="dot" color="success" aria-label={arialLabel} /> : <span />;
}
@ -403,11 +411,17 @@ export class Table extends PureComponent<IndexedFieldProps> {
}
render() {
const { items, editField, deleteField, indexPattern } = this.props;
const {
items,
editField,
deleteField,
indexPattern,
euiTablePersist: { pageSize, sorting, onTableChange },
} = this.props;
const pagination = {
initialPageSize: 10,
pageSizeOptions: [5, 10, 25, 50],
pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
};
const columns: Array<EuiBasicTableColumn<IndexedFieldItem>> = [
@ -508,8 +522,18 @@ export class Table extends PureComponent<IndexedFieldProps> {
items={items}
columns={columns}
pagination={pagination}
sorting={{ sort: { field: 'displayName', direction: 'asc' } }}
sorting={sorting}
onTableChange={onTableChange}
/>
);
}
}
export const TableWithoutPersist = TableClass; // For testing purposes
export const Table = withEuiTablePersist(TableClass, {
tableId: 'dataViewsIndexedFields',
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialSort: { field: 'displayName', direction: 'asc' },
initialPageSize: 10,
});

View file

@ -18,6 +18,7 @@ import {
import { CoreStart } from '@kbn/core/public';
import { get } from 'lodash';
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
import {
SavedObjectRelation,
@ -139,12 +140,20 @@ export const RelationshipsTable = ({
] as SearchFilterConfig[],
};
const { pageSize, onTableChange } = useEuiTablePersist<SavedObjectRelation>({
tableId: 'dataViewMgmtRelationships',
initialPageSize: 10,
});
return (
<RedirectAppLinks currentAppId={IPM_APP_ID} navigateToUrl={navigateToUrl}>
<EuiInMemoryTable<SavedObjectRelation>
items={relationships}
columns={columns}
pagination={true}
pagination={{
pageSize,
}}
onTableChange={onTableChange}
search={search}
rowProps={() => ({
'data-test-subj': `relationshipsTableRow`,

View file

@ -75,9 +75,10 @@ exports[`Table should render normally 1`] = `
},
]
}
onTableChange={[Function]}
pagination={
Object {
"initialPageSize": 10,
"pageSize": 10,
"pageSizeOptions": Array [
5,
10,
@ -87,7 +88,14 @@ exports[`Table should render normally 1`] = `
}
}
searchFormat="eql"
sorting={true}
sorting={
Object {
"sort": Object {
"direction": "asc",
"field": "name",
},
}
}
tableLayout="fixed"
/>
`;

View file

@ -10,7 +10,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Table } from '.';
import { TableWithoutPersist as Table } from './table';
import { ScriptedFieldItem } from '../../types';
import { DataView } from '@kbn/data-views-plugin/public';
@ -21,6 +21,14 @@ const items: ScriptedFieldItem[] = [
{ name: '2', lang: 'painless', script: '', isUserEditable: false },
];
const baseProps = {
euiTablePersist: {
pageSize: 10,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'name' as const } },
},
};
describe('Table', () => {
let indexPattern: DataView;
@ -37,8 +45,9 @@ describe('Table', () => {
});
test('should render normally', () => {
const component = shallow<Table>(
const component = shallow<typeof Table>(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={() => {}}
@ -52,6 +61,7 @@ describe('Table', () => {
test('should render the format', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={() => {}}
@ -68,6 +78,7 @@ describe('Table', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={editField}
@ -85,6 +96,7 @@ describe('Table', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={() => {}}
@ -100,6 +112,7 @@ describe('Table', () => {
test('should not allow edit or deletion for user with only read access', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
editField={() => {}}

View file

@ -13,8 +13,14 @@ import { i18n } from '@kbn/i18n';
import { EuiInMemoryTable, EuiBasicTableColumn } from '@elastic/eui';
import { DataView } from '@kbn/data-views-plugin/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist';
import { ScriptedFieldItem } from '../../types';
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
interface TableProps {
indexPattern: DataView;
items: ScriptedFieldItem[];
@ -22,7 +28,9 @@ interface TableProps {
deleteField: (field: ScriptedFieldItem) => void;
}
export class Table extends PureComponent<TableProps> {
class TableClass extends PureComponent<
TableProps & EuiTablePersistInjectedProps<ScriptedFieldItem>
> {
renderFormatCell = (value: string) => {
const { indexPattern } = this.props;
const title = get(indexPattern, ['fieldFormatMap', value, 'type', 'title'], '');
@ -31,7 +39,12 @@ export class Table extends PureComponent<TableProps> {
};
render() {
const { items, editField, deleteField } = this.props;
const {
items,
editField,
deleteField,
euiTablePersist: { pageSize, sorting, onTableChange },
} = this.props;
const columns: Array<EuiBasicTableColumn<ScriptedFieldItem>> = [
{
@ -132,12 +145,26 @@ export class Table extends PureComponent<TableProps> {
];
const pagination = {
initialPageSize: 10,
pageSizeOptions: [5, 10, 25, 50],
pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
};
return (
<EuiInMemoryTable items={items} columns={columns} pagination={pagination} sorting={true} />
<EuiInMemoryTable
items={items}
columns={columns}
pagination={pagination}
sorting={sorting}
onTableChange={onTableChange}
/>
);
}
}
export const TableWithoutPersist = TableClass; // For testing purposes
export const Table = withEuiTablePersist(TableClass, {
tableId: 'dataViewsScriptedFields',
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialPageSize: 10,
});

View file

@ -79,9 +79,10 @@ exports[`Table should render normally 1`] = `
]
}
loading={true}
onTableChange={[Function]}
pagination={
Object {
"initialPageSize": 10,
"pageSize": 10,
"pageSizeOptions": Array [
5,
10,
@ -91,7 +92,14 @@ exports[`Table should render normally 1`] = `
}
}
searchFormat="eql"
sorting={true}
sorting={
Object {
"sort": Object {
"direction": "asc",
"field": "clientId",
},
}
}
tableLayout="fixed"
/>
`;

View file

@ -10,7 +10,7 @@
import React, { ReactElement } from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { Table, TableProps, TableState } from './table';
import { TableWithoutPersist as Table } from './table';
import { EuiTableFieldDataColumnType, keys } from '@elastic/eui';
import { DataView } from '@kbn/data-views-plugin/public';
import { SourceFiltersTableFilter } from '../../types';
@ -20,10 +20,15 @@ const items: SourceFiltersTableFilter[] = [{ value: 'tim*', clientId: '' }];
const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as DataView);
const getTableColumnRender = (
component: ShallowWrapper<TableProps, TableState, Table>,
index: number = 0
) => {
const baseProps = {
euiTablePersist: {
pageSize: 10,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'clientId' as const } },
},
};
const getTableColumnRender = (component: ShallowWrapper<typeof Table>, index: number = 0) => {
const columns =
component.prop<Array<EuiTableFieldDataColumnType<SourceFiltersTableFilter>>>('columns');
return {
@ -35,6 +40,7 @@ describe('Table', () => {
test('should render normally', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
deleteFilter={() => {}}
@ -48,8 +54,9 @@ describe('Table', () => {
});
test('should render filter matches', () => {
const component = shallow<Table>(
const component = shallow<typeof Table>(
<Table
{...baseProps}
indexPattern={getIndexPatternMock({
getNonScriptedFields: () => [{ name: 'time' }, { name: 'value' }],
})}
@ -70,11 +77,12 @@ describe('Table', () => {
describe('editing', () => {
const saveFilter = jest.fn();
const clientId = '1';
let component: ShallowWrapper<TableProps, TableState, Table>;
let component: ShallowWrapper<typeof Table>;
beforeEach(() => {
component = shallow<Table>(
component = shallow<typeof Table>(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
deleteFilter={() => {}}
@ -125,6 +133,7 @@ describe('Table', () => {
test('should update the matches dynamically as input value is changed', () => {
const localComponent = shallow(
<Table
{...baseProps}
indexPattern={getIndexPatternMock({
getNonScriptedFields: () => [{ name: 'time' }, { name: 'value' }],
})}
@ -191,6 +200,7 @@ describe('Table', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
deleteFilter={deleteFilter}
@ -214,6 +224,7 @@ describe('Table', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
deleteFilter={() => {}}
@ -251,6 +262,7 @@ describe('Table', () => {
const component = shallow(
<Table
{...baseProps}
indexPattern={indexPattern}
items={items}
deleteFilter={() => {}}

View file

@ -21,6 +21,11 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { DataView } from '@kbn/data-views-plugin/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist';
import { SourceFiltersTableFilter } from '../../types';
const filterHeader = i18n.translate(
@ -69,6 +74,8 @@ const cancelAria = i18n.translate(
}
);
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
export interface TableProps {
indexPattern: DataView;
items: SourceFiltersTableFilter[];
@ -83,8 +90,11 @@ export interface TableState {
editingFilterValue: string;
}
export class Table extends Component<TableProps, TableState> {
constructor(props: TableProps) {
class TableClass extends Component<
TableProps & EuiTablePersistInjectedProps<SourceFiltersTableFilter>,
TableState
> {
constructor(props: TableProps & EuiTablePersistInjectedProps<SourceFiltersTableFilter>) {
super(props);
this.state = {
editingFilterId: '',
@ -227,11 +237,15 @@ export class Table extends Component<TableProps, TableState> {
}
render() {
const { items, isSaving } = this.props;
const {
items,
isSaving,
euiTablePersist: { pageSize, sorting, onTableChange },
} = this.props;
const columns = this.getColumns();
const pagination = {
initialPageSize: 10,
pageSizeOptions: [5, 10, 25, 50],
pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
};
return (
@ -240,8 +254,17 @@ export class Table extends Component<TableProps, TableState> {
items={items}
columns={columns}
pagination={pagination}
sorting={true}
sorting={sorting}
onTableChange={onTableChange}
/>
);
}
}
export const TableWithoutPersist = TableClass; // For testing purposes
export const Table = withEuiTablePersist(TableClass, {
tableId: 'dataViewsSourceFilters',
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialPageSize: 10,
});

View file

@ -30,6 +30,8 @@ import { NoDataViewsPromptComponent, useOnTryESQL } from '@kbn/shared-ux-prompt-
import type { SpacesContextProps } from '@kbn/spaces-plugin/public';
import { DataViewType } from '@kbn/data-views-plugin/public';
import { RollupDeprecationTooltip } from '@kbn/rollup';
import { useEuiTablePersist } from '@kbn/shared-ux-table-persist';
import type { IndexPatternManagmentContext } from '../../types';
import { getListBreadcrumbs } from '../breadcrumbs';
import { type RemoveDataViewProps, removeDataView } from '../edit_index_pattern';
@ -42,10 +44,7 @@ import { deleteModalMsg } from './delete_modal_msg';
import { NoData } from './no_data';
import { SpacesList } from './spaces_list';
const pagination = {
initialPageSize: 10,
pageSizeOptions: [5, 10, 25, 50],
};
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
const sorting = {
sort: {
@ -123,6 +122,12 @@ export const IndexPatternTable = ({
};
const onTryESQL = useOnTryESQL(useOnTryESQLParams);
const { pageSize, onTableChange } = useEuiTablePersist<IndexPatternTableItem>({
tableId: 'dataViewsIndexPattern',
initialPageSize: 10,
pageSizeOptions: PAGE_SIZE_OPTIONS,
});
const handleOnChange = ({ queryText, error }: { queryText: string; error: unknown }) => {
if (!error) {
setQuery(queryText);
@ -361,8 +366,12 @@ export const IndexPatternTable = ({
itemId="id"
items={indexPatterns}
columns={columns}
pagination={pagination}
pagination={{
pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
}}
sorting={sorting}
onTableChange={onTableChange}
search={search}
selection={dataViews.getCanSaveSync() ? selection : undefined}
/>

View file

@ -46,6 +46,7 @@
"@kbn/react-kibana-mount",
"@kbn/rollup",
"@kbn/share-plugin",
"@kbn/shared-ux-table-persist",
],
"exclude": [
"target/**/*",

View file

@ -22,6 +22,7 @@ exports[`OpenSearchPanel render 1`] = `
</EuiFlyoutHeader>
<EuiFlyoutBody>
<SavedObjectFinder
id="discoverOpenSearch"
noItemsMessage={
<Memo(MemoizedFormattedMessage)
defaultMessage="No matching searches found."

View file

@ -50,6 +50,7 @@ export function OpenSearchPanel(props: OpenSearchPanelProps) {
</EuiFlyoutHeader>
<EuiFlyoutBody>
<SavedObjectFinder
id="discoverOpenSearch"
services={{
savedObjectsTagging,
contentClient,

View file

@ -197,6 +197,7 @@ export const AddPanelFlyout = ({
</EuiFlyoutHeader>
<EuiFlyoutBody>
<SavedObjectFinder
id="embeddableAddPanel"
services={{
contentClient: contentManagement.client,
savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(),

View file

@ -99,6 +99,7 @@ export const EventAnnotationGroupSavedObjectFinder = ({
</EuiFlexGroup>
) : (
<SavedObjectFinder
id="eventAnnotationGroup"
key="searchSavedObjectFinder"
fixedPageSize={fixedPageSize}
onChoose={(id, type, fullName, savedObject) => {

View file

@ -12,10 +12,11 @@ import React from 'react';
import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
import type { ContentClient } from '@kbn/content-management-plugin/public';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { SavedObjectFinderProps } from './saved_object_finder';
import type { HOCProps } from '@kbn/shared-ux-table-persist';
import type { SavedObjectFinderItem, SavedObjectFinderProps } from './saved_object_finder';
const LazySavedObjectFinder = React.lazy(() => import('./saved_object_finder'));
const SavedObjectFinder = (props: SavedObjectFinderProps) => (
const SavedObjectFinder = (props: HOCProps<SavedObjectFinderItem, SavedObjectFinderProps>) => (
<React.Suspense
fallback={
<EuiDelayRender delay={300}>
@ -32,7 +33,7 @@ export const getSavedObjectFinder = (
uiSettings: IUiSettingsClient,
savedObjectsTagging?: SavedObjectsTaggingApi
) => {
return (props: SavedObjectFinderProps) => (
return (props: HOCProps<SavedObjectFinderItem, SavedObjectFinderProps>) => (
<SavedObjectFinder {...props} services={{ savedObjectsTagging, contentClient, uiSettings }} />
);
};

View file

@ -28,7 +28,10 @@ import { IconType } from '@elastic/eui';
import { mount, shallow } from 'enzyme';
import React from 'react';
import * as sinon from 'sinon';
import { SavedObjectFinderUi as SavedObjectFinder } from './saved_object_finder';
import {
SavedObjectFinderWithoutPersist as SavedObjectFinder,
SavedObjectFinderUi,
} from './saved_object_finder';
import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks';
import { findTestSubject } from '@kbn/test-jest-helpers';
import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
@ -72,6 +75,15 @@ describe('SavedObjectsFinder', () => {
},
];
const baseProps = {
id: 'foo',
euiTablePersist: {
pageSize: 10,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'title' as const } },
},
};
const contentManagement = contentManagementMock.createStartContract();
const contentClient = contentManagement.client;
beforeEach(() => {
@ -109,6 +121,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{
uiSettings,
contentClient,
@ -134,6 +147,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -157,6 +171,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
onChoose={chooseStub}
savedObjectMetaData={searchMetaData}
@ -179,6 +194,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
helpText="This is some description about the action"
@ -199,6 +215,7 @@ describe('SavedObjectsFinder', () => {
const button = <EuiButton>Hello</EuiButton>;
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
leftChildren={button}
@ -227,6 +244,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
/>
@ -251,6 +269,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
/>
@ -279,6 +298,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -299,6 +319,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -322,6 +343,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
/>
@ -346,6 +368,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
/>
@ -375,6 +398,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={[
{
@ -402,6 +426,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -430,6 +455,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -453,6 +479,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = shallow(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={[
{
@ -492,6 +519,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
showFilter={true}
savedObjectMetaData={metaDataConfig}
@ -512,6 +540,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
showFilter={false}
savedObjectMetaData={metaDataConfig}
@ -530,6 +559,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
showFilter={true}
savedObjectMetaData={searchMetaData}
@ -549,6 +579,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging: undefined }}
showFilter={true}
savedObjectMetaData={metaDataConfig}
@ -568,6 +599,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
showFilter={true}
savedObjectMetaData={metaDataConfig}
@ -608,6 +640,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
showFilter={true}
savedObjectMetaData={metaDataConfig}
@ -665,6 +698,7 @@ describe('SavedObjectsFinder', () => {
const noItemsMessage = <span id="myNoItemsMessage" />;
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
noItemsMessage={noItemsMessage}
savedObjectMetaData={searchMetaData}
@ -693,6 +727,11 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
id="foo"
euiTablePersist={{
...baseProps.euiTablePersist,
pageSize: 15,
}}
services={{ uiSettings, contentClient, savedObjectsTagging }}
initialPageSize={15}
savedObjectMetaData={searchMetaData}
@ -715,6 +754,11 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
id="foo"
euiTablePersist={{
...baseProps.euiTablePersist,
pageSize: 5,
}}
services={{ uiSettings, contentClient, savedObjectsTagging }}
initialPageSize={15}
savedObjectMetaData={searchMetaData}
@ -742,14 +786,14 @@ describe('SavedObjectsFinder', () => {
);
const wrapper = mount(
<SavedObjectFinder
<SavedObjectFinderUi
id="foo"
services={{ uiSettings, contentClient, savedObjectsTagging }}
initialPageSize={15}
savedObjectMetaData={searchMetaData}
/>
);
wrapper.instance().componentDidMount!();
await nextTick();
wrapper.update();
expect(wrapper.find(EuiInMemoryTable).find('tbody tr')).toHaveLength(15);
@ -774,6 +818,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
fixedPageSize={33}
savedObjectMetaData={searchMetaData}
@ -796,6 +841,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
fixedPageSize={33}
savedObjectMetaData={searchMetaData}
@ -827,6 +873,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -840,6 +887,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={[
{
@ -862,6 +910,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={searchMetaData}
/>
@ -884,6 +933,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={[
{
@ -912,6 +962,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
/>
@ -933,6 +984,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, savedObjectsTagging, contentClient }}
savedObjectMetaData={searchMetaData}
/>
@ -954,6 +1006,7 @@ describe('SavedObjectsFinder', () => {
const wrapper = mount(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging: undefined }}
savedObjectMetaData={metaDataConfig}
/>
@ -978,6 +1031,7 @@ describe('SavedObjectsFinder', () => {
render(
<SavedObjectFinder
{...baseProps}
services={{ uiSettings, contentClient, savedObjectsTagging }}
savedObjectMetaData={metaDataConfig}
getTooltipText={(item) => (item.id === doc3.id ? tooltipText : undefined)}
@ -990,7 +1044,7 @@ describe('SavedObjectsFinder', () => {
const tooltip = screen.queryByText(tooltipText);
if (show) {
expect(tooltip).toBeInTheDocument();
expect(tooltip)?.toBeInTheDocument();
} else {
expect(tooltip).toBeNull();
}

View file

@ -25,15 +25,21 @@ import {
EuiToolTip,
EuiIconTip,
IconType,
PropertySort,
Query,
SearchFilterConfig,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist/src';
import { FinderAttributes, SavedObjectCommon, LISTING_LIMIT_SETTING } from '../../common';
const PAGE_SIZE_OPTIONS = [5, 10, 15, 25];
export interface SavedObjectMetaData<T extends FinderAttributes = FinderAttributes> {
type: string;
name: string;
@ -45,7 +51,7 @@ export interface SavedObjectMetaData<T extends FinderAttributes = FinderAttribut
includeFields?: string[];
}
interface SavedObjectFinderItem extends SavedObjectCommon {
export interface SavedObjectFinderItem extends SavedObjectCommon {
title: string | null;
name: string | null;
simple: SavedObjectCommon<FinderAttributes>;
@ -55,7 +61,6 @@ interface SavedObjectFinderState {
items: SavedObjectFinderItem[];
query: Query;
isFetchingItems: boolean;
sort?: PropertySort;
}
interface SavedObjectFinderServices {
@ -65,6 +70,7 @@ interface SavedObjectFinderServices {
}
interface BaseSavedObjectFinder {
id: string;
services: SavedObjectFinderServices;
onChoose?: (
id: SavedObjectCommon['id'],
@ -93,8 +99,8 @@ interface SavedObjectFinderInitialPageSize extends BaseSavedObjectFinder {
export type SavedObjectFinderProps = SavedObjectFinderFixedPage | SavedObjectFinderInitialPageSize;
export class SavedObjectFinderUi extends React.Component<
SavedObjectFinderProps,
class SavedObjectFinderUiClass extends React.Component<
SavedObjectFinderProps & EuiTablePersistInjectedProps<SavedObjectFinderItem>,
SavedObjectFinderState
> {
public static propTypes = {
@ -174,7 +180,7 @@ export class SavedObjectFinderUi extends React.Component<
}
}, 300);
constructor(props: SavedObjectFinderProps) {
constructor(props: SavedObjectFinderProps & EuiTablePersistInjectedProps<SavedObjectFinderItem>) {
super(props);
this.state = {
@ -211,7 +217,11 @@ export class SavedObjectFinderUi extends React.Component<
};
public render() {
const { onChoose, savedObjectMetaData } = this.props;
const {
onChoose,
savedObjectMetaData,
euiTablePersist: { pageSize, sorting, onTableChange },
} = this.props;
const taggingApi = this.props.services.savedObjectsTagging;
const originalTagColumn = taggingApi?.ui.getTableColumnDefinition();
const tagColumn: EuiTableFieldDataColumnType<SavedObjectCommon> | undefined = originalTagColumn
@ -320,16 +330,11 @@ export class SavedObjectFinderUi extends React.Component<
...(tagColumn ? [tagColumn] : []),
];
const pagination = {
initialPageSize: this.props.initialPageSize || this.props.fixedPageSize || 10,
pageSizeOptions: [5, 10, 15, 25],
initialPageSize: !!this.props.fixedPageSize ? this.props.fixedPageSize : pageSize ?? 10,
pageSize: !!this.props.fixedPageSize ? undefined : pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
showPerPageOptions: !this.props.fixedPageSize,
};
const sorting = {
sort: this.state.sort ?? {
field: this.state.query?.text ? '' : 'title',
direction: 'asc',
},
};
const typeFilter: SearchFilterConfig = {
type: 'field_value_selection',
field: 'type',
@ -382,10 +387,8 @@ export class SavedObjectFinderUi extends React.Component<
message={this.props.noItemsMessage}
search={search}
pagination={pagination}
sorting={sorting}
onTableChange={({ sort }) => {
this.setState({ sort });
}}
sorting={!!this.state.query?.text ? undefined : sorting}
onTableChange={onTableChange}
/>
</EuiFlexItem>
</EuiFlexGroup>
@ -393,6 +396,16 @@ export class SavedObjectFinderUi extends React.Component<
}
}
export const SavedObjectFinderUi = withEuiTablePersist(SavedObjectFinderUiClass, {
get: (props) => ({
tableId: `soFinder-${props.id}`,
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialPageSize: props.initialPageSize ?? props.fixedPageSize ?? 10,
}),
});
export const SavedObjectFinderWithoutPersist = SavedObjectFinderUiClass; // For testing
// Needed for React.lazy
// eslint-disable-next-line import/no-default-export
export default SavedObjectFinderUi;

View file

@ -15,6 +15,7 @@
"@kbn/content-management-plugin",
"@kbn/content-management-utils",
"@kbn/core-ui-settings-browser",
"@kbn/shared-ux-table-persist",
],
"exclude": [
"target/**/*",

View file

@ -102,7 +102,6 @@ exports[`Flyout conflicts should allow conflict resolution 1`] = `
onTableChange={[Function]}
pagination={
Object {
"pageIndex": 0,
"pageSize": 5,
"pageSizeOptions": Array [
5,
@ -251,10 +250,6 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = `
"newIndexPatternId": "2",
},
],
"unmatchedReferencesTablePagination": Object {
"pageIndex": 0,
"pageSize": 5,
},
},
},
],

View file

@ -110,7 +110,12 @@ exports[`Relationships should render dashboards normally 1`] = `
},
]
}
pagination={true}
onTableChange={[Function]}
pagination={
Object {
"pageSize": 10,
}
}
rowProps={[Function]}
search={
Object {
@ -310,7 +315,12 @@ exports[`Relationships should render index patterns normally 1`] = `
},
]
}
pagination={true}
onTableChange={[Function]}
pagination={
Object {
"pageSize": 10,
}
}
rowProps={[Function]}
search={
Object {
@ -501,7 +511,12 @@ exports[`Relationships should render invalid relations 1`] = `
]
}
items={Array []}
pagination={true}
onTableChange={[Function]}
pagination={
Object {
"pageSize": 10,
}
}
rowProps={[Function]}
search={
Object {
@ -652,7 +667,12 @@ exports[`Relationships should render searches normally 1`] = `
},
]
}
pagination={true}
onTableChange={[Function]}
pagination={
Object {
"pageSize": 10,
}
}
rowProps={[Function]}
search={
Object {
@ -813,7 +833,12 @@ exports[`Relationships should render visualizations normally 1`] = `
},
]
}
pagination={true}
onTableChange={[Function]}
pagination={
Object {
"pageSize": 10,
}
}
rowProps={[Function]}
search={
Object {

View file

@ -12,7 +12,7 @@ import { importFileMock, resolveImportErrorsMock } from './flyout.test.mocks';
import React from 'react';
import { shallowWithI18nProvider } from '@kbn/test-jest-helpers';
import { coreMock, httpServiceMock } from '@kbn/core/public/mocks';
import { Flyout, FlyoutProps, FlyoutState } from './flyout';
import { FlyoutClass as Flyout, FlyoutProps, FlyoutState } from './flyout';
import { ShallowWrapper } from 'enzyme';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
@ -21,15 +21,21 @@ const mockFile = {
path: '/home/foo.ndjson',
} as unknown as File;
const baseProps = {
euiTablePersist: {
pageSize: 5,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'foo' as const } },
},
};
describe('Flyout', () => {
let defaultProps: FlyoutProps;
const shallowRender = (props: FlyoutProps) => {
return shallowWithI18nProvider(<Flyout {...props} />) as unknown as ShallowWrapper<
FlyoutProps,
FlyoutState,
Flyout
>;
return shallowWithI18nProvider(
<Flyout {...baseProps} {...props} />
) as unknown as ShallowWrapper<FlyoutProps, FlyoutState, Flyout>;
};
beforeEach(() => {

View file

@ -36,6 +36,10 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { HttpStart, IBasePath } from '@kbn/core/public';
import { ISearchStart } from '@kbn/data-plugin/public';
import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist';
import type { SavedObjectManagementTypeInfo } from '../../../../common/types';
import {
importFile,
@ -50,6 +54,7 @@ import { ImportSummary } from './import_summary';
const CREATE_NEW_COPIES_DEFAULT = false;
const OVERWRITE_ALL_DEFAULT = true;
const PAGE_SIZE_OPTIONS = [5, 10, 25];
export interface FlyoutProps {
close: () => void;
@ -65,7 +70,6 @@ export interface FlyoutProps {
export interface FlyoutState {
unmatchedReferences?: ProcessedImportResponse['unmatchedReferences'];
unmatchedReferencesTablePagination: { pageIndex: number; pageSize: number };
failedImports?: ProcessedImportResponse['failedImports'];
successfulImports?: ProcessedImportResponse['successfulImports'];
conflictingRecord?: ConflictingRecord;
@ -95,16 +99,15 @@ const getErrorMessage = (e: any) => {
});
};
export class Flyout extends Component<FlyoutProps, FlyoutState> {
constructor(props: FlyoutProps) {
export class FlyoutClass extends Component<
FlyoutProps & EuiTablePersistInjectedProps<any>,
FlyoutState
> {
constructor(props: FlyoutProps & EuiTablePersistInjectedProps<unknown>) {
super(props);
this.state = {
unmatchedReferences: undefined,
unmatchedReferencesTablePagination: {
pageIndex: 0,
pageSize: 5,
},
conflictingRecord: undefined,
error: undefined,
file: undefined,
@ -275,7 +278,10 @@ export class Flyout extends Component<FlyoutProps, FlyoutState> {
};
renderUnmatchedReferences() {
const { unmatchedReferences, unmatchedReferencesTablePagination: tablePagination } = this.state;
const { unmatchedReferences } = this.state;
const {
euiTablePersist: { pageSize, onTableChange },
} = this.props;
if (!unmatchedReferences) {
return null;
@ -367,8 +373,8 @@ export class Flyout extends Component<FlyoutProps, FlyoutState> {
];
const pagination = {
...tablePagination,
pageSizeOptions: [5, 10, 25],
pageSize,
pageSizeOptions: PAGE_SIZE_OPTIONS,
};
return (
@ -376,16 +382,7 @@ export class Flyout extends Component<FlyoutProps, FlyoutState> {
items={unmatchedReferences as any[]}
columns={columns}
pagination={pagination}
onTableChange={({ page }) => {
if (page) {
this.setState({
unmatchedReferencesTablePagination: {
pageSize: page.size,
pageIndex: page.index,
},
});
}
}}
onTableChange={onTableChange}
/>
);
}
@ -657,3 +654,9 @@ export class Flyout extends Component<FlyoutProps, FlyoutState> {
);
}
}
export const Flyout = withEuiTablePersist(FlyoutClass, {
tableId: 'savedObjectsMgmtUnmatchedReferences',
pageSizeOptions: PAGE_SIZE_OPTIONS,
initialPageSize: 5,
});

View file

@ -11,7 +11,7 @@ import React from 'react';
import { shallowWithI18nProvider } from '@kbn/test-jest-helpers';
import { httpServiceMock } from '@kbn/core/public/mocks';
import type { SavedObjectManagementTypeInfo } from '../../../../common/types';
import { Relationships, RelationshipsProps } from './relationships';
import { RelationshipsClass as Relationships, RelationshipsProps } from './relationships';
jest.mock('../../../lib/fetch_export_by_type_and_search', () => ({
fetchExportByTypeAndSearch: jest.fn(),
@ -21,6 +21,14 @@ jest.mock('../../../lib/fetch_export_objects', () => ({
fetchExportObjects: jest.fn(),
}));
const baseProps = {
euiTablePersist: {
pageSize: 10,
onTableChange: () => {},
sorting: { sort: { direction: 'asc' as const, field: 'id' as const } },
},
};
const allowedTypes: SavedObjectManagementTypeInfo[] = [
{
name: 'index-pattern',
@ -86,7 +94,7 @@ describe('Relationships', () => {
close: jest.fn(),
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Make sure we are showing loading
expect(component.find('EuiLoadingElastic').length).toBe(1);
@ -155,7 +163,7 @@ describe('Relationships', () => {
close: jest.fn(),
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Make sure we are showing loading
expect(component.find('EuiLoadingElastic').length).toBe(1);
@ -223,7 +231,7 @@ describe('Relationships', () => {
close: jest.fn(),
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Make sure we are showing loading
expect(component.find('EuiLoadingElastic').length).toBe(1);
@ -292,7 +300,7 @@ describe('Relationships', () => {
showPlainSpinner: true,
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Make sure we are showing loading
expect(component.find('EuiLoadingSpinner').length).toBe(1);
@ -332,7 +340,7 @@ describe('Relationships', () => {
close: jest.fn(),
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Ensure all promises resolve
await new Promise((resolve) => process.nextTick(resolve));
@ -378,7 +386,7 @@ describe('Relationships', () => {
close: jest.fn(),
};
const component = shallowWithI18nProvider(<Relationships {...props} />);
const component = shallowWithI18nProvider(<Relationships {...baseProps} {...props} />);
// Ensure all promises resolve
await new Promise((resolve) => process.nextTick(resolve));

View file

@ -27,6 +27,10 @@ import { SearchFilterConfig } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { IBasePath } from '@kbn/core/public';
import {
withEuiTablePersist,
type EuiTablePersistInjectedProps,
} from '@kbn/shared-ux-table-persist';
import type { SavedObjectManagementTypeInfo } from '../../../../common/types';
import { getDefaultTitle, getSavedObjectLabel } from '../../../lib';
import type { v1 } from '../../../../common';
@ -83,8 +87,11 @@ const relationshipColumn = {
},
};
export class Relationships extends Component<RelationshipsProps, RelationshipsState> {
constructor(props: RelationshipsProps) {
export class RelationshipsClass extends Component<
RelationshipsProps & EuiTablePersistInjectedProps<SavedObjectRelation>,
RelationshipsState
> {
constructor(props: RelationshipsProps & EuiTablePersistInjectedProps<SavedObjectRelation>) {
super(props);
this.state = {
@ -218,7 +225,14 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
}
renderRelationshipsTable() {
const { goInspectObject, basePath, savedObject, allowedTypes, showPlainSpinner } = this.props;
const {
goInspectObject,
basePath,
savedObject,
allowedTypes,
showPlainSpinner,
euiTablePersist: { pageSize, onTableChange },
} = this.props;
const { relations, isLoading, error } = this.state;
if (error) {
@ -385,7 +399,8 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
<EuiInMemoryTable
items={relations}
columns={columns as any}
pagination={true}
pagination={{ pageSize }}
onTableChange={onTableChange}
search={search}
rowProps={() => ({
'data-test-subj': `relationshipsTableRow`,
@ -420,3 +435,8 @@ export class Relationships extends Component<RelationshipsProps, RelationshipsSt
);
}
}
export const Relationships = withEuiTablePersist(RelationshipsClass, {
tableId: 'savedObjectsMgmtRelationships',
initialPageSize: 10,
});

View file

@ -32,6 +32,7 @@
"@kbn/shared-ux-link-redirect-app",
"@kbn/code-editor",
"@kbn/react-kibana-context-render",
"@kbn/shared-ux-table-persist",
],
"exclude": [
"target/**/*",

View file

@ -49,6 +49,7 @@ export class SearchSelection extends React.Component<SearchSelectionProps> {
<DialogNavigation goBack={this.props.goBack} />
<SavedObjectFinder
key="searchSavedObjectFinder"
id="visSearchSelection"
onChoose={this.props.onSearchSelected}
showFilter
noItemsMessage={i18n.translate(

View file

@ -179,6 +179,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await visEditor.setInterval('Second');
await visEditor.clickGo();
await inspector.open();
await inspector.setTablePageSize(20);
await inspector.expectTableData(expectedTableData);
await inspector.close();
});
@ -211,6 +212,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await visEditor.toggleScaleMetrics();
await visEditor.clickGo();
await inspector.open();
await inspector.setTablePageSize(20);
await inspector.expectTableData(expectedTableData);
await inspector.close();
});
@ -245,6 +247,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await visEditor.selectAggregateWith('average');
await visEditor.clickGo();
await inspector.open();
await inspector.setTablePageSize(20);
await inspector.expectTableData(expectedTableData);
await inspector.close();
});

View file

@ -97,8 +97,7 @@ export class InspectorService extends FtrService {
* @param size rows count
*/
public async setTablePageSize(size: number): Promise<void> {
const panel = await this.testSubjects.find('inspectorPanel');
await this.find.clickByButtonText('Rows per page: 20', panel);
await this.testSubjects.click('tablePaginationPopoverButton');
// The buttons for setting table page size are in a popover element. This popover
// element appears as if it's part of the inspectorPanel but it's really attached
// to the body element by a portal.

View file

@ -135,6 +135,7 @@ export const AddEmbeddableFlyout: FC<Props> = ({
</EuiFlyoutHeader>
<EuiFlyoutBody>
<SavedObjectFinder
id="canvasEmbeddableFlyout"
onChoose={onAddPanel}
savedObjectMetaData={metaData}
showFilter={true}

View file

@ -343,6 +343,7 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({
<EuiModalBody>
<SavedObjectFinder
key="searchSavedObjectFinder"
id="casesMarkdownLens"
onChoose={handleChooseLensSO}
showFilter={false}
noItemsMessage={

View file

@ -29,6 +29,7 @@ export function SourcePicker({
}: SourcePickerProps) {
return (
<SavedObjectFinder
id="graphSourcePicker"
services={{ contentClient: contentManagement.client, uiSettings }}
onChoose={(_id, _type, _name, indexPattern) => {
onIndexPatternSelected(indexPattern as IndexPatternSavedObject);

View file

@ -125,6 +125,7 @@ export const SourceSelection: FC = () => {
</>
)}
<SavedObjectFinder
id="mlDFASourceSelection"
key="searchSavedObjectFinder"
onChoose={onSearchSelected}
showFilter

View file

@ -53,6 +53,7 @@ export const DataDriftIndexOrSearchRedirect: FC = () => {
</MlPageHeader>
<EuiPanel hasShadow={false} hasBorder>
<SavedObjectFinder
id="mlDataDriftDataViewsPicker"
key="searchSavedObjectFinder"
onChoose={onObjectSelection}
showFilter

View file

@ -146,6 +146,7 @@ export const ChangeDataViewModal: FC<Props> = ({ onClose }) => {
<EuiSpacer size="s" />
<SavedObjectFinder
id="mlJobsDatafeedDataView"
key="searchSavedObjectFinder"
onChoose={onDataViewSelected}
showFilter

View file

@ -56,6 +56,7 @@ export const Page: FC<PageProps> = ({
</MlPageHeader>
<EuiPanel hasShadow={false} hasBorder>
<SavedObjectFinder
id="mlJobsDatafeedDataView"
key="searchSavedObjectFinder"
onChoose={onObjectSelection}
showFilter

View file

@ -48,6 +48,7 @@ export const SearchSelection: FC<SearchSelectionProps> = ({
</EuiModalHeader>
<EuiModalBody>
<SavedObjectFinder
id="transformMgtSearchSelection"
key="searchSavedObjectFinder"
onChoose={onSearchSelected}
showFilter