mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
842 lines
25 KiB
TypeScript
842 lines
25 KiB
TypeScript
/*
|
|
* 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, { useCallback, useMemo, useState, useRef, useEffect } from 'react';
|
|
import classnames from 'classnames';
|
|
import { FormattedMessage } from '@kbn/i18n-react';
|
|
import { of } from 'rxjs';
|
|
import useObservable from 'react-use/lib/useObservable';
|
|
import './data_table.scss';
|
|
import type { Storage } from '@kbn/kibana-utils-plugin/public';
|
|
import {
|
|
EuiDataGridSorting,
|
|
EuiDataGrid,
|
|
EuiScreenReaderOnly,
|
|
EuiSpacer,
|
|
EuiText,
|
|
htmlIdGenerator,
|
|
EuiLoadingSpinner,
|
|
EuiIcon,
|
|
EuiDataGridRefProps,
|
|
EuiDataGridInMemory,
|
|
EuiDataGridControlColumn,
|
|
EuiDataGridCustomBodyProps,
|
|
EuiDataGridCellValueElementProps,
|
|
EuiDataGridStyle,
|
|
} from '@elastic/eui';
|
|
import type { DataView } from '@kbn/data-views-plugin/public';
|
|
import {
|
|
useDataGridColumnsCellActions,
|
|
type UseDataGridColumnsCellActionsProps,
|
|
} from '@kbn/cell-actions';
|
|
import type { ToastsStart, IUiSettingsClient } from '@kbn/core/public';
|
|
import type { Serializable } from '@kbn/utility-types';
|
|
import type { DataTableRecord } from '@kbn/discover-utils/types';
|
|
import { getShouldShowFieldHandler, DOC_HIDE_TIME_COLUMN_SETTING } from '@kbn/discover-utils';
|
|
import type { DataViewFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
|
|
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
|
import type { ThemeServiceStart } from '@kbn/react-kibana-context-common';
|
|
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
|
import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types';
|
|
import { UnifiedDataTableSettings, ValueToStringConverter } from '../types';
|
|
import { getDisplayedColumns } from '../utils/columns';
|
|
import { convertValueToString } from '../utils/convert_value_to_string';
|
|
import { getRowsPerPageOptions } from '../utils/rows_per_page';
|
|
import { getRenderCellValueFn } from '../utils/get_render_cell_value';
|
|
import { getEuiGridColumns, getLeadControlColumns, getVisibleColumns } from './data_table_columns';
|
|
import { UnifiedDataTableContext } from '../table_context';
|
|
import { getSchemaDetectors } from './data_table_schema';
|
|
import { DataTableDocumentToolbarBtn } from './data_table_document_selection';
|
|
import { useRowHeightsOptions } from '../hooks/use_row_heights_options';
|
|
import {
|
|
DEFAULT_ROWS_PER_PAGE,
|
|
GRID_STYLE,
|
|
toolbarVisibility as toolbarVisibilityDefaults,
|
|
} from '../constants';
|
|
import { UnifiedDataTableFooter } from './data_table_footer';
|
|
|
|
export type SortOrder = [string, string];
|
|
|
|
export enum DataLoadingState {
|
|
loading = 'loading',
|
|
loadingMore = 'loadingMore',
|
|
loaded = 'loaded',
|
|
}
|
|
|
|
const themeDefault = { darkMode: false };
|
|
|
|
interface SortObj {
|
|
id: string;
|
|
direction: string;
|
|
}
|
|
|
|
export interface UnifiedDataTableProps {
|
|
/**
|
|
* Determines which element labels the grid for ARIA
|
|
*/
|
|
ariaLabelledBy: string;
|
|
/**
|
|
* Optional class name to apply
|
|
*/
|
|
className?: string;
|
|
/**
|
|
* Determines ids of the columns which are displayed
|
|
*/
|
|
columns: string[];
|
|
/**
|
|
* If set, the given document is displayed in a flyout
|
|
*/
|
|
expandedDoc?: DataTableRecord;
|
|
/**
|
|
* The used data view
|
|
*/
|
|
dataView: DataView;
|
|
/**
|
|
* Determines if data is currently loaded
|
|
*/
|
|
loadingState: DataLoadingState;
|
|
/**
|
|
* Function to add a filter in the grid cell or document flyout
|
|
*/
|
|
onFilter: DocViewFilterFn;
|
|
/**
|
|
* Function triggered when a column is resized by the user
|
|
*/
|
|
onResize?: (colSettings: { columnId: string; width: number }) => void;
|
|
/**
|
|
* Function to set all columns
|
|
*/
|
|
onSetColumns: (columns: string[], hideTimeColumn: boolean) => void;
|
|
/**
|
|
* function to change sorting of the documents, skipped when isSortEnabled is set to false
|
|
*/
|
|
onSort?: (sort: string[][]) => void;
|
|
/**
|
|
* Array of documents provided by Elasticsearch
|
|
*/
|
|
rows?: DataTableRecord[];
|
|
/**
|
|
* The max size of the documents returned by Elasticsearch
|
|
*/
|
|
sampleSize: number;
|
|
/**
|
|
* Function to set the expanded document, which is displayed in a flyout
|
|
*/
|
|
setExpandedDoc?: (doc?: DataTableRecord) => void;
|
|
/**
|
|
* Grid display settings persisted in Elasticsearch (e.g. column width)
|
|
*/
|
|
settings?: UnifiedDataTableSettings;
|
|
/**
|
|
* Search description
|
|
*/
|
|
searchDescription?: string;
|
|
/**
|
|
* Search title
|
|
*/
|
|
searchTitle?: string;
|
|
/**
|
|
* Determines whether the time columns should be displayed (legacy settings)
|
|
*/
|
|
showTimeCol: boolean;
|
|
/**
|
|
* Determines whether the full screen button should be displayed
|
|
*/
|
|
showFullScreenButton?: boolean;
|
|
/**
|
|
* Manage user sorting control
|
|
*/
|
|
isSortEnabled?: boolean;
|
|
/**
|
|
* Current sort setting
|
|
*/
|
|
sort: SortOrder[];
|
|
/**
|
|
* How the data is fetched
|
|
*/
|
|
useNewFieldsApi: boolean;
|
|
/**
|
|
* Manage pagination control
|
|
*/
|
|
isPaginationEnabled?: boolean;
|
|
/**
|
|
* List of used control columns (available: 'openDetails', 'select')
|
|
*/
|
|
controlColumnIds?: string[];
|
|
/**
|
|
* Row height from state
|
|
*/
|
|
rowHeightState?: number;
|
|
/**
|
|
* Update row height state
|
|
*/
|
|
onUpdateRowHeight?: (rowHeight: number) => void;
|
|
/**
|
|
* Is text base lang mode enabled
|
|
*/
|
|
isPlainRecord?: boolean;
|
|
/**
|
|
* Current state value for rowsPerPage
|
|
*/
|
|
rowsPerPageState?: number;
|
|
/**
|
|
* Update rows per page state
|
|
*/
|
|
onUpdateRowsPerPage?: (rowsPerPage: number) => void;
|
|
/**
|
|
* Callback to execute on edit runtime field
|
|
*/
|
|
onFieldEdited?: () => void;
|
|
/**
|
|
* Optional triggerId to retrieve the column cell actions that will override the default ones
|
|
*/
|
|
cellActionsTriggerId?: string;
|
|
/**
|
|
* Service dependencies
|
|
*/
|
|
services: {
|
|
theme: ThemeServiceStart;
|
|
fieldFormats: FieldFormatsStart;
|
|
uiSettings: IUiSettingsClient;
|
|
dataViewFieldEditor: DataViewFieldEditorStart;
|
|
toastNotifications: ToastsStart;
|
|
storage: Storage;
|
|
data: DataPublicPluginStart;
|
|
};
|
|
/**
|
|
* Callback to render DocumentView when the document is expanded
|
|
*/
|
|
renderDocumentView?: (
|
|
hit: DataTableRecord,
|
|
displayedRows: DataTableRecord[],
|
|
displayedColumns: string[]
|
|
) => JSX.Element | undefined;
|
|
/**
|
|
* Optional value for providing configuration setting for UnifiedDataTable rows height
|
|
*/
|
|
configRowHeight?: number;
|
|
/**
|
|
* Optional value for providing configuration setting for enabling to display the complex fields in the table. Default is true.
|
|
*/
|
|
showMultiFields?: boolean;
|
|
/**
|
|
* Optional value for providing configuration setting for maximum number of document fields to display in the table. Default is 50.
|
|
*/
|
|
maxDocFieldsDisplayed?: number;
|
|
/**
|
|
* Optional value for providing EuiDataGridControlColumn list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select.
|
|
*/
|
|
externalControlColumns?: EuiDataGridControlColumn[];
|
|
/**
|
|
* Number total hits from ES
|
|
*/
|
|
totalHits?: number;
|
|
/**
|
|
* To fetch more
|
|
*/
|
|
onFetchMoreRecords?: () => void;
|
|
/**
|
|
* Optional value for providing the additional controls available in the UnifiedDataTable toolbar to manage it's records or state. UnifiedDataTable includes Columns, Sorting and Bulk Actions.
|
|
*/
|
|
externalAdditionalControls?: React.ReactNode;
|
|
/**
|
|
* Optional list of number type values to set custom UnifiedDataTable paging options to display the records per page.
|
|
*/
|
|
rowsPerPageOptions?: number[];
|
|
/**
|
|
* An optional function called to completely customize and control the rendering of
|
|
* EuiDataGrid's body and cell placement. This can be used to, e.g. remove EuiDataGrid's
|
|
* virtualization library, or roll your own.
|
|
*
|
|
* This component is **only** meant as an escape hatch for extremely custom use cases.
|
|
*
|
|
* Behind the scenes, this function is treated as a React component,
|
|
* allowing hooks, context, and other React concepts to be used.
|
|
* It receives #EuiDataGridCustomBodyProps as its only argument.
|
|
*/
|
|
renderCustomGridBody?: (args: EuiDataGridCustomBodyProps) => React.ReactNode;
|
|
/**
|
|
* An optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid.
|
|
*/
|
|
trailingControlColumns?: EuiDataGridControlColumn[];
|
|
/**
|
|
* An optional value for a custom number of the visible cell actions in the table. By default is up to 3.
|
|
**/
|
|
visibleCellActions?: number;
|
|
/**
|
|
* An optional settings for a specified fields rendering like links. Applied only for the listed fields rendering.
|
|
*/
|
|
externalCustomRenderers?: Record<
|
|
string,
|
|
(props: EuiDataGridCellValueElementProps) => React.ReactNode
|
|
>;
|
|
/**
|
|
* Name of the UnifiedDataTable consumer component or application
|
|
*/
|
|
consumer?: string;
|
|
/**
|
|
* Optional key/value pairs to set guided onboarding steps ids for a data table components included to guided tour.
|
|
*/
|
|
componentsTourSteps?: Record<string, string>;
|
|
/**
|
|
* Optional gridStyle override.
|
|
*/
|
|
gridStyleOverride?: EuiDataGridStyle;
|
|
}
|
|
|
|
export const EuiDataGridMemoized = React.memo(EuiDataGrid);
|
|
|
|
const CONTROL_COLUMN_IDS_DEFAULT = ['openDetails', 'select'];
|
|
|
|
export const UnifiedDataTable = ({
|
|
ariaLabelledBy,
|
|
columns,
|
|
controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT,
|
|
dataView,
|
|
loadingState,
|
|
onFilter,
|
|
onResize,
|
|
onSetColumns,
|
|
onSort,
|
|
rows,
|
|
sampleSize,
|
|
searchDescription,
|
|
searchTitle,
|
|
settings,
|
|
showTimeCol,
|
|
showFullScreenButton = true,
|
|
sort,
|
|
useNewFieldsApi,
|
|
isSortEnabled = true,
|
|
isPaginationEnabled = true,
|
|
cellActionsTriggerId,
|
|
className,
|
|
rowHeightState,
|
|
onUpdateRowHeight,
|
|
isPlainRecord = false,
|
|
rowsPerPageState,
|
|
onUpdateRowsPerPage,
|
|
onFieldEdited,
|
|
services,
|
|
renderCustomGridBody,
|
|
trailingControlColumns,
|
|
totalHits,
|
|
onFetchMoreRecords,
|
|
renderDocumentView,
|
|
setExpandedDoc,
|
|
expandedDoc,
|
|
configRowHeight,
|
|
showMultiFields = true,
|
|
maxDocFieldsDisplayed = 50,
|
|
externalControlColumns,
|
|
externalAdditionalControls,
|
|
rowsPerPageOptions,
|
|
visibleCellActions,
|
|
externalCustomRenderers,
|
|
consumer = 'discover',
|
|
componentsTourSteps,
|
|
gridStyleOverride,
|
|
}: UnifiedDataTableProps) => {
|
|
const { fieldFormats, toastNotifications, dataViewFieldEditor, uiSettings, storage, data } =
|
|
services;
|
|
const { darkMode } = useObservable(services.theme?.theme$ ?? of(themeDefault), themeDefault);
|
|
const dataGridRef = useRef<EuiDataGridRefProps>(null);
|
|
const [selectedDocs, setSelectedDocs] = useState<string[]>([]);
|
|
const [isFilterActive, setIsFilterActive] = useState(false);
|
|
const displayedColumns = getDisplayedColumns(columns, dataView);
|
|
const defaultColumns = displayedColumns.includes('_source');
|
|
const usedSelectedDocs = useMemo(() => {
|
|
if (!selectedDocs.length || !rows?.length) {
|
|
return [];
|
|
}
|
|
const idMap = rows.reduce((map, row) => map.set(row.id, true), new Map());
|
|
// filter out selected docs that are no longer part of the current data
|
|
const result = selectedDocs.filter((docId) => !!idMap.get(docId));
|
|
if (result.length === 0 && isFilterActive) {
|
|
setIsFilterActive(false);
|
|
}
|
|
return result;
|
|
}, [selectedDocs, rows, isFilterActive]);
|
|
|
|
const displayedRows = useMemo(() => {
|
|
if (!rows) {
|
|
return [];
|
|
}
|
|
if (!isFilterActive || usedSelectedDocs.length === 0) {
|
|
return rows;
|
|
}
|
|
const rowsFiltered = rows.filter((row) => usedSelectedDocs.includes(row.id));
|
|
if (!rowsFiltered.length) {
|
|
// in case the selected docs are no longer part of the sample of 500, show all docs
|
|
return rows;
|
|
}
|
|
return rowsFiltered;
|
|
}, [rows, usedSelectedDocs, isFilterActive]);
|
|
|
|
const valueToStringConverter: ValueToStringConverter = useCallback(
|
|
(rowIndex, columnId, options) => {
|
|
return convertValueToString({
|
|
rowIndex,
|
|
rows: displayedRows,
|
|
dataView,
|
|
columnId,
|
|
fieldFormats,
|
|
options,
|
|
});
|
|
},
|
|
[displayedRows, dataView, fieldFormats]
|
|
);
|
|
|
|
const unifiedDataTableContextValue = useMemo(
|
|
() => ({
|
|
expanded: expandedDoc,
|
|
setExpanded: setExpandedDoc,
|
|
rows: displayedRows,
|
|
onFilter,
|
|
dataView,
|
|
isDarkMode: darkMode,
|
|
selectedDocs: usedSelectedDocs,
|
|
setSelectedDocs: (newSelectedDocs: React.SetStateAction<string[]>) => {
|
|
setSelectedDocs(newSelectedDocs);
|
|
if (isFilterActive && newSelectedDocs.length === 0) {
|
|
setIsFilterActive(false);
|
|
}
|
|
},
|
|
valueToStringConverter,
|
|
componentsTourSteps,
|
|
}),
|
|
[
|
|
componentsTourSteps,
|
|
darkMode,
|
|
dataView,
|
|
displayedRows,
|
|
expandedDoc,
|
|
isFilterActive,
|
|
onFilter,
|
|
setExpandedDoc,
|
|
usedSelectedDocs,
|
|
valueToStringConverter,
|
|
]
|
|
);
|
|
|
|
/**
|
|
* Pagination
|
|
*/
|
|
const currentPageSize =
|
|
typeof rowsPerPageState === 'number' && rowsPerPageState > 0
|
|
? rowsPerPageState
|
|
: DEFAULT_ROWS_PER_PAGE;
|
|
const [pagination, setPagination] = useState({
|
|
pageIndex: 0,
|
|
pageSize: currentPageSize,
|
|
});
|
|
const rowCount = useMemo(() => (displayedRows ? displayedRows.length : 0), [displayedRows]);
|
|
const pageCount = useMemo(
|
|
() => Math.ceil(rowCount / pagination.pageSize),
|
|
[rowCount, pagination]
|
|
);
|
|
|
|
const paginationObj = useMemo(() => {
|
|
const onChangeItemsPerPage = (pageSize: number) => {
|
|
onUpdateRowsPerPage?.(pageSize);
|
|
};
|
|
|
|
const onChangePage = (pageIndex: number) =>
|
|
setPagination((paginationData) => ({ ...paginationData, pageIndex }));
|
|
|
|
return isPaginationEnabled
|
|
? {
|
|
onChangeItemsPerPage,
|
|
onChangePage,
|
|
pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex,
|
|
pageSize: pagination.pageSize,
|
|
pageSizeOptions: rowsPerPageOptions ?? getRowsPerPageOptions(pagination.pageSize),
|
|
}
|
|
: undefined;
|
|
}, [
|
|
isPaginationEnabled,
|
|
pagination.pageIndex,
|
|
pagination.pageSize,
|
|
pageCount,
|
|
rowsPerPageOptions,
|
|
onUpdateRowsPerPage,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
setPagination((paginationData) =>
|
|
paginationData.pageSize === currentPageSize
|
|
? paginationData
|
|
: { ...paginationData, pageSize: currentPageSize }
|
|
);
|
|
}, [currentPageSize, setPagination]);
|
|
|
|
/**
|
|
* Sorting
|
|
*/
|
|
const sortingColumns = useMemo(() => sort.map(([id, direction]) => ({ id, direction })), [sort]);
|
|
|
|
const [inmemorySortingColumns, setInmemorySortingColumns] = useState([]);
|
|
const onTableSort = useCallback(
|
|
(sortingColumnsData) => {
|
|
if (isSortEnabled) {
|
|
if (isPlainRecord) {
|
|
setInmemorySortingColumns(sortingColumnsData);
|
|
} else if (onSort) {
|
|
onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction]));
|
|
}
|
|
}
|
|
},
|
|
[onSort, isSortEnabled, isPlainRecord, setInmemorySortingColumns]
|
|
);
|
|
|
|
const shouldShowFieldHandler = useMemo(() => {
|
|
const dataViewFields = dataView.fields.getAll().map((fld) => fld.name);
|
|
return getShouldShowFieldHandler(dataViewFields, dataView, showMultiFields);
|
|
}, [dataView, showMultiFields]);
|
|
|
|
/**
|
|
* Cell rendering
|
|
*/
|
|
const renderCellValue = useMemo(
|
|
() =>
|
|
getRenderCellValueFn(
|
|
dataView,
|
|
displayedRows,
|
|
useNewFieldsApi,
|
|
shouldShowFieldHandler,
|
|
() => dataGridRef.current?.closeCellPopover(),
|
|
services.fieldFormats,
|
|
maxDocFieldsDisplayed,
|
|
externalCustomRenderers
|
|
),
|
|
[
|
|
dataView,
|
|
displayedRows,
|
|
useNewFieldsApi,
|
|
shouldShowFieldHandler,
|
|
maxDocFieldsDisplayed,
|
|
services.fieldFormats,
|
|
externalCustomRenderers,
|
|
]
|
|
);
|
|
|
|
/**
|
|
* Render variables
|
|
*/
|
|
const randomId = useMemo(() => htmlIdGenerator()(), []);
|
|
const closeFieldEditor = useRef<() => void | undefined>();
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (closeFieldEditor?.current) {
|
|
closeFieldEditor?.current();
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const editField = useMemo(
|
|
() =>
|
|
onFieldEdited
|
|
? (fieldName: string) => {
|
|
closeFieldEditor.current = services.dataViewFieldEditor.openEditor({
|
|
ctx: {
|
|
dataView,
|
|
},
|
|
fieldName,
|
|
onSave: async () => {
|
|
await onFieldEdited();
|
|
},
|
|
});
|
|
}
|
|
: undefined,
|
|
[dataView, onFieldEdited, services.dataViewFieldEditor]
|
|
);
|
|
|
|
const visibleColumns = useMemo(
|
|
() => getVisibleColumns(displayedColumns, dataView, showTimeCol),
|
|
[dataView, displayedColumns, showTimeCol]
|
|
);
|
|
|
|
const getCellValue = useCallback<UseDataGridColumnsCellActionsProps['getCellValue']>(
|
|
(fieldName, rowIndex) =>
|
|
displayedRows[rowIndex % displayedRows.length].flattened[fieldName] as Serializable,
|
|
[displayedRows]
|
|
);
|
|
|
|
const cellActionsFields = useMemo<UseDataGridColumnsCellActionsProps['fields']>(
|
|
() =>
|
|
cellActionsTriggerId && !isPlainRecord
|
|
? visibleColumns.map(
|
|
(columnName) =>
|
|
dataView.getFieldByName(columnName)?.toSpec() ?? {
|
|
name: '',
|
|
type: '',
|
|
aggregatable: false,
|
|
searchable: false,
|
|
}
|
|
)
|
|
: undefined,
|
|
[cellActionsTriggerId, isPlainRecord, visibleColumns, dataView]
|
|
);
|
|
|
|
const columnsCellActions = useDataGridColumnsCellActions({
|
|
fields: cellActionsFields,
|
|
getCellValue,
|
|
triggerId: cellActionsTriggerId,
|
|
dataGridRef,
|
|
});
|
|
|
|
const euiGridColumns = useMemo(
|
|
() =>
|
|
getEuiGridColumns({
|
|
columns: visibleColumns,
|
|
columnsCellActions,
|
|
rowsCount: displayedRows.length,
|
|
settings,
|
|
dataView,
|
|
defaultColumns,
|
|
isSortEnabled,
|
|
isPlainRecord,
|
|
services: {
|
|
uiSettings,
|
|
toastNotifications,
|
|
},
|
|
hasEditDataViewPermission: () => dataViewFieldEditor.userPermissions.editIndexPattern(),
|
|
valueToStringConverter,
|
|
onFilter,
|
|
editField,
|
|
visibleCellActions,
|
|
}),
|
|
[
|
|
onFilter,
|
|
visibleColumns,
|
|
columnsCellActions,
|
|
displayedRows,
|
|
dataView,
|
|
settings,
|
|
defaultColumns,
|
|
isSortEnabled,
|
|
isPlainRecord,
|
|
uiSettings,
|
|
toastNotifications,
|
|
dataViewFieldEditor,
|
|
valueToStringConverter,
|
|
editField,
|
|
visibleCellActions,
|
|
]
|
|
);
|
|
|
|
const hideTimeColumn = useMemo(
|
|
() => services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false),
|
|
[services.uiSettings]
|
|
);
|
|
const schemaDetectors = useMemo(() => getSchemaDetectors(), []);
|
|
const columnsVisibility = useMemo(
|
|
() => ({
|
|
visibleColumns,
|
|
setVisibleColumns: (newColumns: string[]) => {
|
|
onSetColumns(newColumns, hideTimeColumn);
|
|
},
|
|
}),
|
|
[visibleColumns, hideTimeColumn, onSetColumns]
|
|
);
|
|
const sorting = useMemo(() => {
|
|
if (isSortEnabled) {
|
|
return {
|
|
columns: isPlainRecord ? inmemorySortingColumns : sortingColumns,
|
|
onSort: onTableSort,
|
|
};
|
|
}
|
|
return { columns: sortingColumns, onSort: () => {} };
|
|
}, [isSortEnabled, sortingColumns, isPlainRecord, inmemorySortingColumns, onTableSort]);
|
|
|
|
const canSetExpandedDoc = Boolean(setExpandedDoc && !!renderDocumentView);
|
|
|
|
const leadingControlColumns = useMemo(() => {
|
|
const internalControlColumns = getLeadControlColumns(canSetExpandedDoc).filter(({ id }) =>
|
|
controlColumnIds.includes(id)
|
|
);
|
|
return externalControlColumns
|
|
? [...internalControlColumns, ...externalControlColumns]
|
|
: internalControlColumns;
|
|
}, [canSetExpandedDoc, externalControlColumns, controlColumnIds]);
|
|
|
|
const additionalControls = useMemo(
|
|
() => (
|
|
<>
|
|
{usedSelectedDocs.length ? (
|
|
<DataTableDocumentToolbarBtn
|
|
isFilterActive={isFilterActive}
|
|
rows={rows!}
|
|
selectedDocs={usedSelectedDocs}
|
|
setSelectedDocs={setSelectedDocs}
|
|
setIsFilterActive={setIsFilterActive}
|
|
/>
|
|
) : null}
|
|
{externalAdditionalControls}
|
|
</>
|
|
),
|
|
[usedSelectedDocs, isFilterActive, rows, externalAdditionalControls]
|
|
);
|
|
|
|
const showDisplaySelector = useMemo(
|
|
() =>
|
|
!!onUpdateRowHeight
|
|
? {
|
|
allowDensity: false,
|
|
allowRowHeight: true,
|
|
}
|
|
: undefined,
|
|
[onUpdateRowHeight]
|
|
);
|
|
|
|
const inMemory = useMemo(() => {
|
|
return isPlainRecord && columns.length
|
|
? ({ level: 'sorting' } as EuiDataGridInMemory)
|
|
: undefined;
|
|
}, [columns.length, isPlainRecord]);
|
|
|
|
const toolbarVisibility = useMemo(
|
|
() =>
|
|
defaultColumns
|
|
? {
|
|
...toolbarVisibilityDefaults,
|
|
showColumnSelector: false,
|
|
showSortSelector: isSortEnabled,
|
|
additionalControls,
|
|
showDisplaySelector,
|
|
showFullScreenSelector: showFullScreenButton,
|
|
}
|
|
: {
|
|
...toolbarVisibilityDefaults,
|
|
showSortSelector: isSortEnabled,
|
|
additionalControls,
|
|
showDisplaySelector,
|
|
showFullScreenSelector: showFullScreenButton,
|
|
},
|
|
[defaultColumns, isSortEnabled, additionalControls, showDisplaySelector, showFullScreenButton]
|
|
);
|
|
|
|
const rowHeightsOptions = useRowHeightsOptions({
|
|
rowHeightState,
|
|
onUpdateRowHeight,
|
|
storage,
|
|
configRowHeight,
|
|
consumer,
|
|
});
|
|
|
|
const isRenderComplete = loadingState !== DataLoadingState.loading;
|
|
|
|
if (!rowCount && loadingState === DataLoadingState.loading) {
|
|
return (
|
|
<div className="euiDataGrid__loading" data-test-subj="unifiedDataTableLoading">
|
|
<EuiText size="xs" color="subdued">
|
|
<EuiLoadingSpinner />
|
|
<EuiSpacer size="s" />
|
|
<FormattedMessage id="unifiedDataTable.loadingResults" defaultMessage="Loading results" />
|
|
</EuiText>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!rowCount) {
|
|
return (
|
|
<div
|
|
className="euiDataGrid__noResults"
|
|
data-render-complete={isRenderComplete}
|
|
data-shared-item=""
|
|
data-title={searchTitle}
|
|
data-description={searchDescription}
|
|
data-document-number={0}
|
|
>
|
|
<EuiText size="xs" color="subdued">
|
|
<EuiIcon type="discoverApp" size="m" color="subdued" />
|
|
<EuiSpacer size="s" />
|
|
<FormattedMessage
|
|
id="unifiedDataTable.noResultsFound"
|
|
defaultMessage="No results found"
|
|
/>
|
|
</EuiText>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<UnifiedDataTableContext.Provider value={unifiedDataTableContextValue}>
|
|
<span className="unifiedDataTable__inner">
|
|
<div
|
|
data-test-subj="discoverDocTable"
|
|
data-render-complete={isRenderComplete}
|
|
data-shared-item=""
|
|
data-title={searchTitle}
|
|
data-description={searchDescription}
|
|
data-document-number={displayedRows.length}
|
|
className={classnames(className, 'unifiedDataTable__table')}
|
|
>
|
|
<EuiDataGridMemoized
|
|
aria-describedby={randomId}
|
|
aria-labelledby={ariaLabelledBy}
|
|
columns={euiGridColumns}
|
|
columnVisibility={columnsVisibility}
|
|
data-test-subj="docTable"
|
|
leadingControlColumns={leadingControlColumns}
|
|
onColumnResize={onResize}
|
|
pagination={paginationObj}
|
|
renderCellValue={renderCellValue}
|
|
ref={dataGridRef}
|
|
rowCount={rowCount}
|
|
schemaDetectors={schemaDetectors}
|
|
sorting={sorting as EuiDataGridSorting}
|
|
toolbarVisibility={toolbarVisibility}
|
|
rowHeightsOptions={rowHeightsOptions}
|
|
inMemory={inMemory}
|
|
gridStyle={gridStyleOverride ?? GRID_STYLE}
|
|
renderCustomGridBody={renderCustomGridBody}
|
|
trailingControlColumns={trailingControlColumns}
|
|
/>
|
|
</div>
|
|
{loadingState !== DataLoadingState.loading &&
|
|
isPaginationEnabled && ( // we hide the footer for Surrounding Documents page
|
|
<UnifiedDataTableFooter
|
|
isLoadingMore={loadingState === DataLoadingState.loadingMore}
|
|
rowCount={rowCount}
|
|
sampleSize={sampleSize}
|
|
pageCount={pageCount}
|
|
pageIndex={paginationObj?.pageIndex}
|
|
totalHits={totalHits}
|
|
onFetchMoreRecords={onFetchMoreRecords}
|
|
data={data}
|
|
fieldFormats={fieldFormats}
|
|
/>
|
|
)}
|
|
{searchTitle && (
|
|
<EuiScreenReaderOnly>
|
|
<p id={String(randomId)}>
|
|
{searchDescription ? (
|
|
<FormattedMessage
|
|
id="unifiedDataTable.searchGenerationWithDescriptionGrid"
|
|
defaultMessage="Table generated by search {searchTitle} ({searchDescription})"
|
|
values={{ searchTitle, searchDescription }}
|
|
/>
|
|
) : (
|
|
<FormattedMessage
|
|
id="unifiedDataTable.searchGenerationWithDescription"
|
|
defaultMessage="Table generated by search {searchTitle}"
|
|
values={{ searchTitle }}
|
|
/>
|
|
)}
|
|
</p>
|
|
</EuiScreenReaderOnly>
|
|
)}
|
|
{canSetExpandedDoc &&
|
|
expandedDoc &&
|
|
renderDocumentView!(expandedDoc, displayedRows, displayedColumns)}
|
|
</span>
|
|
</UnifiedDataTableContext.Provider>
|
|
);
|
|
};
|