fix(bases): always render kanban NO_VALUE column unless explicitly hidden

This commit is contained in:
Philipinho
2026-05-24 13:13:12 +01:00
parent a60de83e57
commit fd6f6a9341
2 changed files with 27 additions and 11 deletions
@@ -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", () => {
@@ -55,19 +55,18 @@ export function partitionRowsByGroup(
const valid = new Set<string>([...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];
}