Skip to content

Commit

Permalink
Extracted DiscoverGrid to a package named @kbn/unified-data-table as …
Browse files Browse the repository at this point in the history
…UnifiedDataTable component (#163211)

## Summary

Current PR includes the next set of changes:

1. Moved `DiscoverGrid` component to a package `@kbn/unified-data-table`
and added `@elastic/kibana-data-discovery` as code owners.
2. Changed `@kbn/unified-data-table` package naming for data grid
related components and methods to correspond `UnifiedDataTable` instead
of `Discover`.

3. Moved hooks `useColumns` and `useRowHeightsOptions` to a package as
its logic belongs to `UnifiedDataTable`.
4. Renamed `DiscoverGridContext` to `UnifiedDataTableContext`.
5. Extended `UnifiedDataTable` interface and functionality with the next
customization options:
- `renderDocumentView?: (displayedRows:
DataTableRecord[],displayedColumns: string[]) => JSX.Element |
undefined;` - callback to render DocumentView when the document is
expanded
- `configRowHeight?: number;` - optional value for providing
configuration setting for UnifiedDataTable rows height
- `showMultiFields?: boolean;` - optional value for providing
configuration setting for enabling to display the complex fields in the
table. Default is true.
- `maxDocFieldsDisplayed?: number;` - optional value for providing
configuration setting for maximum number of document fields to display
in the table. Default is 50.
- `externalControlColumns?: EuiDataGridControlColumn[];` - optional
value for providing EuiDataGridControlColumn list of the additional
leading control columns. UnifiedDataTable includes two control columns:
Open Details and Select.
<img width="522" alt="Screenshot 2023-08-22 at 2 26 57 PM"
src="https://github.com/elastic/kibana/assets/55110838/d796b9c8-2fef-4bcc-a3c9-9f5cc6349ab9">

- `externalAdditionalControls?: React.ReactNode;` - 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.
<img width="673" alt="Screenshot 2023-08-22 at 2 40 28 PM"
src="https://github.com/elastic/kibana/assets/55110838/f7ac0c87-5310-49dd-9084-1ce01ca0f366">

- `rowsPerPageOptions?: number[];` - optional list of number type values
to set custom UnifiedDataTable paging options to display the records per
page.
- `renderCustomGridBody?: (args: EuiDataGridCustomBodyProps) =>
React.ReactNode;` - An optional function called to completely customize
and control the rendering of EuiDataGrid's body and cell placement.
<img width="1658" alt="Screenshot 2023-08-22 at 2 50 27 PM"
src="https://github.com/elastic/kibana/assets/55110838/14adc18d-73af-40f5-9859-b3c708e265b1">

- `trailingControlColumns?: EuiDataGridControlColumn[];` - optional list
of the `EuiDataGridControlColumn` type for setting trailing control
columns standard for `EuiDataGrid`.
- `visibleCellActions?: number;` - optional value for a custom number of
the visible cell actions in the table
<img width="497" alt="Screenshot 2023-08-22 at 2 45 49 PM"
src="https://github.com/elastic/kibana/assets/55110838/57ef3ad9-7401-46bb-9b38-cc8cca2e6a24">

- `externalCustomRenderers?: Record<string,(props:
EuiDataGridCellValueElementProps) => React.ReactNode>;` - an optional
settings for a specified fields rendering like links. Applied only for
the listed fields rendering:
<img width="1121" alt="Screenshot 2023-08-22 at 2 51 07 PM"
src="https://github.com/elastic/kibana/assets/55110838/77501eae-3046-4a2c-90e1-2db487c21e2c">

- `consumer` - optional string value for the name of the
`UnifiedDataTable` consumer component or application.
6. Extended `UnifiedDataGrid` services with the two additional: 
    `storage: Storage;`
    `data: DataPublicPluginStart; `
replaced `core: CoreStart;` with `theme: ThemeServiceStart;`, because
`core` is used only to get `theme`
7. Replaced `DocumentView` property with `renderDocumentView?:
(displayedRows: DataTableRecord[],displayedColumns: string[]) =>
JSX.Element | undefined;` callback function, which allows not to use
`DiscoverGridFlyout` definition for the documents rendering.
```
    /**
   * Document detail view component
   */
  DocumentView?: typeof DiscoverGridFlyout;
```
8. Removed the next properties from the data table interface, because it
was used to render DiscoverGridFlyout:
```
   /**
   * Filters applied by saved search embeddable
   */
  filters?: Filter[];
  /**
   * Query applied by KQL bar or text based editor
   */
  query?: Query | AggregateQuery;
  /**
   * Saved search id used for links to single doc and surrounding docs in the flyout
   */
  savedSearchId?: string;
```
9. Added usage examples and interface description to README file.
10. Changed grid styles naming from `.dscDiscoverGrid*` to
`.udtDataTable*`
11. Migrated discover plugin to use `UnifiedDataTable` instead of
`DiscoverGrid`

Extra changes were needed to avoid the circular dependancies:
- moved `DocViewFilterFn` and `FieldMapping` from discover plugin to
`packages/kbn-discover-utils/src/types.ts`
- added own `export type SortOrder = [string, string];` to avoid deps
for saved-search plugin

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
YulNaumenko and kibanamachine authored Sep 2, 2023
1 parent b838cd6 commit 8fb5a65
Show file tree
Hide file tree
Showing 97 changed files with 2,956 additions and 1,225 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,7 @@ test/plugin_functional/plugins/ui_settings_plugin @elastic/kibana-core
packages/kbn-ui-shared-deps-npm @elastic/kibana-operations
packages/kbn-ui-shared-deps-src @elastic/kibana-operations
packages/kbn-ui-theme @elastic/kibana-operations
packages/kbn-unified-data-table @elastic/kibana-data-discovery
packages/kbn-unified-doc-viewer @elastic/kibana-data-discovery
examples/unified_doc_viewer @elastic/kibana-core
src/plugins/unified_doc_viewer @elastic/kibana-data-discovery
Expand Down
3 changes: 2 additions & 1 deletion .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@
"unifiedDocViewer": ["src/plugins/unified_doc_viewer", "packages/kbn-unified-doc-viewer"],
"unifiedSearch": "src/plugins/unified_search",
"unifiedFieldList": "packages/kbn-unified-field-list",
"unifiedHistogram": "src/plugins/unified_histogram"
"unifiedHistogram": "src/plugins/unified_histogram",
"unifiedDataTable": "packages/kbn-unified-data-table"
},
"translations": []
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,7 @@
"@kbn/ui-shared-deps-npm": "link:packages/kbn-ui-shared-deps-npm",
"@kbn/ui-shared-deps-src": "link:packages/kbn-ui-shared-deps-src",
"@kbn/ui-theme": "link:packages/kbn-ui-theme",
"@kbn/unified-data-table": "link:packages/kbn-unified-data-table",
"@kbn/unified-doc-viewer": "link:packages/kbn-unified-doc-viewer",
"@kbn/unified-doc-viewer-examples": "link:examples/unified_doc_viewer",
"@kbn/unified-doc-viewer-plugin": "link:src/plugins/unified_doc_viewer",
Expand Down
241 changes: 241 additions & 0 deletions packages/kbn-unified-data-table/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
# @kbn/unified-data-table

