From fd6f6a93418ccac7d565e618b0dbacfd229bb791 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 24 May 2026 13:13:12 +0100 Subject: [PATCH] fix(bases): always render kanban NO_VALUE column unless explicitly hidden --- .../hooks/__tests__/use-kanban-groups.spec.ts | 19 ++++++++++++++++++- .../features/base/hooks/use-kanban-groups.ts | 19 +++++++++---------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/apps/client/src/features/base/hooks/__tests__/use-kanban-groups.spec.ts b/apps/client/src/features/base/hooks/__tests__/use-kanban-groups.spec.ts index b47ae1a8f..d14fba2e6 100644 --- a/apps/client/src/features/base/hooks/__tests__/use-kanban-groups.spec.ts +++ b/apps/client/src/features/base/hooks/__tests__/use-kanban-groups.spec.ts @@ -44,6 +44,16 @@ describe("partitionRowsByGroup", () => { expect(result.columns.map((c) => c.key)).toEqual([NO_VALUE_CHOICE_ID, "c1"]); }); + it("hides the NO_VALUE column when hiddenChoiceIds includes the sentinel", () => { + const result = partitionRowsByGroup( + rows, + property, + [NO_VALUE_CHOICE_ID], + undefined, + ); + expect(result.columns.map((c) => c.key)).toEqual(["c1", "c2"]); + }); + it("respects an override choiceOrder", () => { const result = partitionRowsByGroup( rows, @@ -79,7 +89,14 @@ describe("partitionRowsByGroup", () => { undefined, ["c1", "deleted-choice", "c2"], ); - expect(result.columns.map((c) => c.key)).toEqual(["c1", "c2"]); + // 'deleted-choice' is filtered out as stale. NO_VALUE is auto-appended + // (it's never in the property's choices but always rendered unless + // explicitly hidden via hiddenChoiceIds). + expect(result.columns.map((c) => c.key)).toEqual([ + "c1", + "c2", + NO_VALUE_CHOICE_ID, + ]); }); it("returns null columns when groupByPropertyId is unset", () => { diff --git a/apps/client/src/features/base/hooks/use-kanban-groups.ts b/apps/client/src/features/base/hooks/use-kanban-groups.ts index 554fe9bf1..1c55605e0 100644 --- a/apps/client/src/features/base/hooks/use-kanban-groups.ts +++ b/apps/client/src/features/base/hooks/use-kanban-groups.ts @@ -55,19 +55,18 @@ export function partitionRowsByGroup( const valid = new Set([...propertyChoiceIds, NO_VALUE_CHOICE_ID]); const fromOverride = choiceOrderOverride.filter((id) => valid.has(id)); const overrideSet = new Set(fromOverride); + // Always include NO_VALUE somewhere (sentinel for rows with no + // grouping value). If the user listed it in the override, respect + // that position; otherwise append it after the override entries. + const noValueTail = overrideSet.has(NO_VALUE_CHOICE_ID) + ? [] + : [NO_VALUE_CHOICE_ID]; + // Append any property choices the user hasn't placed yet (new + // choices added to the property after this view was saved). const missingChoices = propertyChoiceIds.filter( (id) => !overrideSet.has(id), ); - // Only inject NO_VALUE implicitly when there are newly-discovered choices - // to append — when the override fully covers the current property, leave - // NO_VALUE off unless the user listed it explicitly. - const tail = - missingChoices.length > 0 - ? overrideSet.has(NO_VALUE_CHOICE_ID) - ? missingChoices - : [NO_VALUE_CHOICE_ID, ...missingChoices] - : []; - order = [...fromOverride, ...tail]; + order = [...fromOverride, ...noValueTail, ...missingChoices]; } else { order = [NO_VALUE_CHOICE_ID, ...propertyChoiceIds]; }