From 81b99cef087ed028e0f5d4b4cdbb9ab5826d490e Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 6 Mar 2026 11:46:39 +0100 Subject: [PATCH 1/2] fix(AnalyticalTable): reconcile columnOrder with columns on reorder --- .../AnalyticalTable/AnalyticalTable.cy.tsx | 73 +++++++++++++++++++ .../AnalyticalTable/hooks/useDragAndDrop.ts | 22 ++++-- .../components/AnalyticalTable/types/index.ts | 2 +- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx index 87636bdcc8c..f26fff8cc5b 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx @@ -2034,6 +2034,7 @@ describe('AnalyticalTable', () => { }); }); }); + it('columns drag & drop', () => { const localCols = [...columns]; localCols.pop(); @@ -2058,6 +2059,78 @@ describe('AnalyticalTable', () => { cy.get('@reorder').should('have.been.calledTwice'); }); + it('columns drag & drop: reorder then add/remove column', () => { + const baseCols = [ + { accessor: 'name', Header: 'Name' }, + { accessor: 'age', Header: 'Age' }, + { accessor: 'friend.name', Header: 'Friend Name' }, + ]; + const extraCol = { accessor: 'friend.age', Header: 'Friend Age' }; + const reorder = cy.spy().as('reorder'); + + const TestComp = () => { + const [cols, setCols] = useState(baseCols); + return ( + <> + + + + ); + }; + + const dataTransferById = (colId) => ({ + getData: () => colId, + }); + + cy.mount(); + // name -> age => [age, name, friend.name] + cy.get('[data-column-id="name"]') + .trigger('dragstart') + .trigger('drop', { dataTransfer: dataTransferById('age') }); + cy.get('[data-column-id]').each(($col, index) => { + cy.wrap($col).should('have.text', ['Age', 'Name', 'Friend Name'][index]); + }); + + // add friend.age => [age, name, friend.name, friend.age] + cy.findByText('Toggle Column').click(); + cy.get('[data-column-id]').should('have.length', 4); + cy.get('[data-column-id]').each(($col, index) => { + cy.wrap($col).should('have.text', ['Age', 'Name', 'Friend Name', 'Friend Age'][index]); + }); + cy.get('[role="separator"]').eq(0).click(); + + // friend.age -> age => [friend.age, age, name, friend.name] + cy.get('[data-column-id="age"]') + .trigger('dragstart') + .trigger('drop', { dataTransfer: dataTransferById('friend.age') }); + cy.get('[data-column-id]').each(($col, index) => { + cy.wrap($col).should('have.text', ['Friend Age', 'Age', 'Name', 'Friend Name'][index]); + }); + + // remove friend.age => [age, name, friend.name] + cy.findByText('Toggle Column').click(); + cy.get('[data-column-id]').should('have.length', 3); + cy.get('[data-column-id]').each(($col, index) => { + cy.wrap($col).should('have.text', ['Age', 'Name', 'Friend Name'][index]); + }); + + // friend.name -> age => [friend.name, age, name] + cy.get('[data-column-id="age"]') + .trigger('dragstart') + .trigger('drop', { dataTransfer: dataTransferById('friend.name') }); + cy.get('[data-column-id]').each(($col, index) => { + cy.wrap($col).should('have.text', ['Friend Name', 'Age', 'Name'][index]); + }); + + cy.get('@reorder').should('have.callCount', 3); + }); + it('w/o selection column', () => { cy.mount( { - return typeof column.accessor === 'string' ? column.accessor : column.id; -}; +import type { ColumnType, ReactTableHooks, TableInstance } from '../types/index.js'; function getHeaderProps( props: Record, @@ -39,7 +35,19 @@ function getHeaderProps( const draggedColId = e.dataTransfer.getData('text'); if (droppedColId === draggedColId) return; - const internalColumnOrder = columnOrder.length > 0 ? columnOrder : columns.map((col) => getColumnId(col)); + // Reconciliation uses same approach as visibleColumns in plugin-hooks/useColumnOrder.js of react-table + const columnOrderCopy = [...columnOrder]; + const columnsCopy = [...columns]; + const columnsInOrder: ColumnType[] = []; + + while (columnsCopy.length && columnOrderCopy.length) { + const targetId = columnOrderCopy.shift(); + const foundIndex = columnsCopy.findIndex((col) => col.id === targetId); + if (foundIndex > -1) { + columnsInOrder.push(columnsCopy.splice(foundIndex, 1)[0]); + } + } + const internalColumnOrder = [...columnsInOrder, ...columnsCopy].map((col) => col.id); const droppedColIdx = internalColumnOrder.findIndex((col) => col === droppedColId); const draggedColIdx = internalColumnOrder.findIndex((col) => col === draggedColId); @@ -50,7 +58,7 @@ function getHeaderProps( setColumnOrder(tempCols); if (typeof onColumnsReorder === 'function') { - const columnsNewOrder = tempCols.map((tempColId) => columns.find((col) => getColumnId(col) === tempColId)); + const columnsNewOrder = tempCols.map((tempColId) => columns.find((col) => col.id === tempColId)); onColumnsReorder( enrichEventWithDetails(e, { columnsNewOrder, diff --git a/packages/main/src/components/AnalyticalTable/types/index.ts b/packages/main/src/components/AnalyticalTable/types/index.ts index e3a08944b58..c31bc1669c2 100644 --- a/packages/main/src/components/AnalyticalTable/types/index.ts +++ b/packages/main/src/components/AnalyticalTable/types/index.ts @@ -51,7 +51,7 @@ export enum RenderColumnTypes { } export interface ColumnType extends Omit { - id?: string; + id: string; Expandable?: any; Grouped?: any; RepeatedValue?: any; From 7c8db0091421cef63f678639d29bd5203c5714b4 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Fri, 6 Mar 2026 11:56:54 +0100 Subject: [PATCH 2/2] Update index.ts --- packages/main/src/components/AnalyticalTable/types/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/components/AnalyticalTable/types/index.ts b/packages/main/src/components/AnalyticalTable/types/index.ts index c31bc1669c2..e3a08944b58 100644 --- a/packages/main/src/components/AnalyticalTable/types/index.ts +++ b/packages/main/src/components/AnalyticalTable/types/index.ts @@ -51,7 +51,7 @@ export enum RenderColumnTypes { } export interface ColumnType extends Omit { - id: string; + id?: string; Expandable?: any; Grouped?: any; RepeatedValue?: any;