From 87ec7cec07c205f68e952e081637c7d9b7493b1c Mon Sep 17 00:00:00 2001 From: Nathnael Date: Thu, 20 Aug 2026 08:25:47 +0000 Subject: [PATCH] fix(export-ui): let field groups be expanded and collapsed by hand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The accordion's open state was derived from the selection on every render, which made it fully controlled with nothing driving it. Clicking a group that had no fields selected opened it for one render and the recomputed value immediately shut it again, so such a group could only be opened by selecting something inside it — and conversely a group with a selection could not be collapsed at all. Open state is now real state with an onChange, seeded from the fields marked default rather than the live selection, so clearing every field doesn't close the groups underneath the user. Search still force-opens every group holding a match, but only as a display override — the manual state survives and returns when the search clears. --- .../src/components/export/ExportDialog.tsx | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/components/export/ExportDialog.tsx b/apps/edr-freight-web/backoffice/src/components/export/ExportDialog.tsx index 195d05f08..6c7af128c 100644 --- a/apps/edr-freight-web/backoffice/src/components/export/ExportDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/components/export/ExportDialog.tsx @@ -105,14 +105,22 @@ export function ExportDialog({ opened, onClose, dataset, params }: ExportDialogP return out; }, [dataset.fields, dataset.groups, search]); - // Searching force-expands so matches aren't hidden inside collapsed groups. - // Otherwise open only groups that already have something selected, which is - // what keeps 77 fields tractable on open. - const openGroups = search.trim() - ? [...visibleByGroup.keys()] - : dataset.groups - .filter((g) => dataset.fields.some((f) => f.group === g.id && selectedSet.has(f.key))) - .map((g) => g.id); + // Which groups are expanded. Real state, NOT derived from the selection: + // deriving it made the accordion fully controlled with no way to change it, + // so clicking a group that had nothing selected re-collapsed on the next + // render and the group could only be opened by selecting a field in it. + // Seeded from `default` (not the live selection) so clearing every field + // doesn't slam the open groups shut underneath the user. + const [expanded, setExpanded] = useState(() => + dataset.groups + .filter((g) => dataset.fields.some((f) => f.group === g.id && f.default)) + .map((g) => g.id), + ); + + // Searching force-opens every group holding a match, so a hit can't hide + // inside a collapsed section. It only overrides what is displayed — the + // user's own expand state is untouched and returns when the search clears. + const openGroups = search.trim() ? [...visibleByGroup.keys()] : expanded; const toggleField = (key: string) => setSelected((prev) => (prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key])); @@ -264,7 +272,13 @@ export function ExportDialog({ opened, onClose, dataset, params }: ExportDialogP - + {dataset.groups.map((group) => { const fields = visibleByGroup.get(group.id); if (!fields) return null;