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() {