From 2874148e30f3161bb625c25663b6451b7a307120 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Wed, 26 Jan 2022 16:18:49 -0800 Subject: [PATCH 01/10] Enable sorting + targeting row indices outside of the current page - to test handling the exposed APIs when dealing with sorted/paginated data --- src-docs/src/views/datagrid/ref.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src-docs/src/views/datagrid/ref.js b/src-docs/src/views/datagrid/ref.js index 2ce537c6884..ffe772f91f2 100644 --- a/src-docs/src/views/datagrid/ref.js +++ b/src-docs/src/views/datagrid/ref.js @@ -107,6 +107,12 @@ export default () => { [] ); + // Sorting + const [sortingColumns, setSortingColumns] = useState([]); + const onSort = useCallback((sortingColumns) => { + setSortingColumns(sortingColumns); + }, []); + // Manual cell focus const [rowIndexAction, setRowIndexAction] = useState(0); const [colIndexAction, setColIndexAction] = useState(0); @@ -118,7 +124,7 @@ export default () => { setRowIndexAction(Number(e.target.value))} compressed @@ -129,7 +135,7 @@ export default () => { setColIndexAction(Number(e.target.value))} compressed @@ -177,6 +183,8 @@ export default () => { aria-label="Data grid demo" columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} + sorting={{ columns: sortingColumns, onSort }} + inMemory={{ level: 'sorting' }} rowCount={raw_data.length} renderCellValue={({ rowIndex, columnId }) => raw_data[rowIndex][columnId] From eaeaba2fed05df4f0eec2b9dfb9b0ff30ad7fca0 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Wed, 26 Jan 2022 16:22:33 -0800 Subject: [PATCH 02/10] Switch data grid example to Typescript - to test type issues during consumer usage + @ts-ignore faker complaints --- .../views/datagrid/datagrid_ref_example.js | 2 +- .../src/views/datagrid/{ref.js => ref.tsx} | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) rename src-docs/src/views/datagrid/{ref.js => ref.tsx} (83%) diff --git a/src-docs/src/views/datagrid/datagrid_ref_example.js b/src-docs/src/views/datagrid/datagrid_ref_example.js index ee550b19479..49e9ee6fd2b 100644 --- a/src-docs/src/views/datagrid/datagrid_ref_example.js +++ b/src-docs/src/views/datagrid/datagrid_ref_example.js @@ -28,7 +28,7 @@ export const DataGridRefExample = { { source: [ { - type: GuideSectionTypes.JS, + type: GuideSectionTypes.TSX, code: dataGridRefSource, }, ], diff --git a/src-docs/src/views/datagrid/ref.js b/src-docs/src/views/datagrid/ref.tsx similarity index 83% rename from src-docs/src/views/datagrid/ref.js rename to src-docs/src/views/datagrid/ref.tsx index ffe772f91f2..267892df41e 100644 --- a/src-docs/src/views/datagrid/ref.js +++ b/src-docs/src/views/datagrid/ref.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useMemo, useState, useRef } from 'react'; +// @ts-ignore - faker does not have type declarations import { fake } from 'faker'; import { @@ -9,15 +10,16 @@ import { EuiFieldNumber, EuiButton, EuiDataGrid, + EuiDataGridRefProps, EuiModal, EuiModalBody, EuiModalFooter, EuiModalHeader, EuiModalHeaderTitle, EuiText, -} from '../../../../src/components/'; +} from '../../../../src/components'; -const raw_data = []; +const raw_data: Array<{ [key: string]: string }> = []; for (let i = 1; i < 100; i++) { raw_data.push({ name: fake('{{name.lastName}}, {{name.firstName}}'), @@ -29,20 +31,23 @@ for (let i = 1; i < 100; i++) { } export default () => { - const dataGridRef = useRef(); + const dataGridRef = useRef(null); // Modal const [isModalVisible, setIsModalVisible] = useState(false); - const [lastFocusedCell, setLastFocusedCell] = useState({}); + const [lastFocusedCell, setLastFocusedCell] = useState({ + rowIndex: 0, + colIndex: 0, + }); const closeModal = useCallback(() => { setIsModalVisible(false); - dataGridRef.current.setFocusedCell(lastFocusedCell); // Set the data grid focus back to the cell that opened the modal + dataGridRef.current!.setFocusedCell(lastFocusedCell); // Set the data grid focus back to the cell that opened the modal }, [lastFocusedCell]); const showModal = useCallback(({ rowIndex, colIndex }) => { setIsModalVisible(true); - dataGridRef.current.closeCellPopover(); // Close any open cell popovers + dataGridRef.current!.closeCellPopover(); // Close any open cell popovers setLastFocusedCell({ rowIndex, colIndex }); // Store the cell that opened this modal }, []); @@ -101,11 +106,12 @@ export default () => { // Pagination const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 25 }); - const onChangePage = useCallback( - (pageIndex) => - setPagination((pagination) => ({ ...pagination, pageIndex })), - [] - ); + const onChangePage = useCallback((pageIndex) => { + setPagination((pagination) => ({ ...pagination, pageIndex })); + }, []); + const onChangePageSize = useCallback((pageSize) => { + setPagination((pagination) => ({ ...pagination, pageSize })); + }, []); // Sorting const [sortingColumns, setSortingColumns] = useState([]); @@ -146,7 +152,7 @@ export default () => { - dataGridRef.current.setFocusedCell({ + dataGridRef.current!.setFocusedCell({ rowIndex: rowIndexAction, colIndex: colIndexAction, }) @@ -159,7 +165,7 @@ export default () => { - dataGridRef.current.openCellPopover({ + dataGridRef.current!.openCellPopover({ rowIndex: rowIndexAction, colIndex: colIndexAction, }) @@ -171,7 +177,7 @@ export default () => { dataGridRef.current.setIsFullScreen(true)} + onClick={() => dataGridRef.current!.setIsFullScreen(true)} > Set grid to full screen @@ -191,8 +197,9 @@ export default () => { } pagination={{ ...pagination, - pageSizeOptions: [25], + pageSizeOptions: [25, 50], onChangePage: onChangePage, + onChangeItemsPerPage: onChangePageSize, }} height={400} ref={dataGridRef} From 7786fccc57949f3a062bf5dfbe9e1d19e6f71886 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 10:25:58 -0800 Subject: [PATCH 03/10] Fix cell expansion buttons on paginated pages not working correctly --- src/components/datagrid/body/data_grid_cell.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/datagrid/body/data_grid_cell.tsx b/src/components/datagrid/body/data_grid_cell.tsx index 7413dfc58e8..b6041330b0f 100644 --- a/src/components/datagrid/body/data_grid_cell.tsx +++ b/src/components/datagrid/body/data_grid_cell.tsx @@ -490,7 +490,7 @@ export class EuiDataGridCell extends Component< rowManager, ...rest } = this.props; - const { rowIndex, colIndex } = rest; + const { rowIndex, visibleRowIndex, colIndex } = rest; const popoverIsOpen = this.isPopoverOpen(); const hasCellButtons = isExpandable || column?.cellActions; @@ -534,7 +534,7 @@ export class EuiDataGridCell extends Component< case keys.ENTER: case keys.F2: event.preventDefault(); - openCellPopover({ rowIndex, colIndex }); + openCellPopover({ rowIndex: visibleRowIndex, colIndex }); break; } } else { @@ -641,7 +641,7 @@ export class EuiDataGridCell extends Component< if (popoverIsOpen) { closeCellPopover(); } else { - openCellPopover({ rowIndex, colIndex }); + openCellPopover({ rowIndex: visibleRowIndex, colIndex }); } }} /> From 4a80624fbd10d4533917e5a92ad1ec926b0c10d6 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 10:33:06 -0800 Subject: [PATCH 04/10] Attempt to more clearly document `rowIndex`s that are actually `visibleRowIndex`s --- src/components/datagrid/data_grid_types.ts | 8 ++++--- src/components/datagrid/utils/scrolling.ts | 3 +++ src/components/datagrid/utils/sorting.ts | 26 ++++++++++++---------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/components/datagrid/data_grid_types.ts b/src/components/datagrid/data_grid_types.ts index d3d8abdbb1e..51038aa09e1 100644 --- a/src/components/datagrid/data_grid_types.ts +++ b/src/components/datagrid/data_grid_types.ts @@ -180,9 +180,12 @@ export interface EuiDataGridVisibleRows { export interface DataGridSortingContextShape { sorting?: EuiDataGridSorting; sortedRowMap: { [key: number]: number }; - getCorrectRowIndex: (rowIndex: number) => number; + getCorrectRowIndex: (visibleRowIndex: number) => number; } +// An array of [x,y] coordinates. Note that the `y` value expected internally is a `visibleRowIndex` +export type EuiDataGridFocusedCell = [number, number]; + export interface DataGridFocusContextShape { focusedCell?: EuiDataGridFocusedCell; setFocusedCell: (cell: EuiDataGridFocusedCell) => void; @@ -196,6 +199,7 @@ export interface DataGridFocusContextShape { export interface DataGridCellPopoverContextShape { popoverIsOpen: boolean; + // Note that the rowIndex used to locate cells internally is a `visibleRowIndex` cellLocation: { rowIndex: number; colIndex: number }; openCellPopover(args: { rowIndex: number; colIndex: number }): void; closeCellPopover(): void; @@ -764,8 +768,6 @@ export interface EuiDataGridInMemory { skipColumns?: string[]; } -export type EuiDataGridFocusedCell = [number, number]; - export interface EuiDataGridInMemoryValues { [rowIndex: string]: { [columnId: string]: string }; } diff --git a/src/components/datagrid/utils/scrolling.ts b/src/components/datagrid/utils/scrolling.ts index 4aaf3df4ee3..486cecfc983 100644 --- a/src/components/datagrid/utils/scrolling.ts +++ b/src/components/datagrid/utils/scrolling.ts @@ -74,6 +74,9 @@ export const useScrollCellIntoView = ({ hasStickyFooter, }: Dependencies) => { const scrollCellIntoView = useCallback( + // Note: in order for this UX to work correctly with react-window's APIs, + // the `rowIndex` arg expected is actually our internal `visibleRowIndex`, + // not the `rowIndex` from the raw unsorted/unpaginated user data async ({ rowIndex, colIndex }: ScrollCellIntoView) => { if (!gridRef.current || !outerGridRef.current || !innerGridRef.current) { return; // Grid isn't rendered yet or is empty diff --git a/src/components/datagrid/utils/sorting.ts b/src/components/datagrid/utils/sorting.ts index 9a4f17e6eed..1f39869c735 100644 --- a/src/components/datagrid/utils/sorting.ts +++ b/src/components/datagrid/utils/sorting.ts @@ -103,19 +103,21 @@ export const useSorting = ({ schemaDetectors, ]); + // Given a visible row index, obtain the unpaginated & unsorted + // row index from the passed cell data const getCorrectRowIndex = useCallback( - (rowIndex: number) => { - let rowIndexWithOffset = rowIndex; - - if (rowIndex - startRow < 0) { - rowIndexWithOffset = rowIndex + startRow; - } - - const correctRowIndex = sortedRowMap.hasOwnProperty(rowIndexWithOffset) - ? sortedRowMap[rowIndexWithOffset] - : rowIndexWithOffset; - - return correctRowIndex; + (visibleRowIndex: number) => { + const isPaginated = visibleRowIndex - startRow < 0; + const unpaginatedRowIndex = isPaginated + ? visibleRowIndex + startRow + : visibleRowIndex; + + const unsortedRowIndex = + unpaginatedRowIndex in sortedRowMap + ? sortedRowMap[unpaginatedRowIndex] + : unpaginatedRowIndex; + + return unsortedRowIndex; }, [startRow, sortedRowMap] ); From 540de1529079f782d157353f4b60c745e09d2ac3 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 10:47:12 -0800 Subject: [PATCH 05/10] [setup] Move imperative handler setup to its own util file - this will let us set up ref-specific helpers & add more comment context without bloating the main file --- src/components/datagrid/data_grid.tsx | 23 +++------- src/components/datagrid/utils/ref.ts | 63 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 src/components/datagrid/utils/ref.ts diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index e84c0caa00a..707eafbdce6 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -13,7 +13,6 @@ import React, { useMemo, useRef, useState, - useImperativeHandle, } from 'react'; import { VariableSizeGrid as Grid, @@ -56,6 +55,7 @@ import { schemaDetectors as providedSchemaDetectors, useMergedSchema, } from './utils/data_grid_schema'; +import { useImperativeGridRef } from './utils/ref'; import { EuiDataGridColumn, EuiDataGridProps, @@ -302,23 +302,14 @@ export const EuiDataGrid = forwardRef( }; /** - * Expose internal APIs as ref to consumer + * Expose certain internal APIs as ref to consumer */ - const { setFocusedCell } = focusContext; // eslint complains about the dependency array otherwise - const { openCellPopover, closeCellPopover } = cellPopoverContext; - - useImperativeHandle( + useImperativeGridRef({ ref, - () => ({ - setIsFullScreen, - setFocusedCell: ({ rowIndex, colIndex }) => { - setFocusedCell([colIndex, rowIndex]); // Transmog args from obj to array - }, - openCellPopover, - closeCellPopover, - }), - [setFocusedCell, openCellPopover, closeCellPopover] - ); + setIsFullScreen, + focusContext, + cellPopoverContext, + }); /** * Classes diff --git a/src/components/datagrid/utils/ref.ts b/src/components/datagrid/utils/ref.ts new file mode 100644 index 00000000000..d5622158b75 --- /dev/null +++ b/src/components/datagrid/utils/ref.ts @@ -0,0 +1,63 @@ +/* + * 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 { useImperativeHandle, useCallback, Ref } from 'react'; +import { + EuiDataGridRefProps, + DataGridFocusContextShape, + DataGridCellPopoverContextShape, +} from '../data_grid_types'; + +interface Dependencies { + ref: Ref; + setIsFullScreen: EuiDataGridRefProps['setIsFullScreen']; + focusContext: DataGridFocusContextShape; + cellPopoverContext: DataGridCellPopoverContextShape; +} + +export const useImperativeGridRef = ({ + ref, + setIsFullScreen, + focusContext, + cellPopoverContext, +}: Dependencies) => { + // Focus APIs + const { setFocusedCell: _setFocusedCell } = focusContext; // eslint complains about the dependency array otherwise + + const setFocusedCell = useCallback( + ({ rowIndex, colIndex }) => { + _setFocusedCell([colIndex, rowIndex]); // Transmog args from obj to array + }, + [_setFocusedCell] + ); + + // Popover APIs + const { + openCellPopover: _openCellPopover, + closeCellPopover, + } = cellPopoverContext; + + const openCellPopover = useCallback( + ({ rowIndex, colIndex }) => { + _openCellPopover({ rowIndex, colIndex }); + }, + [_openCellPopover] + ); + + // Set the ref APIs + useImperativeHandle( + ref, + () => ({ + setIsFullScreen, + setFocusedCell, + openCellPopover, + closeCellPopover, + }), + [setIsFullScreen, setFocusedCell, openCellPopover, closeCellPopover] + ); +}; From 9477d1f13cd398d5bc5330f0aac4d1f20e69b1e8 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 11:00:13 -0800 Subject: [PATCH 06/10] Add catch/check for cell locations that do not exist in the current grid --- src/components/datagrid/data_grid.tsx | 2 + src/components/datagrid/utils/ref.test.ts | 40 ++++++++++++++++++ src/components/datagrid/utils/ref.ts | 49 ++++++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/components/datagrid/utils/ref.test.ts diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 707eafbdce6..9c1573b0926 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -309,6 +309,8 @@ export const EuiDataGrid = forwardRef( setIsFullScreen, focusContext, cellPopoverContext, + rowCount, + visibleColCount, }); /** diff --git a/src/components/datagrid/utils/ref.test.ts b/src/components/datagrid/utils/ref.test.ts new file mode 100644 index 00000000000..36228f81230 --- /dev/null +++ b/src/components/datagrid/utils/ref.test.ts @@ -0,0 +1,40 @@ +/* + * 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 { testCustomHook } from '../../../test/test_custom_hook.test_helper'; +import { useCellLocationCheck } from './ref'; + +// TODO: see ref.spec.tsx for E2E useImperativeGridRef tests + +describe('useCellLocationCheck', () => { + const { + return: { checkCellExists }, + } = testCustomHook(() => useCellLocationCheck(10, 5)); + + it("throws an error if the passed rowIndex is higher than the grid's rowCount", () => { + expect(() => { + checkCellExists({ rowIndex: 12, colIndex: 0 }); + }).toThrow( + 'Row 12 is not a valid row. The maximum visible row index is 9.' + ); + }); + + it("throws an error if the passed colIndex is higher than the grid's visibleColCount", () => { + expect(() => { + checkCellExists({ rowIndex: 1, colIndex: 5 }); + }).toThrow( + 'Column 5 is not a valid column. The maximum visible column index is 4.' + ); + }); + + it('does not throw if the rowIndex and colIndex are within grid bounds', () => { + expect(() => { + checkCellExists({ rowIndex: 0, colIndex: 0 }); + }).not.toThrow(); + }); +}); diff --git a/src/components/datagrid/utils/ref.ts b/src/components/datagrid/utils/ref.ts index d5622158b75..4455bcb15ab 100644 --- a/src/components/datagrid/utils/ref.ts +++ b/src/components/datagrid/utils/ref.ts @@ -18,6 +18,8 @@ interface Dependencies { setIsFullScreen: EuiDataGridRefProps['setIsFullScreen']; focusContext: DataGridFocusContextShape; cellPopoverContext: DataGridCellPopoverContextShape; + rowCount: number; + visibleColCount: number; } export const useImperativeGridRef = ({ @@ -25,15 +27,25 @@ export const useImperativeGridRef = ({ setIsFullScreen, focusContext, cellPopoverContext, + rowCount, + visibleColCount, }: Dependencies) => { + // Cell location helpers + const { checkCellExists } = useCellLocationCheck(rowCount, visibleColCount); + // Focus APIs const { setFocusedCell: _setFocusedCell } = focusContext; // eslint complains about the dependency array otherwise + // When we pass this API to the consumer, we can't know for sure that + // the targeted cell is valid or in view (unlike our internal state, where + // both of those states can be guaranteed), so we need to do some extra + // checks here to make sure the grid automatically handles all cells const setFocusedCell = useCallback( ({ rowIndex, colIndex }) => { + checkCellExists({ rowIndex, colIndex }); _setFocusedCell([colIndex, rowIndex]); // Transmog args from obj to array }, - [_setFocusedCell] + [_setFocusedCell, checkCellExists] ); // Popover APIs @@ -42,11 +54,16 @@ export const useImperativeGridRef = ({ closeCellPopover, } = cellPopoverContext; + // When we pass this API to the consumer, we can't know for sure that + // the targeted cell is valid or in view (unlike our internal state, where + // both of those states can be guaranteed), so we need to do some extra + // checks here to make sure the grid automatically handles all cells const openCellPopover = useCallback( ({ rowIndex, colIndex }) => { + checkCellExists({ rowIndex, colIndex }); _openCellPopover({ rowIndex, colIndex }); }, - [_openCellPopover] + [_openCellPopover, checkCellExists] ); // Set the ref APIs @@ -61,3 +78,31 @@ export const useImperativeGridRef = ({ [setIsFullScreen, setFocusedCell, openCellPopover, closeCellPopover] ); }; + +/** + * Throw a digestible error if the consumer attempts to focus into an invalid + * cell range, which should also stop the APIs from continuing + */ +export const useCellLocationCheck = (rowCount: number, colCount: number) => { + const checkCellExists = useCallback( + ({ rowIndex, colIndex }) => { + if (rowIndex >= rowCount || rowIndex < 0) { + throw new Error( + `Row ${rowIndex} is not a valid row. The maximum visible row index is ${ + rowCount - 1 + }.` + ); + } + if (colIndex >= colCount) { + throw new Error( + `Column ${colIndex} is not a valid column. The maximum visible column index is ${ + colCount - 1 + }.` + ); + } + }, + [rowCount, colCount] + ); + + return { checkCellExists }; +}; From 74a833f5b4dedf973eb950bbd33af3710df133b2 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 11:39:59 -0800 Subject: [PATCH 07/10] Add getVisibleRowIndex helper - Converts the `rowIndex` from the consumer to a `visibleRowIndex` that our internal state can use - Account for sorted grid by finding the inversed index of the `sortedRowMap` - To make this easier, I converted soredRowMap to an array (since it's already only uses numbers for both keys and values), since arrays have a handy .findIndex method - Handles automatically paginating the grid if the targeted cell is on a different page --- .../header/data_grid_header_cell.test.tsx | 2 +- src/components/datagrid/data_grid.tsx | 2 + src/components/datagrid/data_grid_types.ts | 2 +- src/components/datagrid/utils/ref.test.ts | 67 ++++++++++++++++++- src/components/datagrid/utils/ref.ts | 54 +++++++++++++-- src/components/datagrid/utils/sorting.ts | 4 +- 6 files changed, 122 insertions(+), 9 deletions(-) diff --git a/src/components/datagrid/body/header/data_grid_header_cell.test.tsx b/src/components/datagrid/body/header/data_grid_header_cell.test.tsx index e17c2f65a82..2e26839e72f 100644 --- a/src/components/datagrid/body/header/data_grid_header_cell.test.tsx +++ b/src/components/datagrid/body/header/data_grid_header_cell.test.tsx @@ -49,7 +49,7 @@ describe('EuiDataGridHeaderCell', () => { diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 9c1573b0926..bed2f94fb14 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -309,6 +309,8 @@ export const EuiDataGrid = forwardRef( setIsFullScreen, focusContext, cellPopoverContext, + sortingContext, + pagination, rowCount, visibleColCount, }); diff --git a/src/components/datagrid/data_grid_types.ts b/src/components/datagrid/data_grid_types.ts index 51038aa09e1..48618ab813d 100644 --- a/src/components/datagrid/data_grid_types.ts +++ b/src/components/datagrid/data_grid_types.ts @@ -179,7 +179,7 @@ export interface EuiDataGridVisibleRows { export interface DataGridSortingContextShape { sorting?: EuiDataGridSorting; - sortedRowMap: { [key: number]: number }; + sortedRowMap: number[]; getCorrectRowIndex: (visibleRowIndex: number) => number; } diff --git a/src/components/datagrid/utils/ref.test.ts b/src/components/datagrid/utils/ref.test.ts index 36228f81230..f2f6b3722f0 100644 --- a/src/components/datagrid/utils/ref.test.ts +++ b/src/components/datagrid/utils/ref.test.ts @@ -7,7 +7,7 @@ */ import { testCustomHook } from '../../../test/test_custom_hook.test_helper'; -import { useCellLocationCheck } from './ref'; +import { useCellLocationCheck, useVisibleRowIndex } from './ref'; // TODO: see ref.spec.tsx for E2E useImperativeGridRef tests @@ -38,3 +38,68 @@ describe('useCellLocationCheck', () => { }).not.toThrow(); }); }); + +describe('useVisibleRowIndex', () => { + describe('if the grid is not sorted or paginated', () => { + const pagination = undefined; + const sortedRowMap: number[] = []; + + it('returns the passed rowIndex as-is', () => { + const { + return: { getVisibleRowIndex }, + } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + + expect(getVisibleRowIndex(5)).toEqual(5); + }); + }); + + describe('if the grid is sorted', () => { + const pagination = undefined; + const sortedRowMap = [3, 4, 1, 2, 0]; + + it('returns the visibleRowIndex of the passed rowIndex (which is the index of the sortedRowMap)', () => { + const { + return: { getVisibleRowIndex }, + } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + + expect(getVisibleRowIndex(0)).toEqual(4); + expect(getVisibleRowIndex(1)).toEqual(2); + expect(getVisibleRowIndex(2)).toEqual(3); + expect(getVisibleRowIndex(3)).toEqual(0); + expect(getVisibleRowIndex(4)).toEqual(1); + }); + }); + + describe('if the grid is paginated', () => { + const pagination = { + pageSize: 20, + pageIndex: 0, + onChangePage: jest.fn(), + onChangeItemsPerPage: jest.fn(), + }; + const sortedRowMap: number[] = []; + + beforeEach(() => jest.clearAllMocks()); + + it('calculates what page the row should be on, paginates to that page, and returns the index of the row on that page', () => { + const { + return: { getVisibleRowIndex }, + } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + + expect(getVisibleRowIndex(20)).toEqual(0); // First item on 2nd page + expect(pagination.onChangePage).toHaveBeenLastCalledWith(1); + + expect(getVisibleRowIndex(75)).toEqual(15); // 16th item on 4th page + expect(pagination.onChangePage).toHaveBeenLastCalledWith(3); + }); + + it('does not paginate if the user is already on the correct page', () => { + const { + return: { getVisibleRowIndex }, + } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + + expect(getVisibleRowIndex(5)).toEqual(5); + expect(pagination.onChangePage).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/components/datagrid/utils/ref.ts b/src/components/datagrid/utils/ref.ts index 4455bcb15ab..afc488292ae 100644 --- a/src/components/datagrid/utils/ref.ts +++ b/src/components/datagrid/utils/ref.ts @@ -9,8 +9,10 @@ import { useImperativeHandle, useCallback, Ref } from 'react'; import { EuiDataGridRefProps, + EuiDataGridProps, DataGridFocusContextShape, DataGridCellPopoverContextShape, + DataGridSortingContextShape, } from '../data_grid_types'; interface Dependencies { @@ -18,6 +20,8 @@ interface Dependencies { setIsFullScreen: EuiDataGridRefProps['setIsFullScreen']; focusContext: DataGridFocusContextShape; cellPopoverContext: DataGridCellPopoverContextShape; + sortingContext: DataGridSortingContextShape; + pagination: EuiDataGridProps['pagination']; rowCount: number; visibleColCount: number; } @@ -27,11 +31,14 @@ export const useImperativeGridRef = ({ setIsFullScreen, focusContext, cellPopoverContext, + sortingContext: { sortedRowMap }, + pagination, rowCount, visibleColCount, }: Dependencies) => { // Cell location helpers const { checkCellExists } = useCellLocationCheck(rowCount, visibleColCount); + const { getVisibleRowIndex } = useVisibleRowIndex(pagination, sortedRowMap); // Focus APIs const { setFocusedCell: _setFocusedCell } = focusContext; // eslint complains about the dependency array otherwise @@ -43,9 +50,10 @@ export const useImperativeGridRef = ({ const setFocusedCell = useCallback( ({ rowIndex, colIndex }) => { checkCellExists({ rowIndex, colIndex }); - _setFocusedCell([colIndex, rowIndex]); // Transmog args from obj to array + const visibleRowIndex = getVisibleRowIndex(rowIndex); + _setFocusedCell([colIndex, visibleRowIndex]); // Transmog args from obj to array }, - [_setFocusedCell, checkCellExists] + [_setFocusedCell, checkCellExists, getVisibleRowIndex] ); // Popover APIs @@ -61,9 +69,10 @@ export const useImperativeGridRef = ({ const openCellPopover = useCallback( ({ rowIndex, colIndex }) => { checkCellExists({ rowIndex, colIndex }); - _openCellPopover({ rowIndex, colIndex }); + const visibleRowIndex = getVisibleRowIndex(rowIndex); + _openCellPopover({ rowIndex: visibleRowIndex, colIndex }); }, - [_openCellPopover, checkCellExists] + [_openCellPopover, checkCellExists, getVisibleRowIndex] ); // Set the ref APIs @@ -106,3 +115,40 @@ export const useCellLocationCheck = (rowCount: number, colCount: number) => { return { checkCellExists }; }; + +/** + * The rowIndex passed from the consumer is the unsorted and unpaginated + * index derived from their original data. We need to convert that rowIndex + * into a visibleRowIndex (which is what our internal cell APIs use) and, if + * the row is not on the current page, the grid should automatically handle + * paginating to that row. + */ +export const useVisibleRowIndex = ( + pagination: EuiDataGridProps['pagination'], + sortedRowMap: DataGridSortingContextShape['sortedRowMap'] +) => { + const getVisibleRowIndex = useCallback( + (rowIndex: number): number => { + // Account for sorting + const visibleRowIndex = sortedRowMap.length + ? sortedRowMap.findIndex((mappedIndex) => mappedIndex === rowIndex) + : rowIndex; + + // Account for pagination + if (pagination) { + const pageIndex = Math.floor(visibleRowIndex / pagination.pageSize); + // If the targeted row is on a different page than the current page, + // we should automatically navigate the user to the correct page + if (pageIndex !== pagination.pageIndex) { + pagination.onChangePage(pageIndex); + } + // Get the row's visible row index on that page + return visibleRowIndex % pagination.pageSize; + } + return visibleRowIndex; + }, + [pagination, sortedRowMap] + ); + + return { getVisibleRowIndex }; +}; diff --git a/src/components/datagrid/utils/sorting.ts b/src/components/datagrid/utils/sorting.ts index 1f39869c735..59a9d11fce7 100644 --- a/src/components/datagrid/utils/sorting.ts +++ b/src/components/datagrid/utils/sorting.ts @@ -21,7 +21,7 @@ export const DataGridSortingContext = createContext< DataGridSortingContextShape >({ sorting: undefined, - sortedRowMap: {}, + sortedRowMap: [], getCorrectRowIndex: (number) => number, }); @@ -43,7 +43,7 @@ export const useSorting = ({ const sortingColumns = sorting?.columns; const sortedRowMap = useMemo(() => { - const rowMap: DataGridSortingContextShape['sortedRowMap'] = {}; + const rowMap: DataGridSortingContextShape['sortedRowMap'] = []; if ( inMemory?.level === 'sorting' && From ba29a97f9a4e027cd4ea1b8bee1233dae6c3268b Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 13:02:42 -0800 Subject: [PATCH 08/10] Replace grid ref Jest tests with more complete Cypress tests --- src/components/datagrid/data_grid.test.tsx | 29 +-- src/components/datagrid/utils/ref.spec.tsx | 198 +++++++++++++++++++++ src/components/datagrid/utils/ref.test.ts | 2 +- 3 files changed, 201 insertions(+), 28 deletions(-) create mode 100644 src/components/datagrid/utils/ref.spec.tsx diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 019b20824b7..846d8f27d73 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import React, { useEffect, useState, createRef } from 'react'; +import React, { useEffect, useState } from 'react'; import { mount, ReactWrapper, render } from 'enzyme'; import { EuiDataGrid } from './'; -import { EuiDataGridProps, EuiDataGridRefProps } from './data_grid_types'; +import { EuiDataGridProps } from './data_grid_types'; import { findTestSubject, requiredProps, @@ -2749,29 +2749,4 @@ describe('EuiDataGrid', () => { expect(takeMountedSnapshot(component)).toMatchSnapshot(); }); }); - - it('returns a ref which exposes internal imperative APIs', () => { - const gridRef = createRef(); - - mount( - {}, - }} - rowCount={1} - renderCellValue={() => 'value'} - ref={gridRef} - /> - ); - - expect(gridRef.current).toEqual({ - setIsFullScreen: expect.any(Function), - setFocusedCell: expect.any(Function), - openCellPopover: expect.any(Function), - closeCellPopover: expect.any(Function), - }); - }); }); diff --git a/src/components/datagrid/utils/ref.spec.tsx b/src/components/datagrid/utils/ref.spec.tsx new file mode 100644 index 00000000000..59f8937ddd3 --- /dev/null +++ b/src/components/datagrid/utils/ref.spec.tsx @@ -0,0 +1,198 @@ +/* + * 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, { useState, createRef, forwardRef } from 'react'; +import { EuiDataGrid } from '../'; +import { EuiDataGridRefProps } from '../data_grid_types'; + +// We need to set up a test component here for sorting/pagination state to work +// The underlying imperative ref should still be forwarded and work as normal +const GridTest = forwardRef((_, ref) => { + // Pagination + const [pageIndex, setPageIndex] = useState(0); + const onChangePage = (pageIndex) => setPageIndex(pageIndex); + + // Sorting + const [sortingColumns, setSortingColumns] = useState([]); + const onSort = (sortingColumns) => setSortingColumns(sortingColumns); + + return ( + {}, + }} + rowCount={100} + renderCellValue={({ rowIndex, columnId }) => `${columnId}, ${rowIndex}`} + pagination={{ + pageIndex, + pageSize: 25, + onChangePage, + onChangeItemsPerPage: () => {}, + }} + sorting={{ columns: sortingColumns, onSort }} + inMemory={{ level: 'sorting' }} + /> + ); +}); +GridTest.displayName = 'GridTest'; + +describe('useImperativeGridRef', () => { + const ref = createRef(); + + beforeEach(() => { + cy.mount(); + cy.get('[data-test-subj="euiDataGridBody"]'); // Wait for the grid to finish rendering and pass back the ref + }); + + describe('setIsFullScreen', () => { + it('allows the consumer to manually toggle full screen mode', () => { + ref.current.setIsFullScreen(true); + cy.get('[data-test-subj="euiDataGrid"]').should( + 'have.class', + 'euiDataGrid--fullScreen' + ); + // Has to be a separate .then() block from the above for some Cypress-y reason + cy.then(() => { + ref.current.setIsFullScreen(false); + cy.get('[data-test-subj="euiDataGrid"]').should( + 'not.have.class', + 'euiDataGrid--fullScreen' + ); + }); + }); + }); + + describe('setFocusedCell', () => { + it('allows the consumer to manually focus into a specific grid cell', () => { + ref.current.setFocusedCell({ rowIndex: 1, colIndex: 1 }); + cy.focused() + .should('have.attr', 'data-gridcell-visible-row-index', '1') + .should('have.attr', 'data-gridcell-column-index', '1'); + }); + + it('should scroll to cells that are not in view', () => { + ref.current.setFocusedCell({ rowIndex: 24, colIndex: 5 }); + cy.focused() + .should('have.attr', 'data-gridcell-visible-row-index', '24') + .should('have.attr', 'data-gridcell-column-index', '5'); + }); + + it('should paginate to cells that are not on the current page', () => { + ref.current.setFocusedCell({ rowIndex: 50, colIndex: 0 }); + cy.get('.euiPagination .euiScreenReaderOnly').should( + 'have.text', + 'Page 3 of 4' + ); + cy.focused() + .should('have.attr', 'data-gridcell-visible-row-index', '0') + .should('have.attr', 'data-gridcell-column-index', '0'); + }); + + it('should correctly find the specified rowIndex when sorted', () => { + cy.get('[data-test-subj="dataGridHeaderCell-A"]').click(); + cy.contains('Sort High-Low').click(); + cy.then(() => { + ref.current.setFocusedCell({ rowIndex: 95, colIndex: 0 }); + cy.focused() + .should('have.attr', 'data-gridcell-visible-row-index', '4') + .should('have.attr', 'data-gridcell-column-index', '0'); + }); + }); + + it('should throw an error if the passed cell indices are invalid', () => { + cy.on('fail', (err) => { + expect(err.message).to.equal( + 'Row 150 is not a valid row. The maximum visible row index is 99.' + ); + }); + ref.current.setFocusedCell({ rowIndex: 150, colIndex: 0 }); + }); + }); + + describe('openCellPopover', () => { + it("allows the consumer to manually open a specific grid cell's popover", () => { + ref.current.openCellPopover({ rowIndex: 2, colIndex: 2 }); + cy.focused() + .should('have.attr', 'data-test-subj', 'euiDataGridExpansionPopover') + .find('.euiText') + .should('have.text', 'C, 2'); + }); + + it('should scroll to cells that are not in view', () => { + ref.current.openCellPopover({ rowIndex: 23, colIndex: 0 }); + cy.focused() + .should('have.attr', 'data-test-subj', 'euiDataGridExpansionPopover') + .find('.euiText') + .should('have.text', 'A, 23'); + }); + + it('should paginate to cells that are not on the current page', () => { + ref.current.openCellPopover({ rowIndex: 99, colIndex: 5 }); + cy.get('.euiPagination .euiScreenReaderOnly').should( + 'have.text', + 'Page 4 of 4' + ); + cy.focused() + .should('have.attr', 'data-test-subj', 'euiDataGridExpansionPopover') + .find('.euiText') + .should('have.text', 'F, 99'); + }); + + it('should correctly find the specified rowIndex when sorted', () => { + cy.get('[data-test-subj="dataGridHeaderCell-A"]').click(); + cy.contains('Sort High-Low').click(); + cy.then(() => { + ref.current.openCellPopover({ rowIndex: 98, colIndex: 1 }); + cy.focused() + .should('have.attr', 'data-test-subj', 'euiDataGridExpansionPopover') + .find('.euiText') + .should('have.text', 'B, 98'); + }); + }); + + it('should throw an error if the passed cell indices are invalid', () => { + cy.on('fail', (err) => { + expect(err.message).to.equal( + 'Column 10 is not a valid column. The maximum visible column index is 5.' + ); + }); + ref.current.openCellPopover({ rowIndex: 0, colIndex: 10 }); + }); + }); + + describe('closeCellPopover', () => { + it('allows the consumer to manually close any open popovers', () => { + ref.current.setFocusedCell({ colIndex: 0, rowIndex: 0 }); + cy.realPress('Enter'); + cy.get('[data-test-subj="euiDataGridExpansionPopover"]').should( + 'have.length', + 1 + ); + cy.then(() => { + ref.current.closeCellPopover(); + cy.get('[data-test-subj="euiDataGridExpansionPopover"]').should( + 'have.length', + 0 + ); + }); + }); + }); +}); diff --git a/src/components/datagrid/utils/ref.test.ts b/src/components/datagrid/utils/ref.test.ts index f2f6b3722f0..abb7957297a 100644 --- a/src/components/datagrid/utils/ref.test.ts +++ b/src/components/datagrid/utils/ref.test.ts @@ -9,7 +9,7 @@ import { testCustomHook } from '../../../test/test_custom_hook.test_helper'; import { useCellLocationCheck, useVisibleRowIndex } from './ref'; -// TODO: see ref.spec.tsx for E2E useImperativeGridRef tests +// see ref.spec.tsx for E2E useImperativeGridRef tests describe('useCellLocationCheck', () => { const { From 56465f6258e6994e135f7a62c9659bf6c25d4358 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Jan 2022 13:40:45 -0800 Subject: [PATCH 09/10] Update documentation with new behavior --- .../views/datagrid/datagrid_ref_example.js | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src-docs/src/views/datagrid/datagrid_ref_example.js b/src-docs/src/views/datagrid/datagrid_ref_example.js index 49e9ee6fd2b..40a2c47f54b 100644 --- a/src-docs/src/views/datagrid/datagrid_ref_example.js +++ b/src-docs/src/views/datagrid/datagrid_ref_example.js @@ -63,10 +63,30 @@ export const DataGridRefExample = {
  • -

    - openCellPopover({'{ rowIndex, colIndex }'}) - - opens the specified cell's popover contents. -

    + openCellPopover({'{ rowIndex, colIndex }'}) - + opens the specified cell's popover contents. + + + When using setFocusedCell or{' '} + openCellPopover, keep in mind: +
      +
    • + colIndex is affected by the user reordering or hiding + columns. +
    • +
    • + If the passed cell indices are outside the data grid's + total row count or visible column count, an error will be + thrown. +
    • +
    • + If the data grid is paginated or sorted, the grid will + handle automatically finding specified row index's + correct location for you. +
    • +
    +
    +
  • From 55a4ba6b513dc651ba8817880fee69a149de8771 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Tue, 1 Feb 2022 12:20:08 -0800 Subject: [PATCH 10/10] [PR feedback] Rename fns to indicate multiple concerns - having a side effect in a getter feels bad, so change that to a `find` - rename use hook to indicate sorting and pagination concerns --- src/components/datagrid/utils/ref.test.ts | 40 +++++++++++------------ src/components/datagrid/utils/ref.ts | 16 ++++----- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/components/datagrid/utils/ref.test.ts b/src/components/datagrid/utils/ref.test.ts index abb7957297a..bf49b5fcd87 100644 --- a/src/components/datagrid/utils/ref.test.ts +++ b/src/components/datagrid/utils/ref.test.ts @@ -7,7 +7,7 @@ */ import { testCustomHook } from '../../../test/test_custom_hook.test_helper'; -import { useCellLocationCheck, useVisibleRowIndex } from './ref'; +import { useCellLocationCheck, useSortPageCheck } from './ref'; // see ref.spec.tsx for E2E useImperativeGridRef tests @@ -39,17 +39,17 @@ describe('useCellLocationCheck', () => { }); }); -describe('useVisibleRowIndex', () => { +describe('useSortPageCheck', () => { describe('if the grid is not sorted or paginated', () => { const pagination = undefined; const sortedRowMap: number[] = []; it('returns the passed rowIndex as-is', () => { const { - return: { getVisibleRowIndex }, - } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + return: { findVisibleRowIndex }, + } = testCustomHook(() => useSortPageCheck(pagination, sortedRowMap)); - expect(getVisibleRowIndex(5)).toEqual(5); + expect(findVisibleRowIndex(5)).toEqual(5); }); }); @@ -59,14 +59,14 @@ describe('useVisibleRowIndex', () => { it('returns the visibleRowIndex of the passed rowIndex (which is the index of the sortedRowMap)', () => { const { - return: { getVisibleRowIndex }, - } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); - - expect(getVisibleRowIndex(0)).toEqual(4); - expect(getVisibleRowIndex(1)).toEqual(2); - expect(getVisibleRowIndex(2)).toEqual(3); - expect(getVisibleRowIndex(3)).toEqual(0); - expect(getVisibleRowIndex(4)).toEqual(1); + return: { findVisibleRowIndex }, + } = testCustomHook(() => useSortPageCheck(pagination, sortedRowMap)); + + expect(findVisibleRowIndex(0)).toEqual(4); + expect(findVisibleRowIndex(1)).toEqual(2); + expect(findVisibleRowIndex(2)).toEqual(3); + expect(findVisibleRowIndex(3)).toEqual(0); + expect(findVisibleRowIndex(4)).toEqual(1); }); }); @@ -83,22 +83,22 @@ describe('useVisibleRowIndex', () => { it('calculates what page the row should be on, paginates to that page, and returns the index of the row on that page', () => { const { - return: { getVisibleRowIndex }, - } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + return: { findVisibleRowIndex }, + } = testCustomHook(() => useSortPageCheck(pagination, sortedRowMap)); - expect(getVisibleRowIndex(20)).toEqual(0); // First item on 2nd page + expect(findVisibleRowIndex(20)).toEqual(0); // First item on 2nd page expect(pagination.onChangePage).toHaveBeenLastCalledWith(1); - expect(getVisibleRowIndex(75)).toEqual(15); // 16th item on 4th page + expect(findVisibleRowIndex(75)).toEqual(15); // 16th item on 4th page expect(pagination.onChangePage).toHaveBeenLastCalledWith(3); }); it('does not paginate if the user is already on the correct page', () => { const { - return: { getVisibleRowIndex }, - } = testCustomHook(() => useVisibleRowIndex(pagination, sortedRowMap)); + return: { findVisibleRowIndex }, + } = testCustomHook(() => useSortPageCheck(pagination, sortedRowMap)); - expect(getVisibleRowIndex(5)).toEqual(5); + expect(findVisibleRowIndex(5)).toEqual(5); expect(pagination.onChangePage).not.toHaveBeenCalled(); }); }); diff --git a/src/components/datagrid/utils/ref.ts b/src/components/datagrid/utils/ref.ts index afc488292ae..88aebb8eb8d 100644 --- a/src/components/datagrid/utils/ref.ts +++ b/src/components/datagrid/utils/ref.ts @@ -38,7 +38,7 @@ export const useImperativeGridRef = ({ }: Dependencies) => { // Cell location helpers const { checkCellExists } = useCellLocationCheck(rowCount, visibleColCount); - const { getVisibleRowIndex } = useVisibleRowIndex(pagination, sortedRowMap); + const { findVisibleRowIndex } = useSortPageCheck(pagination, sortedRowMap); // Focus APIs const { setFocusedCell: _setFocusedCell } = focusContext; // eslint complains about the dependency array otherwise @@ -50,10 +50,10 @@ export const useImperativeGridRef = ({ const setFocusedCell = useCallback( ({ rowIndex, colIndex }) => { checkCellExists({ rowIndex, colIndex }); - const visibleRowIndex = getVisibleRowIndex(rowIndex); + const visibleRowIndex = findVisibleRowIndex(rowIndex); _setFocusedCell([colIndex, visibleRowIndex]); // Transmog args from obj to array }, - [_setFocusedCell, checkCellExists, getVisibleRowIndex] + [_setFocusedCell, checkCellExists, findVisibleRowIndex] ); // Popover APIs @@ -69,10 +69,10 @@ export const useImperativeGridRef = ({ const openCellPopover = useCallback( ({ rowIndex, colIndex }) => { checkCellExists({ rowIndex, colIndex }); - const visibleRowIndex = getVisibleRowIndex(rowIndex); + const visibleRowIndex = findVisibleRowIndex(rowIndex); _openCellPopover({ rowIndex: visibleRowIndex, colIndex }); }, - [_openCellPopover, checkCellExists, getVisibleRowIndex] + [_openCellPopover, checkCellExists, findVisibleRowIndex] ); // Set the ref APIs @@ -123,11 +123,11 @@ export const useCellLocationCheck = (rowCount: number, colCount: number) => { * the row is not on the current page, the grid should automatically handle * paginating to that row. */ -export const useVisibleRowIndex = ( +export const useSortPageCheck = ( pagination: EuiDataGridProps['pagination'], sortedRowMap: DataGridSortingContextShape['sortedRowMap'] ) => { - const getVisibleRowIndex = useCallback( + const findVisibleRowIndex = useCallback( (rowIndex: number): number => { // Account for sorting const visibleRowIndex = sortedRowMap.length @@ -150,5 +150,5 @@ export const useVisibleRowIndex = ( [pagination, sortedRowMap] ); - return { getVisibleRowIndex }; + return { findVisibleRowIndex }; };