diff --git a/packages/eui/changelogs/upcoming/9030.md b/packages/eui/changelogs/upcoming/9030.md new file mode 100644 index 00000000000..91dae90f327 --- /dev/null +++ b/packages/eui/changelogs/upcoming/9030.md @@ -0,0 +1,4 @@ +**Bug fixes** + +- Fixed `columns` in `EuiDataGrid` not being rendered in the correct order as defined by `columnVisibility.visibleColumns` which resulted in unexpected updates when reordering columns + diff --git a/packages/eui/src/components/datagrid/controls/column_selector.tsx b/packages/eui/src/components/datagrid/controls/column_selector.tsx index 3299f7574ea..5ff1bdfc568 100644 --- a/packages/eui/src/components/datagrid/controls/column_selector.tsx +++ b/packages/eui/src/components/datagrid/controls/column_selector.tsx @@ -60,12 +60,34 @@ export const useDataGridColumnSelector = ( 'allowReorder' ); - const [sortedColumns, setSortedColumns] = useDependentState( - () => availableColumns.map(({ id }) => id), - [availableColumns] - ); - const { visibleColumns, setVisibleColumns } = columnVisibility; + + const [sortedColumns, setSortedColumns] = useDependentState(() => { + const availableColumnIds = availableColumns.map(({ id }) => id); + const availableSet = new Set(availableColumnIds); + // Filter visibleColumns to only include existing columns + const validVisibleColumns = visibleColumns.filter((id) => + availableSet.has(id) + ); + const visibleSet = new Set(validVisibleColumns); + + const result: string[] = []; + let visibleIndex = 0; + + for (const columnId of availableColumnIds) { + if (visibleSet.has(columnId)) { + // Replace with next visible column in order + result.push(validVisibleColumns[visibleIndex++]); + } else { + // Keep hidden column in original position + result.push(columnId); + } + } + + return result; + // doesn't depend on visibleColumns on purpose to keep it an initial state + }, [availableColumns]); + const visibleColumnIds = useMemo( () => new Set(visibleColumns), [visibleColumns] diff --git a/packages/eui/src/components/datagrid/data_grid.spec.tsx b/packages/eui/src/components/datagrid/data_grid.spec.tsx index 3169fb4baad..71399fbbabe 100644 --- a/packages/eui/src/components/datagrid/data_grid.spec.tsx +++ b/packages/eui/src/components/datagrid/data_grid.spec.tsx @@ -10,7 +10,7 @@ /// /// -import React, { ReactNode } from 'react'; +import React, { ReactNode, useState } from 'react'; import { EuiDataGrid, EuiDataGridColumn, @@ -737,6 +737,169 @@ a, footer\tb, footer }); }); }); + + describe('column reordering', () => { + const props = { + ...baseProps, + columns: [ + { id: 'a', display: 'First' }, + { id: 'b', display: 'Second' }, + { id: 'c', display: 'Third' }, + { id: 'd', display: 'Fourth' }, + ], + columnVisibility: { + visibleColumns: ['a', 'b', 'd'], + setVisibleColumns: () => {}, + canDragAndDropColumns: true, + }, + } as EuiDataGridProps; + + const StatefulDataGrid = ({ + columns, + columnVisibility, + ...rest + }: EuiDataGridProps) => { + const [visibleColumns, setVisibleColumns] = useState( + columnVisibility.visibleColumns + ); + + return ( + + ); + }; + + it('renders columns in the correct order when `columns` and `visibleColumns` have the same order', () => { + cy.mount(); + + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'First'); + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Second'); + cy.get( + '[data-gridcell-column-index="2"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Fourth'); + + /* checks column selector popover order */ + cy.get('[data-test-subj="dataGridColumnSelectorButton"]').click(); + + cy.get('.euiDataGridColumnSelector__item').eq(0).should('have.text', 'a'); + cy.get('.euiDataGridColumnSelector__item').eq(1).should('have.text', 'b'); + cy.get('.euiDataGridColumnSelector__item').eq(2).should('have.text', 'c'); + cy.get('.euiDataGridColumnSelector__item').eq(3).should('have.text', 'd'); + + cy.get( + '.euiDataGridColumnSelector__item [data-test-subj="dataGridColumnSelectorToggleColumnVisibility-c"]' + ).should('have.attr', 'aria-checked', 'false'); + }); + + it('renders columns in the corect order when `columns` and `visibleColumns` have different orders', () => { + cy.mount( + + ); + + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Second'); + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Fourth'); + cy.get( + '[data-gridcell-column-index="2"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'First'); + + /* checks column selector popover order */ + cy.get('[data-test-subj="dataGridColumnSelectorButton"]').click(); + + cy.get('.euiDataGridColumnSelector__item').eq(0).should('have.text', 'b'); + cy.get('.euiDataGridColumnSelector__item').eq(1).should('have.text', 'd'); + cy.get('.euiDataGridColumnSelector__item').eq(2).should('have.text', 'c'); + cy.get('.euiDataGridColumnSelector__item').eq(3).should('have.text', 'a'); + + cy.get( + '.euiDataGridColumnSelector__item [data-test-subj="dataGridColumnSelectorToggleColumnVisibility-c"]' + ).should('have.attr', 'aria-checked', 'false'); + }); + + it('reorders columns from the column selector', () => { + cy.mount(); + + cy.get('[data-test-subj="dataGridColumnSelectorButton"]').click(); + + /* reorder column selectors */ + cy.get('.euiDataGridColumnSelector__item') + .eq(0) + .realHover() + .realMouseDown({ position: 'center' }) + .realMouseMove(0, 0) // start drag + .realMouseMove(0, 120) // move (absolute coordinates) + .realMouseUp(); + + cy.get('.euiDataGridColumnSelector__item').eq(0).should('have.text', 'b'); + cy.get('.euiDataGridColumnSelector__item').eq(1).should('have.text', 'a'); + cy.get('.euiDataGridColumnSelector__item').eq(2).should('have.text', 'c'); + cy.get('.euiDataGridColumnSelector__item').eq(3).should('have.text', 'd'); + + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Second'); + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'First'); + cy.get( + '[data-gridcell-column-index="2"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Fourth'); + }); + + it('reorders columns via drag and drop', () => { + cy.mount(); + + /* reorder columns*/ + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-visible-row-index="-1"]' + ) + .realHover() + .realMouseDown({ position: 'center' }) + .realMouseMove(0, 0) // start drag + .realMouseMove(250, 50) // move (absolute coordinates) + .realMouseUp(); + + cy.get( + '[data-gridcell-column-index="0"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Second'); + cy.get( + '[data-gridcell-column-index="1"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'Fourth'); + cy.get( + '[data-gridcell-column-index="2"][data-gridcell-visible-row-index="-1"] .euiDataGridHeaderCell__content' + ).should('have.text', 'First'); + + cy.get('[data-test-subj="dataGridColumnSelectorButton"]').click(); + + cy.get('.euiDataGridColumnSelector__item').eq(0).should('have.text', 'b'); + cy.get('.euiDataGridColumnSelector__item').eq(1).should('have.text', 'c'); + cy.get('.euiDataGridColumnSelector__item').eq(2).should('have.text', 'd'); + cy.get('.euiDataGridColumnSelector__item').eq(3).should('have.text', 'a'); + }); + }); }); function getGridData() {