This package contains components and services for the unified data table UI (as used in Discover).

## UnifiedDataTable Component
Props description:
| Property | Type | Description |
| :--- | :--- | :--- |
| **ariaLabelledBy** | string | Determines which element labels the grid for ARIA. |
| **className** | (optional) string | Optional class name to apply. |
| **columns** | string[] | Determines ids of the columns which are displayed. |
| **expandedDoc** | (optional) DataTableRecord | If set, the given document is displayed in a flyout. |
| **dataView** | DataView | The used data view. |
| **loadingState** | DataLoadingState | Determines if data is currently loaded. |
| **onFilter** | DocViewFilterFn | Function to add a filter in the grid cell or document flyout. |
| **onResize** | (optional)(colSettings: { columnId: string; width: number }) => void; | Function triggered when a column is resized by the user. |
| **onSetColumns** | (columns: string[], hideTimeColumn: boolean) => void; | Function to set all columns. |
| **onSort** | (optional)(sort: string[][]) => void; | Function to change sorting of the documents, skipped when isSortEnabled is set to false. |
| **rows** | (optional)DataTableRecord[] | Array of documents provided by Elasticsearch. |
| **sampleSize** | number | The max size of the documents returned by Elasticsearch. |
| **setExpandedDoc** | (optional)(doc?: DataTableRecord) => void; | Function to set the expanded document, which is displayed in a flyout. |
| **settings** | (optional)UnifiedDataTableSettings | Grid display settings persisted in Elasticsearch (e.g. column width). |
| **searchDescription** | (optional)string | Search description. |
| **searchTitle** | (optional)string | Search title. |
| **showTimeCol** | boolean | Determines whether the time columns should be displayed (legacy settings). |
| **showFullScreenButton** | (optional)boolean | Determines whether the full screen button should be displayed. |
| **isSortEnabled** | (optional)boolean | Manage user sorting control. |
| **sort** | SortOrder[] | Current sort setting. |
| **useNewFieldsApi** | boolean | How the data is fetched. |
| **isPaginationEnabled** | (optional)boolean | Manage pagination control. |
| **controlColumnIds** | (optional)string[] | List of used control columns (available: 'openDetails', 'select'). |
| **rowHeightState** | (optional)number | Row height from state. |
| **onUpdateRowHeight** | (optional)(rowHeight: number) => void; | Update row height state. |
| **isPlainRecord** | (optional)boolean | Is text base lang mode enabled. |
| **rowsPerPageState** | (optional)number | Current state value for rowsPerPage. |
| **onUpdateRowsPerPage** | (optional)(rowsPerPage: number) => void; | Update rows per page state. |
| **onFieldEdited** | (optional)() => void; | Callback to execute on edit runtime field. |
| **cellActionsTriggerId** | (optional)string | Optional triggerId to retrieve the column cell actions that will override the default ones. |
| **services** | See Required **services** list below | Service dependencies. |
| **renderDocumentView** | (optional)(hit: DataTableRecord,displayedRows: DataTableRecord[],displayedColumns: string[]) => JSX.Element | undefined; | Callback to render DocumentView when the document is expanded. |
| **configRowHeight** | (optional)number | Optional value for providing configuration setting for UnifiedDataTable rows height. |
| **showMultiFields** | (optional)boolean | Optional value for providing configuration setting for enabling to display the complex fields in the table. Default is true. |
| **maxDocFieldsDisplayed** | (optional)number | Optional value for providing configuration setting for maximum number of document fields to display in the table. Default is 50. |
| **externalControlColumns** | (optional)EuiDataGridControlColumn[] | Optional value for providing EuiDataGridControlColumn list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select. |
| **totalHits** | (optional)number | Number total hits from ES. |
| **onFetchMoreRecords** | (optional)() => void | To fetch more. |
| **externalAdditionalControls** | (optional)React.ReactNode | 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. |
| **rowsPerPageOptions** | (optional)number[] | Optional list of number type values to set custom UnifiedDataTable paging options to display the records per page. |
| **renderCustomGridBody** | (optional)(args: EuiDataGridCustomBodyProps) => React.ReactNode; | An optional function called to completely customize and control the rendering of EuiDataGrid's body and cell placement. |
| **trailingControlColumns** | (optional)EuiDataGridControlColumn[] | An optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid. |
| **visibleCellActions** | (optional)number | An optional value for a custom number of the visible cell actions in the table. By default is up to 3. |
| **externalCustomRenderers** | (optional)Record<string,(props: EuiDataGridCellValueElementProps) => React.ReactNode>; | An optional settings for a specified fields rendering like links. Applied only for the listed fields rendering. |
| **consumer** | (optional)string | Name of the UnifiedDataTable consumer component or application. |
| **componentsTourSteps** | (optional)Record<string,string> | Optional key/value pairs to set guided onboarding steps ids for a data table components included to guided tour. |

