Skip to content

Commit

Permalink
[8.x] [Discover] In-table search (#206454) (#208868)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.x`:
- [[Discover] In-table search
(#206454)](#206454)

<!--- Backport version: 9.6.4 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Julia
Rechkunova","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-01-29T23:52:07Z","message":"[Discover]
In-table search (#206454)\n\n- Closes
https://github.com/elastic/kibana/issues/192360\r\n\r\n##
Summary\r\n\r\nThe default browser Find-in-page does not work great with
the grid\r\nvirtualization and our pagination and it can only find
matches in rows\r\nwhich are currently displayed.\r\n\r\nThis PR adds
in-table search support to the grid so users can find\r\nmatches in all
grid rows (up to `500` sample docs/rows by default) and\r\njump between
them with \"Previous\"/\"Next\"
buttons.\r\n\r\n![Jan-24-2025\r\n22-03-54](https://github.com/user-attachments/assets/95b31fb8-4740-4c5f-ba91-8e1c19066e02)\r\n\r\nThe
implementation is extracted in a new
package\r\n`@kbn/data-grid-in-table-search`. This would allow to use
in-table\r\nsearch with `EuiDataGrid` on other pages of Kibana
too.\r\n\r\n`Cmd+F` shortcut is overridden when one of grid elements is
in focus\r\notherwise we keep the browser default behaviour.\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
florent-leborgne
<[email protected]>","sha":"8ffb2ff62830655587d2b91e295b5d76fc86806e","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["v9.0.0","release_note:feature","Team:DataDiscovery","backport:prev-minor","Project:OneDiscover"],"title":"[Discover]
In-table
search","number":206454,"url":"https://github.com/elastic/kibana/pull/206454","mergeCommit":{"message":"[Discover]
In-table search (#206454)\n\n- Closes
https://github.com/elastic/kibana/issues/192360\r\n\r\n##
Summary\r\n\r\nThe default browser Find-in-page does not work great with
the grid\r\nvirtualization and our pagination and it can only find
matches in rows\r\nwhich are currently displayed.\r\n\r\nThis PR adds
in-table search support to the grid so users can find\r\nmatches in all
grid rows (up to `500` sample docs/rows by default) and\r\njump between
them with \"Previous\"/\"Next\"
buttons.\r\n\r\n![Jan-24-2025\r\n22-03-54](https://github.com/user-attachments/assets/95b31fb8-4740-4c5f-ba91-8e1c19066e02)\r\n\r\nThe
implementation is extracted in a new
package\r\n`@kbn/data-grid-in-table-search`. This would allow to use
in-table\r\nsearch with `EuiDataGrid` on other pages of Kibana
too.\r\n\r\n`Cmd+F` shortcut is overridden when one of grid elements is
in focus\r\notherwise we keep the browser default behaviour.\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
florent-leborgne
<[email protected]>","sha":"8ffb2ff62830655587d2b91e295b5d76fc86806e"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/206454","number":206454,"mergeCommit":{"message":"[Discover]
In-table search (#206454)\n\n- Closes
https://github.com/elastic/kibana/issues/192360\r\n\r\n##
Summary\r\n\r\nThe default browser Find-in-page does not work great with
the grid\r\nvirtualization and our pagination and it can only find
matches in rows\r\nwhich are currently displayed.\r\n\r\nThis PR adds
in-table search support to the grid so users can find\r\nmatches in all
grid rows (up to `500` sample docs/rows by default) and\r\njump between
them with \"Previous\"/\"Next\"
buttons.\r\n\r\n![Jan-24-2025\r\n22-03-54](https://github.com/user-attachments/assets/95b31fb8-4740-4c5f-ba91-8e1c19066e02)\r\n\r\nThe
implementation is extracted in a new
package\r\n`@kbn/data-grid-in-table-search`. This would allow to use
in-table\r\nsearch with `EuiDataGrid` on other pages of Kibana
too.\r\n\r\n`Cmd+F` shortcut is overridden when one of grid elements is
in focus\r\notherwise we keep the browser default behaviour.\r\n\r\n###
Checklist\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\r\n-
[
]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas
added for features that require explanation or tutorials\r\n- [x] [Unit
or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] If a plugin
configuration key changed, check if it needs to be\r\nallowlisted in the
cloud and added to the
[docker\r\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\r\n-
[ ] This was checked for breaking HTTP API changes, and any
breaking\r\nchanges have been approved by the breaking-change committee.
The\r\n`release_note:breaking` label should be applied in these
situations.\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] The PR description includes
the appropriate Release Notes section,\r\nand the correct
`release_note:*` label is applied per
the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<[email protected]>\r\nCo-authored-by:
florent-leborgne
<[email protected]>","sha":"8ffb2ff62830655587d2b91e295b5d76fc86806e"}}]}]
BACKPORT-->

---------

Co-authored-by: Julia Rechkunova <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
3 people authored Jan 30, 2025
1 parent ad15e7d commit c40c6d6
Show file tree
Hide file tree
Showing 48 changed files with 2,933 additions and 25 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ packages/kbn-cypress-config @elastic/kibana-operations
x-pack/platform/plugins/shared/dashboard_enhanced @elastic/kibana-presentation
src/platform/plugins/shared/dashboard @elastic/kibana-presentation
x-pack/platform/packages/shared/kbn-data-forge @elastic/obs-ux-management-team
src/platform/packages/shared/kbn-data-grid-in-table-search @elastic/kibana-data-discovery
src/platform/plugins/shared/data @elastic/kibana-visualizations @elastic/kibana-data-discovery
x-pack/platform/plugins/shared/data_quality @elastic/obs-ux-logs-team
test/plugin_functional/plugins/data_search @elastic/kibana-data-discovery
Expand Down
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"unifiedFieldList": "src/platform/packages/shared/kbn-unified-field-list",
"unifiedHistogram": "src/platform/plugins/shared/unified_histogram",
"unifiedDataTable": "src/platform/packages/shared/kbn-unified-data-table",
"dataGridInTableSearch": "src/platform/packages/shared/kbn-data-grid-in-table-search",
"unsavedChangesBadge": "src/platform/packages/private/kbn-unsaved-changes-badge",
"unsavedChangesPrompt": "src/platform/packages/shared/kbn-unsaved-changes-prompt",
"managedContentBadge": "src/platform/packages/private/kbn-managed-content-badge",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@
"@kbn/dashboard-enhanced-plugin": "link:x-pack/platform/plugins/shared/dashboard_enhanced",
"@kbn/dashboard-plugin": "link:src/platform/plugins/shared/dashboard",
"@kbn/data-forge": "link:x-pack/platform/packages/shared/kbn-data-forge",
"@kbn/data-grid-in-table-search": "link:src/platform/packages/shared/kbn-data-grid-in-table-search",
"@kbn/data-plugin": "link:src/platform/plugins/shared/data",
"@kbn/data-quality-plugin": "link:x-pack/platform/plugins/shared/data_quality",
"@kbn/data-search-plugin": "link:test/plugin_functional/plugins/data_search",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# @kbn/data-grid-in-table-search

This package allows to extend `EuiDataGrid` with in-table search.

If you are already using `UnifiedDataTable` component, you can enable in-table search simply by passing `enableInTableSearch` prop to it.

```tsx
<UnifiedDataTable
enableInTableSearch={true}
// ... other props
/>
```

If you are using `EuiDataGrid` directly, you can enable in-table search by importing this package and following these steps:

1. include `useDataGridInTableSearch` hook in your component
2. pass `inTableSearchControl` to `EuiDataGrid` inside `additionalControls.right` prop or `renderCustomToolbar`
3. pass `inTableSearchCss` to the grid container element as `css` prop
4. update your `cellContext` prop with `cellContextWithInTableSearchSupport`
5. update your `renderCellValue` prop with `renderCellValueWithInTableSearchSupport`.

```tsx
import { useDataGridInTableSearch } from '@kbn/data-grid-in-table-search';

// ...

const dataGridRef = useRef<EuiDataGridRefProps>(null);
const [dataGridWrapper, setDataGridWrapper] = useState<HTMLElement | null>(null);

// ...

const { inTableSearchTermCss, inTableSearchControl, cellContextWithInTableSearchSupport, renderCellValueWithInTableSearchSupport } =
useDataGridInTableSearch({
dataGridWrapper,
dataGridRef,
visibleColumns,
rows,
cellContext,
renderCellValue,
pagination,
});

const toolbarVisibility: EuiDataGridProps['toolbarVisibility'] = useMemo(
() => ({
additionalControls: inTableSearchControl ? { right: inTableSearchControl } : false,
// ...
}),
[inTableSearchControl]
);

// ...
<div ref={(node) => setDataGridWrapper(node)} css={inTableSearchCss}>
<EuiDataGrid
ref={dataGridRef}
toolbarVisibility={toolbarVisibility}
renderCellValue={renderCellValueWithInTableSearchSupport}
cellContext={cellContextWithInTableSearchSupport}
pagination={pagination}
// ... other props
/>
</div>
```

An example of how to use this package can be found in `kbn-data-grid-in-table-search/__mocks__/data_grid_example.tsx`
or in `kbn-unified-data-table` package.






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", 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".
*/

export {
useDataGridInTableSearch,
type UseDataGridInTableSearchProps,
type UseDataGridInTableSearchReturn,
} from './src';

export {
BUTTON_TEST_SUBJ,
COUNTER_TEST_SUBJ,
BUTTON_PREV_TEST_SUBJ,
BUTTON_NEXT_TEST_SUBJ,
INPUT_TEST_SUBJ,
HIGHLIGHT_CLASS_NAME,
} from './src/constants';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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".
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../../../..',
roots: ['<rootDir>/src/platform/packages/shared/kbn-data-grid-in-table-search'],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "shared-browser",
"id": "@kbn/data-grid-in-table-search",
"owner": "@elastic/kibana-data-discovery",
"group": "platform",
"visibility": "shared"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@kbn/data-grid-in-table-search",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0",
"sideEffects": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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".
*/

export const generateMockData = (rowsCount: number, columnsCount: number) => {
const testData: string[][] = [];

Array.from({ length: rowsCount }).forEach((_, i) => {
const row: string[] = [];
Array.from({ length: columnsCount }).forEach((__, j) => {
row.push(`cell-in-row-${i}-col-${j}`);
});
testData.push(row);
});

return testData;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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, { useMemo, useRef, useState } from 'react';
import { EuiDataGrid, EuiDataGridProps, EuiDataGridRefProps } from '@elastic/eui';
import { generateMockData } from './data';
import { getRenderCellValueMock } from './render_cell_value_mock';
import { useDataGridInTableSearch } from '../use_data_grid_in_table_search';

export interface DataGridWithInTableSearchExampleProps {
rowsCount: number;
columnsCount: number;
pageSize: number | null;
}

export const DataGridWithInTableSearchExample: React.FC<DataGridWithInTableSearchExampleProps> = ({
rowsCount,
columnsCount,
pageSize,
}) => {
const dataGridRef = useRef<EuiDataGridRefProps>(null);
const [dataGridWrapper, setDataGridWrapper] = useState<HTMLElement | null>(null);

const sampleData = useMemo(
() => generateMockData(rowsCount, columnsCount),
[rowsCount, columnsCount]
);
const columns = useMemo(
() => Array.from({ length: columnsCount }, (_, i) => ({ id: `column-${i}` })),
[columnsCount]
);

const [visibleColumns, setVisibleColumns] = useState(columns.map(({ id }) => id));

const renderCellValue = useMemo(() => getRenderCellValueMock(sampleData), [sampleData]);

const isPaginationEnabled = typeof pageSize === 'number';
const [pageIndex, setPageIndex] = useState(0);
const pagination = useMemo(() => {
return isPaginationEnabled
? {
onChangePage: setPageIndex,
onChangeItemsPerPage: () => {},
pageIndex,
pageSize,
}
: undefined;
}, [isPaginationEnabled, setPageIndex, pageSize, pageIndex]);

const {
inTableSearchTermCss,
inTableSearchControl,
cellContextWithInTableSearchSupport,
renderCellValueWithInTableSearchSupport,
} = useDataGridInTableSearch({
dataGridWrapper,
dataGridRef,
visibleColumns,
rows: sampleData,
cellContext: undefined,
renderCellValue,
pagination,
});

const toolbarVisibility: EuiDataGridProps['toolbarVisibility'] = useMemo(
() => ({
additionalControls: inTableSearchControl ? { right: inTableSearchControl } : false,
}),
[inTableSearchControl]
);

return (
<div ref={(node) => setDataGridWrapper(node)} css={inTableSearchTermCss}>
<EuiDataGrid
ref={dataGridRef}
aria-label="Data grid with in-table search"
columns={columns}
columnVisibility={{ visibleColumns, setVisibleColumns }}
toolbarVisibility={toolbarVisibility}
rowCount={rowsCount}
pagination={pagination}
cellContext={cellContextWithInTableSearchSupport}
renderCellValue={renderCellValueWithInTableSearchSupport}
width={800}
height={200}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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".
*/

export { generateMockData } from './data';
export { getRenderCellValueMock } from './render_cell_value_mock';
export { DataGridWithInTableSearchExample } from './data_grid_example';
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { EuiDataGridCellValueElementProps } from '@elastic/eui';

export function getRenderCellValueMock(testData: string[][]) {
return function OriginalRenderCellValue({
colIndex,
rowIndex,
}: EuiDataGridCellValueElementProps) {
const cellValue = testData[rowIndex][colIndex];

if (!cellValue) {
throw new Error('Testing unexpected errors');
}

return <div>{cellValue}</div>;
};
}
Loading

0 comments on commit c40c6d6

Please sign in to comment.