Skip to content

Commit 5027b89

Browse files
authored
fix(AnalyticalTable): recalc column widths after columns change with retainColumnWidth (#8487)
1 parent ffa95fa commit 5027b89

4 files changed

Lines changed: 101 additions & 14 deletions

File tree

packages/main/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ useEffect(() => {
446446
<AnalyticalTable
447447
data={data}
448448
columns={columns}
449-
retainColumnWidth // prevent column width reset
449+
retainColumnWidth // prevent column width reset on container resize
450450
reactTableOptions={{
451451
autoResetSelectedRows: !skipPageResetRef.current,
452452
autoResetSortBy: !skipPageResetRef.current,

packages/main/src/components/AnalyticalTable/AnalyticalTable.cy.tsx

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4825,6 +4825,61 @@ describe('AnalyticalTable', () => {
48254825
cy.get('[data-component-name="AnalyticalTableBody"]').should('have.prop', 'scrollTop', 2500);
48264826
});
48274827

4828+
it('retainColumnWidth: recalculates widths after columns change', () => {
4829+
const columnsA = [
4830+
{ Header: 'Name', accessor: 'name' },
4831+
{ Header: 'Age', accessor: 'age' },
4832+
];
4833+
const columnsB = [
4834+
{ Header: 'Product', accessor: 'product' },
4835+
{ Header: 'Price', accessor: 'price' },
4836+
{ Header: 'Qty', accessor: 'qty' },
4837+
];
4838+
const dataA = [
4839+
{ name: 'Alice', age: 30 },
4840+
{ name: 'Bob', age: 25 },
4841+
];
4842+
const dataB = [
4843+
{ product: 'Widget', price: '$10', qty: 5 },
4844+
{ product: 'Gadget', price: '$20', qty: 3 },
4845+
];
4846+
4847+
function TestComp() {
4848+
const [useB, setUseB] = useState(false);
4849+
return (
4850+
<>
4851+
<Button data-testid="switch" onClick={() => setUseB((prev) => !prev)}>
4852+
Switch
4853+
</Button>
4854+
<AnalyticalTable
4855+
columns={useB ? columnsB : columnsA}
4856+
data={useB ? dataB : dataA}
4857+
retainColumnWidth
4858+
scaleWidthMode={AnalyticalTableScaleWidthMode.Default}
4859+
/>
4860+
</>
4861+
);
4862+
}
4863+
4864+
cy.mount(<TestComp />);
4865+
cy.get('[data-column-id="name"]').invoke('outerWidth').should('be.gt', 150).as('initialWidth');
4866+
4867+
// resize first column
4868+
cy.get('[data-component-name="AnalyticalTableResizer"]')
4869+
.eq(0)
4870+
.realMouseDown()
4871+
.realMouseMove(-50, 0, { scrollBehavior: false });
4872+
cy.get('body').realMouseUp();
4873+
cy.get('@initialWidth').then((initialWidth) => {
4874+
cy.get('[data-column-id="name"]').invoke('outerWidth').should('not.eq', initialWidth);
4875+
});
4876+
4877+
cy.get('[data-testid="switch"]').click();
4878+
cy.get('[data-column-id="product"]').invoke('outerWidth').should('be.gt', 150);
4879+
cy.get('[data-column-id="price"]').invoke('outerWidth').should('be.gt', 150);
4880+
cy.get('[data-column-id="qty"]').invoke('outerWidth').should('be.gt', 150);
4881+
});
4882+
48284883
cypressPassThroughTestsFactory(AnalyticalTable, { data, columns });
48294884
});
48304885

packages/main/src/components/AnalyticalTable/docs/FAQ.mdx

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -158,42 +158,71 @@ For other elements or components, we recommend either disabling event propagatio
158158

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

161-
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.
162-
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.
161+
By default, the `AnalyticalTable` resets most table state whenever the `data` or `columns` reference changes.
162+
163+
### What resets when?
164+
165+
Each `autoReset*` option defaults to `true`. Most trigger on **`data`** changes, but `autoResetResize` is the exception - it triggers on **`columns`** changes.
166+
167+
<details>
168+
<summary>Full list of <code>autoReset*</code> options</summary>
169+
170+
| `reactTableOptions` option | Default | Resets on | What it resets |
171+
| -------------------------- | ------- | ---------------- | ------------------------------------------- |
172+
| `autoResetSelectedRows` | `true` | `data` change | Row selection (`selectedRowIds`) |
173+
| `autoResetSortBy` | `true` | `data` change | Sort state (unless `manualSortBy`) |
174+
| `autoResetFilters` | `true` | `data` change | Filter state (unless `manualFilters`) |
175+
| `autoResetGlobalFilter` | `true` | `data` change | Global filter (unless `manualGlobalFilter`) |
176+
| `autoResetGroupBy` | `true` | `data` change | Grouping state (unless `manualGroupBy`) |
177+
| `autoResetExpanded` | `true` | `data` change | Expanded rows |
178+
| `autoResetHiddenColumns` | `true` | `data` change | Hidden columns |
179+
| `autoResetResize` | `true` | `columns` change | User-resized column widths |
180+
181+
</details>
182+
183+
**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`.
184+
185+
### `retainColumnWidth` prop
186+
187+
`retainColumnWidth` is a separate mechanism from `autoResetResize`. It controls what happens on **container resizes** (e.g., window resize):
188+
189+
- **Without** `retainColumnWidth`: A container resize clears user-resized column widths and triggers dynamic width recalculation.
190+
- **With** `retainColumnWidth`: User-resized column widths are preserved across container resizes.
191+
192+
### Keeping state across data updates
193+
194+
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:
163195

164196
```jsx
165-
const [data, setData] = React.useState([])
166-
const skipPageResetRef = React.useRef(false)
197+
const [data, setData] = useState([])
198+
const skipPageResetRef = useRef(false)
167199

168200
const updateData = newData => {
169-
// When data gets updated with this function, set a flag
170-
// to disable all of the auto resetting
201+
// When data gets updated with this function, set a flag to disable all of the auto resetting
171202
skipPageResetRef.current = true
172203

173204
setData(newData)
174205
}
175206

176-
React.useEffect(() => {
177-
// After the table has updated, always remove the flag
207+
useEffect(() => {
208+
// After the table has updated, remove the flag
178209
skipPageResetRef.current = false
179210
})
180211
<AnalyticalTable
181212
columns={columns}
182213
data={data}
183-
// disable auto reset of columns width if a column has been manually resized
214+
// preserve user-resized widths across container resizes
184215
retainColumnWidth
185-
// react-table options
186216
reactTableOptions={{
187217
// ... any other options you want to set
188218
autoResetHiddenColumns: !skipPageResetRef.current,
189-
autoResetPage: !skipPageResetRef.current,
190219
autoResetExpanded: !skipPageResetRef.current,
191220
autoResetGroupBy: !skipPageResetRef.current,
192221
autoResetSelectedRows: !skipPageResetRef.current,
193222
autoResetSortBy: !skipPageResetRef.current,
194223
autoResetFilters: !skipPageResetRef.current,
195-
autoResetRowState: !skipPageResetRef.current,
196-
autoResetResize: !skipPageResetRef.current
224+
// resets on `columns` changes, not `data`
225+
autoResetResize: false,
197226
}}
198227
/>
199228
```

packages/main/src/components/AnalyticalTable/tableReducer/stateReducer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ export const stateReducer: TableInstance['stateReducer'] = (state, action, _prev
6767
return { ...state, subComponentsHeight: payload };
6868
case 'TABLE_COL_RESIZED':
6969
return { ...state, tableColResized: payload };
70+
case 'resetResize':
71+
// reset `tableColResized` state, when react-table resize state is reset
72+
return { ...state, tableColResized: undefined };
7073
case 'ROW_COLLAPSED_FLAG':
7174
return { ...state, rowCollapsed: payload };
7275
case 'COLUMN_DND_START':

0 commit comments

Comments
 (0)