feat(example-showcase): plant the inline-grid time real-machine fixture on showcase_invoice_line - #8655
Conversation
…ture on showcase_invoice_line (#6533) objectui#3569 split `date` / `datetime` / `time` into three distinct inline-grid controls. The `datetime` half got a long-lived showcase fixture (`showcase_expense_line.incurred_at`); the `time` half got only unit tests, so no running app has ever exercised the grid's time control. Adds `showcase_invoice_line.service_start` (type `time`, seeded `09:15` on the T&M service lines) and — crucially — declares the grid's `inlineColumns` explicitly. The explicit declaration is the point, not decoration. This line had exactly six editable fields, i.e. exactly `DEFAULT_MAX_INLINE_COLUMNS`. A seventh column makes auto-derivation curate, and the column that loses the tie-break is `receipt` (a `file` column, unranked in the fill-priority table, so it sorts last) — objectui#2360's upload-in-grid fixture. Demoting it into the column chooser is exactly the cost that kept `time` out of objectui#3569. Declaring the set routes through `hydrateColumns`, which never sets `defaultHidden`, so all seven columns stay default-visible and `receipt` keeps its visibility. Measured in the running app (showcase on a private port + DB, console built at the pinned .objectui-sha): before: Product | Description | Qty | Unit Price | Receipt | Amount after: Product | Description | Service Start | Qty | Unit Price | Receipt | Amount `Service Start` renders as a native time control; the seeded 09:15 reads back as 09:15 AM, and a value typed into an empty cell survived save + reopen. `service_start` is translated at birth (en + zh-CN) and only that field, because check-i18n-coverage freezes the example's untranslated count in both directions — the same rule the `incurred_at` fixture documented. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz
…wcase-time-grid-fixture
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
ACCEPTed on substance — held as draft. ⛔ Not flipped, not enqueued, not armed: blocked repo-wide by the condition filed as #8654 ( ⭐ The budget constraint was proven by ablation, not assertedThis is the part that makes the PR trustworthy. Three real-machine states, columns read off the live grid:
The card's whole reason for existing is that adding a column costs another fixture's default visibility. The ablation reproduces that harm on purpose and then shows the declaration prevents it — so The route choice was costed, not preferredRoute 1 (new shift-line object) was rejected on measurement I did not have when I dispatched: ~8 files (object, index export, seed, permission-set entries or an ADR-0090 grandfather stamp, nav entry, e2e nav list) plus 12–15 new label pairs that must all be translated at birth, because
The real-machine fixture is genuinely realShowcase booted on private ports with its own DBs, console built at the pinned ⇒ This is exactly the thing a unit test cannot supply, and the reason the card refused to accept unit coverage as done. ⭐ A written prediction was falsified, and reported although nothing was wrongThe dev predicted the expense-line control grid would park ⛔ No finding was filed, correctly — nothing is broken. Reporting a falsified prediction that changed no conclusion is the behaviour that makes the predictions which do change conclusions believable. Also noted: the pre-existing #8656 filed: 13 of 49 declared Generated by Claude Code |
Fixes #6533
objectui#3569 split
date/datetime/timeinto three distinct inline-grid controls. Thedatetimehalf got a long-lived showcase fixture (showcase_expense_line.incurred_at) and a real-machine BEFORE/AFTER. Thetimehalf got unit tests only —GridField.test.tsxcovers the control, but no running app has ever rendered atimecolumn inside an inline-edit grid.showcase_field_zoo.f_timeseeds atimevalue, but field_zoo is not a master-detail child, so it never reaches this rendering path.This plants that fixture.
What changed
showcase_invoice_line.service_start— atimefield, seeded'09:15'on the T&M service lines and left empty on the goods lines (the empty-cell case).showcase_invoice_line.invoice.inlineColumns— the grid's column set, declared explicitly.service_starttranslated at birth, en + zh-CN, and only that field.Why the explicit
inlineColumnsis load-bearing, not decorationThis is the constraint the card exists because of, and it was measured rather than assumed.
showcase_invoice_linehad exactly six editable fields — exactlyDEFAULT_MAX_INLINE_COLUMNS. A seventh makes auto-derivation curate: it keeps the primary plus every required column, fills the rest by type usefulness, and marks the overflowdefaultHidden.receiptis afilecolumn, which the fill-priority table does not rank, so it sorts last and loses the tie-break. That column is objectui#2360's upload-in-grid fixture — demoting it is precisely the cost that kepttimeout of objectui#3569.Declaring the set routes through
hydrateColumnsinstead ofderiveColumns(deriveMasterDetail.ts—override.columns?.length ? hydrateColumns(...) : deriveColumns(...)), andhydrateColumnsnever setsdefaultHidden. So all seven columns stay default-visible, andreceipt's visibility stops depending on a tie-break it happens to be losing.Entries are bare
{ field }on purpose:hydrateColumnsfills label, type, options, lookup target,readonlyWhen/requiredWhenand the computedexpressionfrom the schema, so labels stay translatable and the columns cannot drift from the field definitions.Measured in the running app, three states
Showcase booted on a private port with its own DB; console built at the pinned
.objectui-sha(665661ab). Default-visible Line Items columns, read off the live grid:Receiptservice_startwithoutinlineColumns(ablation)service_startwithinlineColumns(this PR)The middle row is the point: the harm is demonstrated, not asserted, and the ablation was predicted in writing before it ran. No existing fixture column loses its default visibility.
The
timecolumn, driven for realService Startrenders as a native time control (clock affordance), not a date control.09:15 AM.09:15:00/13:40:00, goods linesnull.One real-machine fact a unit test could not have shown: the platform stores a
timeasHH:mm:ss, so a seeded'09:15'is at rest as'09:15:00'. That is spec-valid —field-value.zod.tsvalidatesHH:mm[:ss]— no clock information is lost, the grid round-trips whatever is stored, and the user still sees09:15 AM. Worth recording because the widget-level phrase "round-trips HH:mm verbatim" describes the control, not what a real app has on disk.i18n
check-i18n-coveragefreezes this example's untranslated count and fails in both directions. A new declared label must therefore be translated at birth, and only that label — translating the neighbouring line-item fields would push the count below the baseline and fail as an un-ratcheted improvement. Same rule, same shape as theincurred_atfixture, and the reasoning is left in both locale blocks.Verification
pnpm --filter @objectstack/example-showcase testpnpm --filter @objectstack/example-showcase typechecknode scripts/check-type-check-coverage.mjs --re-measurenode scripts/check-i18n-coverage.mjspnpm exec eslint(changed files)node scripts/check-nul-bytes.mjsRe-derived against the actual changed paths with
scripts/pm/dispatch-gates.mjs: no check family names these paths in its own source. The i18n ratchet is not auto-surfaced by that script for an example config, so it was identified fromscripts/i18n-coverage-baseline.jsonand run explicitly.No changeset:
@objectstack/example-showcaseisprivate: true, so this PR releases nothing —skip-changeset.Draft only, and staying draft: a merge freeze is in force while a release runs. Separately,
check-changeset-no-major --self-testis red repo-wide because the release consumed the changeset stock; it is not this PR's doing and was deliberately not touched.Generated by Claude Code