fix(filters): address the final review findings (#188)
Restores the escape hatch for a status list matching no preset. filtersFromSearchParams accepts any non-empty subset of {available, reserved, sold}, and only three of those seven lists are presets; the other four reported as the not-sold fallback and produced no chip, so ?status=reserved was an empty grid reading "No items yet - check back soon" with no Clear filters button and no way out but editing the URL. hasActiveFilters covered all seven before this branch, so that was a regression against main. availabilityDimension now emits a chip for any status that is not the not-sold preset, labelled from SALE_STATE_LABELS when the list matches one and from statusLabel when it does not. The Segmented still reads "Not sold" beside such a chip, which is a cosmetic wart and the cheaper half of the trade.
Runs the frontend unit suite in CI. The 22 tests added over the dimensions were invoked by nothing: the workflow's only frontend steps were the build and the end-to-end run, and its test:unit:cov step is the backend's. The new step is guarded and named in the gate like every other suite, per the invariant workflowGate.test.ts asserts.
Restores the comment explaining why availabilityDimension's render and chips pass different fallbacks to saleStateFromStatuses. The control must read "All" while sold favorites are on screen; chips must stay silent because the customer never chose it. Unifying them would give every signed-in favorites view a phantom All chip and a tally of 2, and nothing said so after the old markup was deleted.
Derives the storefront's "is anything filtered" and FilterBar's tally through one exported chipsFor rather than two expressions over two identically-built contexts. They agreed only by convention, which is the exact defect this branch exists to remove.
Documents that statusDimension and availabilityDimension are alternatives over one field, since composing both type-checks and would render two controls that double-count it, and asserts the price chip's label text, which was the only chip label nothing checked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,6 +105,14 @@ describe('priceDimension', () => {
|
||||
expect(priceDimension.chips(contextFor({ maxPriceCents: 5000 }).ctx)).toHaveLength(1);
|
||||
});
|
||||
|
||||
// formatPriceRange's real output, en dash and all. The only chip whose label
|
||||
// text nothing asserted on, which is how a chip reading "$1000–$5000" — cents
|
||||
// shown as dollars — would have reached a customer unnoticed.
|
||||
it('labels the chip with the formatted range', () => {
|
||||
const { ctx } = contextFor({ minPriceCents: 1000, maxPriceCents: 5000 });
|
||||
expect(priceDimension.chips(ctx)[0]?.label).toBe('$10–$50');
|
||||
});
|
||||
|
||||
it('clears both ends when removed', () => {
|
||||
const { ctx, state } = contextFor({ minPriceCents: 1000, maxPriceCents: 5000 });
|
||||
priceDimension.chips(ctx)[0]?.onRemove();
|
||||
@@ -188,4 +196,30 @@ describe('availabilityDimension', () => {
|
||||
availabilityDimension.chips(ctx)[0]?.onRemove();
|
||||
expect(state.latest?.status).toBeNull();
|
||||
});
|
||||
|
||||
// filtersFromSearchParams accepts any non-empty subset of the three public
|
||||
// statuses — seven lists, of which only three are presets. The other four
|
||||
// report as the 'not-sold' fallback, so a chip keyed off the preset alone
|
||||
// leaves ?status=reserved as an empty grid reading "No items yet" with no
|
||||
// Clear filters button. hasActiveFilters covered all seven before #188; this
|
||||
// is what replaces it.
|
||||
it('reports one chip for a status list matching no preset', () => {
|
||||
const chips = availabilityDimension.chips(contextFor({ status: ['reserved'] }).ctx);
|
||||
expect(chips.map((c) => c.label)).toEqual(['Reserved']);
|
||||
|
||||
// Several statuses read as a list rather than as a preset's name.
|
||||
expect(
|
||||
availabilityDimension
|
||||
.chips(contextFor({ status: ['available', 'sold'] }).ctx)
|
||||
.map((c) => c.label)
|
||||
).toEqual(['Available, Sold']);
|
||||
});
|
||||
|
||||
it('clears the filter when a chip for no preset is removed', () => {
|
||||
const { ctx, state } = contextFor({ status: ['reserved', 'sold'] });
|
||||
const chips = availabilityDimension.chips(ctx);
|
||||
expect(chips).toHaveLength(1);
|
||||
chips[0]?.onRemove();
|
||||
expect(state.latest?.status).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user