*Required **services** list:
```
theme: ThemeServiceStart;
fieldFormats: FieldFormatsStart;
uiSettings: IUiSettingsClient;
dataViewFieldEditor: DataViewFieldEditorStart;
toastNotifications: ToastsStart;
storage: Storage;
data: DataPublicPluginStart;
```

Usage example:

```
// Memoize unified data table to avoid the unnecessary re-renderings
const DataTableMemoized = React.memo(UnifiedDataTable);
// Add memoized component with all needed props
<DataTableMemoized
ariaLabelledBy="timelineDocumentsAriaLabel"
className={'unifiedDataTableTimeline'}
columns={['event.category', 'event.action', 'host.name', 'user.name']}
expandedDoc={expandedDoc as DataTableRecord}
dataView={dataView}
loadingState={isQueryLoading ? DataLoadingState.loading : DataLoadingState.loaded}
onFilter={() => {
// Add logic to refetch the data when the filter by field was added/removed. Refetch data.
}}
onResize={(colSettings: { columnId: string; width: number }) => {
// Update the table state with the new width for the column
}}
onSetColumns={(columns: string[], hideTimeColumn: boolean) => {
// Update table state with the new columns. Refetch data.
}}
onSort={!isTextBasedQuery ? onSort : undefined
// Update table state with the new sorting settings. Refetch data.
}
rows={searchResultRows}
sampleSize={500}
setExpandedDoc={() => {
// Callback function to do the logic when the document is expanded
}}
settings={tableSettings}
showTimeCol={true}
isSortEnabled={true}
sort={sortingColumns}
rowHeightState={3}
onUpdateRowHeight={(rowHeight: number) => {
// Do the state update with the new setting of the row height
}}
isPlainRecord={isTextBasedQuery}
rowsPerPageState={50}
onUpdateRowsPerPage={(rowHeight: number) => {
// Do the state update with the new number of the rows per page
}
onFieldEdited={() =>
// Callback to execute on edit runtime field. Refetch data.
}
cellActionsTriggerId={SecurityCellActionsTrigger.DEFAULT}
services={{
theme,
fieldFormats,
storage,
toastNotifications: toastsService,
uiSettings,
dataViewFieldEditor,
data: dataPluginContract,
}}
visibleCellActions={3}
externalCustomRenderers={{
// Set the record style definition for the specific fields rendering Record<string,(props: EuiDataGridCellValueElementProps) => React.ReactNode>
}}
renderDocumentView={() =>
// Implement similar callback to render the Document flyout
const renderDetailsPanel = useCallback(
() => (
<DetailsPanel
browserFields={browserFields}
handleOnPanelClosed={handleOnPanelClosed}
runtimeMappings={runtimeMappings}
tabType={TimelineTabs.query}
scopeId={timelineId}
isFlyoutView
/>
),
[browserFields, handleOnPanelClosed, runtimeMappings, timelineId]
);
}
externalControlColumns={leadingControlColumns}
externalAdditionalControls={additionalControls}
trailingControlColumns={trailingControlColumns}
renderCustomGridBody={renderCustomGridBody}
rowsPerPageOptions={[10, 30, 40, 100]}
showFullScreenButton={false}
useNewFieldsApi={true}
maxDocFieldsDisplayed={50}
consumer="timeline"
totalHits={
// total number of the documents in the search query result. For example: 1200
}
onFetchMoreRecords={() => {
// Do some data fetch to get more data
}}
configRowHeight={3}
showMultiFields={true}
componentsTourSteps={'expandButton': DISCOVER_TOUR_STEP_ANCHOR_IDS.expandDocument}
/>
```

