fix(review): thickness band derived from thickness_in (#88); MultiSelect shows selected names + commits on toggle settle (#90)

#88 — thickness band is now display-only, derived deterministically from
thickness_in against the synced Woo thickness terms (TV-9 / QUALITY-95).
Replaces the <select> with a read-only div. New pure function
deriveThicknessTermId + 6 unit tests. Docs (UAT-PRACTICE, contracts/UX)
updated to reflect the band is not an owner control.

#90 — MultiSelect closed state now shows the selected option names
(truncated to 2 + +N) instead of just a count. onCommit prop wired so
multi-select changes persist + re-review immediately (250ms debounce,
flush on Done, cleanup on destroy). Single-select binds use string vars
(speciesSel/edgeSel/gradeSel) to fix the number/string mismatch with
<option value={id}>; a tick()-deferred syncSelects() keeps the DOM
option in sync after programmatic writes (AI prefill, reload hydrate)
that Svelte's own select_option would otherwise reset.
This commit is contained in:
Ty
2026-09-21 09:56:44 -07:00
parent 502314b338
commit bdd03afce4
6 changed files with 244 additions and 32 deletions
@@ -0,0 +1,57 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { deriveThicknessTermId, type ThicknessBand } from './thicknessBand';
// Synced-Woo-shaped bands (id + name). The e2e fixture uses a single "2-4 in" band.
const BANDS: ThicknessBand[] = [
{ id: 39, name: '2-4 in' },
];
test('TV-9 / QUALITY-95: 1 in rounds up into the enclosing synced band', () => {
const bands: ThicknessBand[] = [
{ id: 10, name: '1-2 in' },
{ id: 39, name: '2-4 in' },
];
assert.equal(deriveThicknessTermId(1, bands), 10);
assert.equal(deriveThicknessTermId(1.5, bands), 10);
// 2.0 is exactly the upper bound of the 1-2 band → belongs to it.
assert.equal(deriveThicknessTermId(2.0, bands), 10);
// 2.01 falls into 2-4.
assert.equal(deriveThicknessTermId(2.25, bands), 39);
assert.equal(deriveThicknessTermId(4, bands), 39);
});
test('e2e fixture: thickness_in 2 → the single "2-4 in" band (id 39)', () => {
assert.equal(deriveThicknessTermId(2, BANDS), 39);
});
test('above every band → empty (no invented band; upstream thickness fix resolves it)', () => {
assert.equal(deriveThicknessTermId(5, BANDS), '');
});
test('no thickness / non-finite → empty', () => {
assert.equal(deriveThicknessTermId(null, BANDS), '');
assert.equal(deriveThicknessTermId(undefined, BANDS), '');
assert.equal(deriveThicknessTermId(0, BANDS), '');
assert.equal(deriveThicknessTermId(NaN, BANDS), '');
assert.equal(deriveThicknessTermId(2, []), '');
});
test('band names are parsed from the synced names, never hardcoded (order-independent)', () => {
// Reverse the array: the derivation must still pick the right band by bound.
const bands: ThicknessBand[] = [
{ id: 39, name: '2-4 in' },
{ id: 10, name: '1-2 in' },
];
assert.equal(deriveThicknessTermId(1.5, bands), 10);
assert.equal(deriveThicknessTermId(3, bands), 39);
});
test('malformed band names are skipped without throwing', () => {
const bands: ThicknessBand[] = [
{ id: 1, name: 'N/A' },
{ id: 2, name: '4-2 in' }, // inverted, unusable
{ id: 39, name: '2-4 in' },
];
assert.equal(deriveThicknessTermId(3, bands), 39);
});