diff --git a/src/components/datagrid/body/data_grid_row_manager.test.ts b/src/components/datagrid/body/data_grid_row_manager.test.ts index 8525afba21c..277d0f320ef 100644 --- a/src/components/datagrid/body/data_grid_row_manager.test.ts +++ b/src/components/datagrid/body/data_grid_row_manager.test.ts @@ -97,6 +97,24 @@ describe('row manager', () => { /> `); }); + + it("updates the row's visible row index and striping on sort", () => { + const row = rowManager.getRow({ + rowIndex: 0, + visibleRowIndex: 3, + top: '100px', + height: 200, + }); + expect(row).toMatchInlineSnapshot(` +
+ `); + }); }); describe("when the row's child cells are all removed", () => { @@ -132,6 +150,12 @@ describe('row manager', () => { expect(row0.classList.contains('world')).toBe(true); }); + it('allows passing multiple classes', () => { + rerender({ ...mockArgs, rowClasses: { 0: 'hello world' } }); + expect(getRow(0).classList.contains('hello')).toBe(true); + expect(getRow(0).classList.contains('world')).toBe(true); + }); + it('adds/removes row classes correctly when gridStyle.rowClasses updates', () => { rerender({ ...mockArgs, rowClasses: { 1: 'test' } }); const row0 = getRow(0); @@ -141,5 +165,13 @@ describe('row manager', () => { expect(row0.classList.contains('world')).toBe(false); expect(row1.classList.contains('test')).toBe(true); }); + + it('correctly preserves EUI classes when adding/removing classes dynamically', () => { + rerender({ ...mockArgs, rowClasses: undefined }); + + expect(getRow(0).classList.value).toEqual( + 'euiDataGridRow euiDataGridRow--striped' + ); + }); }); }); diff --git a/src/components/datagrid/body/data_grid_row_manager.ts b/src/components/datagrid/body/data_grid_row_manager.ts index 090f4b49365..05984951216 100644 --- a/src/components/datagrid/body/data_grid_row_manager.ts +++ b/src/components/datagrid/body/data_grid_row_manager.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import { useRef, useCallback, useEffect, RefObject } from 'react'; +import { useRef, useCallback, RefObject } from 'react'; +import { useUpdateEffect } from '../../../services'; + import { EuiDataGridRowManager, EuiDataGridStyle } from '../data_grid_types'; export const useRowManager = ({ @@ -25,14 +27,11 @@ export const useRowManager = ({ if (rowElement == null) { rowElement = document.createElement('div'); rowElement.setAttribute('role', 'row'); - rowElement.dataset.gridRowIndex = String(rowIndex); // Row index from data, affected by sorting/pagination - rowElement.dataset.gridVisibleRowIndex = String(visibleRowIndex); // Affected by sorting/pagination + rowElement.dataset.gridRowIndex = String(rowIndex); // Row index from data, not affected by sorting/pagination rowElement.classList.add('euiDataGridRow'); if (rowClasses?.[rowIndex]) { - rowElement.classList.add(rowClasses[rowIndex]); + rowElement.classList.add(...rowClasses[rowIndex].split(' ')); } - const isOddRow = visibleRowIndex % 2 !== 0; - if (isOddRow) rowElement.classList.add('euiDataGridRow--striped'); rowElement.style.position = 'absolute'; rowElement.style.left = '0'; rowElement.style.right = '0'; @@ -58,6 +57,17 @@ export const useRowManager = ({ observer.observe(rowElement, { childList: true }); } + // Ensure the row's visible row index & striping update correctly on sort & pagination + if (rowElement.dataset.gridVisibleRowIndex !== String(visibleRowIndex)) { + rowElement.dataset.gridVisibleRowIndex = String(visibleRowIndex); + const isOddRow = visibleRowIndex % 2 !== 0; + if (isOddRow) { + rowElement.classList.add('euiDataGridRow--striped'); + } else { + rowElement.classList.remove('euiDataGridRow--striped'); + } + } + // Ensure that the row's dimensions are always correct by having each cell update position styles rowElement.style.top = top; rowElement.style.height = `${height}px`; @@ -68,13 +78,17 @@ export const useRowManager = ({ ); // Update row classes dynamically whenever a new prop is passed in - useEffect(() => { + useUpdateEffect(() => { if (rowClasses) { rowIdToElements.current.forEach((rowElement, rowIndex) => { + const euiClasses = Array.from(rowElement.classList) + .filter((className) => className.startsWith('euiDataGridRow')) + .join(' '); + if (rowClasses[rowIndex]) { - rowElement.classList.value = `euiDataGridRow ${rowClasses[rowIndex]}`; + rowElement.classList.value = `${euiClasses} ${rowClasses[rowIndex]}`; } else { - rowElement.classList.value = 'euiDataGridRow'; // Clear any added classes + rowElement.classList.value = euiClasses; // Clear any added classes } }); } diff --git a/upcoming_changelogs/7301.md b/upcoming_changelogs/7301.md new file mode 100644 index 00000000000..c6e239d6f8b --- /dev/null +++ b/upcoming_changelogs/7301.md @@ -0,0 +1,5 @@ +**Bug fixes** + +- Fixed `EuiDataGrid`s with `gridStyle.stripes` sometimes showing buggy row striping after being sorted +- Fixed `EuiDataGrid`'s `gridStyle.rowClasses` API to not conflict with `gridStyle.stripes` if dynamically updated +- Fixed `EuiDataGrid`'s `gridStyle.rowClasses` API to support multiple space-separated classes