Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/main/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ useEffect(() => {
<AnalyticalTable
data={data}
columns={columns}
retainColumnWidth // prevent column width reset
retainColumnWidth // prevent column width reset on container resize
reactTableOptions={{
autoResetSelectedRows: !skipPageResetRef.current,
autoResetSortBy: !skipPageResetRef.current,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4826,6 +4826,61 @@ describe('AnalyticalTable', () => {
cy.get('[data-component-name="AnalyticalTableBody"]').should('have.prop', 'scrollTop', 2500);
});

it('retainColumnWidth: recalculates widths after columns change', () => {
const columnsA = [
{ Header: 'Name', accessor: 'name' },
{ Header: 'Age', accessor: 'age' },
];
const columnsB = [
{ Header: 'Product', accessor: 'product' },
{ Header: 'Price', accessor: 'price' },
{ Header: 'Qty', accessor: 'qty' },
];
const dataA = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 },
];
const dataB = [
{ product: 'Widget', price: '$10', qty: 5 },
{ product: 'Gadget', price: '$20', qty: 3 },
];

function TestComp() {
const [useB, setUseB] = useState(false);
return (
<>
<Button data-testid="switch" onClick={() => setUseB((prev) => !prev)}>
Switch
</Button>
<AnalyticalTable
columns={useB ? columnsB : columnsA}
data={useB ? dataB : dataA}
retainColumnWidth
scaleWidthMode={AnalyticalTableScaleWidthMode.Default}
/>
</>
);
}

cy.mount(<TestComp />);
cy.get('[data-column-id="name"]').invoke('outerWidth').should('be.gt', 150).as('initialWidth');

// resize first column
cy.get('[data-component-name="AnalyticalTableResizer"]')
.eq(0)
.realMouseDown()
.realMouseMove(-50, 0, { scrollBehavior: false });
cy.get('body').realMouseUp();
cy.get('@initialWidth').then((initialWidth) => {
cy.get('[data-column-id="name"]').invoke('outerWidth').should('not.eq', initialWidth);
});

cy.get('[data-testid="switch"]').click();
cy.get('[data-column-id="product"]').invoke('outerWidth').should('be.gt', 150);
cy.get('[data-column-id="price"]').invoke('outerWidth').should('be.gt', 150);
cy.get('[data-column-id="qty"]').invoke('outerWidth').should('be.gt', 150);
});

cypressPassThroughTestsFactory(AnalyticalTable, { data, columns });
});

Expand Down
55 changes: 42 additions & 13 deletions packages/main/src/components/AnalyticalTable/docs/FAQ.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,42 +158,71 @@ For other elements or components, we recommend either disabling event propagatio

## How to stop the table state from automatically resetting when the data changes?

By default, the `AnalyticalTable` will reset the sorters, filters, grouping, selected rows, etc. when the table data changes. It will also reset all manually resized columns if the container width changes.
In case you want to keep the current state of the Table, you can disable this behavior by using the `reactTableOptions` prop or for column resize the `retainColumnWidth` prop.
By default, the `AnalyticalTable` resets most table state whenever the `data` or `columns` reference changes.

### What resets when?

Each `autoReset*` option defaults to `true`. Most trigger on **`data`** changes, but `autoResetResize` is the exception - it triggers on **`columns`** changes.

<details>
<summary>Full list of <code>autoReset*</code> options</summary>

| `reactTableOptions` option | Default | Resets on | What it resets |
| -------------------------- | ------- | ---------------- | ------------------------------------------- |
| `autoResetSelectedRows` | `true` | `data` change | Row selection (`selectedRowIds`) |
| `autoResetSortBy` | `true` | `data` change | Sort state (unless `manualSortBy`) |
| `autoResetFilters` | `true` | `data` change | Filter state (unless `manualFilters`) |
| `autoResetGlobalFilter` | `true` | `data` change | Global filter (unless `manualGlobalFilter`) |
| `autoResetGroupBy` | `true` | `data` change | Grouping state (unless `manualGroupBy`) |
| `autoResetExpanded` | `true` | `data` change | Expanded rows |
| `autoResetHiddenColumns` | `true` | `data` change | Hidden columns |
| `autoResetResize` | `true` | `columns` change | User-resized column widths |

</details>

**Important:** Column resize widths are never reset by `data` changes - only by `columns` changes. If you want to preserve user-resized widths across column changes, set `autoResetResize: false`.

### `retainColumnWidth` prop

`retainColumnWidth` is a separate mechanism from `autoResetResize`. It controls what happens on **container resizes** (e.g., window resize):

- **Without** `retainColumnWidth`: A container resize clears user-resized column widths and triggers dynamic width recalculation.
- **With** `retainColumnWidth`: User-resized column widths are preserved across container resizes.

### Keeping state across data updates

To prevent automatic resets when updating data, set the corresponding `autoReset*` option to `false` in `reactTableOptions`. The ref-based flag shown below is optional - it avoids an unnecessary re-render by reading the flag during render instead of toggling state:

```jsx
const [data, setData] = React.useState([])
const skipPageResetRef = React.useRef(false)
const [data, setData] = useState([])
const skipPageResetRef = useRef(false)

const updateData = newData => {
// When data gets updated with this function, set a flag
// to disable all of the auto resetting
// When data gets updated with this function, set a flag to disable all of the auto resetting
skipPageResetRef.current = true

setData(newData)
}

React.useEffect(() => {
// After the table has updated, always remove the flag
useEffect(() => {
// After the table has updated, remove the flag
skipPageResetRef.current = false
})
<AnalyticalTable
columns={columns}
data={data}
// disable auto reset of columns width if a column has been manually resized
// preserve user-resized widths across container resizes
retainColumnWidth
// react-table options
reactTableOptions={{
// ... any other options you want to set
autoResetHiddenColumns: !skipPageResetRef.current,
autoResetPage: !skipPageResetRef.current,
autoResetExpanded: !skipPageResetRef.current,
autoResetGroupBy: !skipPageResetRef.current,
autoResetSelectedRows: !skipPageResetRef.current,
autoResetSortBy: !skipPageResetRef.current,
autoResetFilters: !skipPageResetRef.current,
autoResetRowState: !skipPageResetRef.current,
autoResetResize: !skipPageResetRef.current
// resets on `columns` changes, not `data`
autoResetResize: false,
}}
/>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ export const stateReducer: TableInstance['stateReducer'] = (state, action, _prev
return { ...state, subComponentsHeight: payload };
case 'TABLE_COL_RESIZED':
return { ...state, tableColResized: payload };
case 'resetResize':
// reset `tableColResized` state, when react-table resize state is reset
return { ...state, tableColResized: undefined };
case 'ROW_COLLAPSED_FLAG':
return { ...state, rowCollapsed: payload };
case 'COLUMN_DND_START':
Expand Down
Loading