## JsonCodeEditorCommon Component
Props description:
| Property | Type | Description |
| :--- | :--- | :--- |
| **width** | (optional) string or number | Editor component width. |
| **height** | (optional) string or number | Editor component height. |
| **hasLineNumbers** | (optional) boolean | Define if the editor component has line numbers style. |
| **hideCopyButton** | (optional) boolean | Show/hide setting for Copy button. |
| **onEditorDidMount** | (editor: monaco.editor.IStandaloneCodeEditor) => void | Do some logic to update the state with the edotor component value. |

Usage example:

```
<JsonCodeEditorCommon
jsonValue={jsonValue}
width={100}
height={400}
hasLineNumbers={true}
onEditorDidMount={(editorNode: monaco.editor.IStandaloneCodeEditor) => setEditor(editorNode)}
/>
```

## Utils

* `getRowsPerPageOptions(currentRowsPerPage)` - gets list of the table defaults for perPage options.

* `getDisplayedColumns(currentRowsPerPage)` - gets list of the table columns with the logic to define the empty list with _source column.

* `popularizeField(...)` - helper function to define the dataView persistance and save indexPattern update capabilities.

## Hooks

* `useColumns(...)` - this hook define the state update for the columns event handlers and allows to use them for external components outside the UnifiedDataTable.

An example of using hooks for defining event handlers for columns management with setting the consumer specific setAppState:

```
const {
columns: currentColumns,
onAddColumn,
onRemoveColumn,
onMoveColumn,
onSetColumns,
} = useColumns({
capabilities,
defaultOrder: uiSettings.get(SORT_DEFAULT_ORDER_SETTING),
dataView,
dataViews,
setAppState: stateContainer.appState.update,
useNewFieldsApi,
columns,
sort,
});
// Use onAddColumn, onRemoveColumn handlers in the DocumentView
const renderDocumentView = useCallback(
(hit: DataTableRecord, displayedRows: DataTableRecord[], displayedColumns: string[]) => (
<DiscoverGridFlyout
dataView={dataView}
hit={hit}
hits={displayedRows}
// if default columns are used, dont make them part of the URL - the context state handling will take care to restore them
columns={displayedColumns}
savedSearchId={savedSearch.id}
onFilter={onAddFilter}
onRemoveColumn={onRemoveColumn}
onAddColumn={onAddColumn}
onClose={() => setExpandedDoc(undefined)}
setExpandedDoc={setExpandedDoc}
query={query}
/>
),
[dataView, onAddColumn, onAddFilter, onRemoveColumn, query, savedSearch.id, setExpandedDoc]
);
```
23 changes: 23 additions & 0 deletions packages/kbn-unified-data-table/__mocks__/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { Config } from '@kbn/config';

export type ConfigMock = jest.Mocked<Config>;

const createConfigMock = (): ConfigMock => ({
has: jest.fn(),
get: jest.fn(),
set: jest.fn(),
getFlattenedPaths: jest.fn(),
toRaw: jest.fn(),
});

export const configMock = {
create: createConfigMock,
};
Loading

0 comments on commit 8fb5a65

Please sign in to comment.