From 09c5d1ec54e0589787c4b8049ac68c7669b81d43 Mon Sep 17 00:00:00 2001 From: Joris Wouter Jonkers <74975850+ExtraToast@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:41:55 +0200 Subject: [PATCH] fix(association): a figure counted as none comes off the band The membership page read `0 Events in the past year` wherever the api counted none. A page selling the association is the last place to print a zero. The published floor is not the answer either: a floor stands for a number nobody has counted yet, and a counted zero has been counted. The figure comes off the band instead and the ones either side of it close up, so the band draws what it is given rather than a fixed four. --- .../domains/association/island/NumberBand.vue | 9 +++++---- .../src/domains/association/numbers.ts | 11 +++++++++-- .../unit/domains/association/numbers.test.ts | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/services/frontend/src/domains/association/island/NumberBand.vue b/services/frontend/src/domains/association/island/NumberBand.vue index 4afd266a7..e3aa5c9da 100644 --- a/services/frontend/src/domains/association/island/NumberBand.vue +++ b/services/frontend/src/domains/association/island/NumberBand.vue @@ -2,12 +2,13 @@ import {figureText, type Figure} from "../numbers" /** - * The association in four numbers, across the page. + * The association in a handful of numbers, across the page. * * Every figure is drawn the moment the band is: the ones the api counts start on the floors the - * association publishes about itself and are replaced where the counts land, so the band never - * changes height and there is nothing pulsing on the page that is meant to sell membership. A - * figure that is a floor rather than a count says so with a `+`. + * association publishes about itself and are replaced where the counts land, so there is + * nothing pulsing on the page that is meant to sell membership. A figure that is a floor rather + * than a count says so with a `+`. Usually four; a figure the api counted as none is not on the + * band at all, so the band draws what it is given rather than a fixed set. */ defineOptions({name: "NumberBand"}) diff --git a/services/frontend/src/domains/association/numbers.ts b/services/frontend/src/domains/association/numbers.ts index bccfc916b..9ce188f41 100644 --- a/services/frontend/src/domains/association/numbers.ts +++ b/services/frontend/src/domains/association/numbers.ts @@ -84,9 +84,16 @@ const counted = ( label: string, ): Figure => ({id, value: value ?? floor, exact: numbers != null, label}) -/** The figures a page names, in the order it names them. */ +/** + * The figures a page names, in the order it names them, less any the api counted as none. + * + * A page that sells the association does not print `0 Events in the past year`. Nor does it + * fall back to the published floor there: the floor stands for a number nobody has counted yet, + * and a counted zero has been counted. The figure comes off the band, and the ones either side + * of it close up. + */ export function figuresFor(ids: readonly FigureId[], numbers: AssociationNumbers | null): Figure[] { - return ids.map(id => FIGURES[id](numbers)) + return ids.map(id => FIGURES[id](numbers)).filter(figure => !(figure.exact && figure.value === 0)) } /** What the membership page leads with. */ diff --git a/services/frontend/tests/unit/domains/association/numbers.test.ts b/services/frontend/tests/unit/domains/association/numbers.test.ts index 1cf25cb62..f4f834cb9 100644 --- a/services/frontend/tests/unit/domains/association/numbers.test.ts +++ b/services/frontend/tests/unit/domains/association/numbers.test.ts @@ -1,6 +1,7 @@ import {describe, expect, it} from "vitest" import { associationFigures, + figuresFor, figureText, FLOORS, MEMBERS_CLAIMED, @@ -45,6 +46,24 @@ describe("associationFigures", () => { }) describe("figureText", () => { + /** + * A page that sells the association does not print `0 Events in the past year`. + * + * Nor does it fall back to the published floor there: the floor stands for a number nobody + * has counted yet, and a counted zero has been counted. + */ + it("leaves out a figure the api counted as none", () => { + const none = figuresFor(["members", "teams", "events"], {...counted, eventsLastYear: 0}) + + expect(none.map(figure => figure.id)).toEqual(["members", "teams"]) + }) + + it("keeps a figure nobody has counted yet, on its floor", () => { + const unread = figuresFor(["events"], null) + + expect(unread).toMatchObject([{id: "events", value: FLOORS.eventsLastYear, exact: false}]) + }) + it("marks a floor as at least that many and states a count plainly", () => { expect(figureText({id: "members", value: 200, exact: false, label: ""})).toBe("200+") expect(figureText({id: "teams", value: 13, exact: true, label: ""})).toBe("13")