Skip to content

Commit 344746a

Browse files
author
Quentin Decré
committed
fix(DatagridConfigurable): better detect and handle columns changes
1 parent 6472e3a commit 344746a

File tree

1 file changed

+83
-12
lines changed

1 file changed

+83
-12
lines changed

packages/ra-ui-materialui/src/list/datagrid/DatagridConfigurable.tsx

+83-12
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import * as React from 'react';
21
import {
3-
useResourceContext,
42
usePreference,
3+
useResourceContext,
54
useStore,
65
useTranslate,
76
} from 'ra-core';
7+
import * as React from 'react';
88

99
import { Configurable } from '../../preferences';
1010
import { Datagrid, DatagridProps } from './Datagrid';
@@ -50,6 +50,11 @@ export const DatagridConfigurable = ({
5050
ConfigurableDatagridColumn[]
5151
>(`preferences.${finalPreferenceKey}.availableColumns`, []);
5252

53+
const [columns, setColumns] = useStore<string[]>(
54+
`preferences.${finalPreferenceKey}.columns`,
55+
[]
56+
);
57+
5358
// eslint-disable-next-line @typescript-eslint/no-unused-vars
5459
const [_, setOmit] = useStore<string[] | undefined>(
5560
`preferences.${finalPreferenceKey}.omit`,
@@ -58,7 +63,7 @@ export const DatagridConfigurable = ({
5863

5964
React.useEffect(() => {
6065
// first render, or the preference have been cleared
61-
const columns = React.Children.toArray(props.children)
66+
const newAvailableColumns = React.Children.toArray(props.children)
6267
.filter(child => React.isValidElement(child))
6368
.map((child: React.ReactElement, index) => ({
6469
index: String(index),
@@ -67,16 +72,82 @@ export const DatagridConfigurable = ({
6772
child.props.label && typeof child.props.label === 'string' // this list is serializable, so we can't store ReactElement in it
6873
? child.props.label
6974
: child.props.source
70-
? // force the label to be the source
71-
undefined
72-
: // no source or label, generate a label
73-
translate('ra.configurable.Datagrid.unlabeled', {
74-
column: index,
75-
_: `Unlabeled column #%{column}`,
76-
}),
75+
? // force the label to be the source
76+
undefined
77+
: // no source or label, generate a label
78+
translate('ra.configurable.Datagrid.unlabeled', {
79+
column: index,
80+
_: `Unlabeled column #%{column}`,
81+
}),
7782
}));
78-
if (columns.length !== availableColumns.length) {
79-
setAvailableColumns(columns);
83+
const hasChanged = newAvailableColumns.some(column => {
84+
const availableColumn = availableColumns.find(
85+
availableColumn =>
86+
(!!availableColumn.source &&
87+
availableColumn.source === column?.source) ||
88+
(!!availableColumn.label &&
89+
availableColumn.label === column?.label)
90+
);
91+
return !availableColumn || availableColumn.index !== column.index;
92+
});
93+
if (hasChanged) {
94+
// first we need to update the columns indexes to match the new availableColumns so we keep the same order
95+
const newColumnsSortedAsOldColumns = columns.flatMap(column => {
96+
const oldColumn = availableColumns.find(
97+
availableColumn => availableColumn.index === column
98+
);
99+
const newColumn = newAvailableColumns.find(
100+
availableColumn =>
101+
(!!availableColumn.source &&
102+
availableColumn.source === oldColumn?.source) ||
103+
(!!availableColumn.label &&
104+
availableColumn.label === oldColumn?.label)
105+
);
106+
return newColumn?.index ? [newColumn.index] : [];
107+
});
108+
setColumns([
109+
// we add the old columns in the same order as before
110+
...newColumnsSortedAsOldColumns,
111+
// then we add at the new columns which are not omited
112+
...newAvailableColumns
113+
.filter(
114+
c =>
115+
!availableColumns.some(
116+
ac =>
117+
(!!ac.source && ac.source === c.source) ||
118+
(!!ac.label && ac.label === c.label)
119+
) && !omit?.includes(c.source as string)
120+
)
121+
.map(c => c.index),
122+
]);
123+
124+
// Then we update the available columns to include the new columns while keeping the same order as before
125+
const newAvailableColumnsSortedAsBefore = [
126+
// First the existing columns, in the same order
127+
...(availableColumns
128+
.map(oldAvailableColumn =>
129+
newAvailableColumns.find(
130+
c =>
131+
(!!c.source &&
132+
c.source === oldAvailableColumn.source) ||
133+
(!!c.label &&
134+
c.label === oldAvailableColumn.label)
135+
)
136+
)
137+
.filter(c => !!c) as ConfigurableDatagridColumn[]), // Remove undefined columns
138+
// Then the new columns
139+
...newAvailableColumns.filter(
140+
c =>
141+
!availableColumns.some(
142+
oldAvailableColumn =>
143+
(!!oldAvailableColumn.source &&
144+
oldAvailableColumn.source === c.source) ||
145+
(!!oldAvailableColumn.label &&
146+
oldAvailableColumn.label === c.label)
147+
)
148+
),
149+
];
150+
setAvailableColumns(newAvailableColumnsSortedAsBefore);
80151
setOmit(omit);
81152
}
82153
}, [availableColumns]); // eslint-disable-line react-hooks/exhaustive-deps

0 commit comments

Comments
 (0)