Repository navigation
feat(plugin-gantt): refresh in place on a real query change; the initial load keeps the placeholder (objectui#7237) - #10539
Conversation
The A-prime half of ruling C+A-prime on objectui#7237. Once the chart has painted, a real sort, filter or permissions change must keep GanttView mounted, show a visible refreshing state on the chart while the query runs, and replace the rows when it returns; the initial load keeps the loading placeholder. Red on the base: the re-query unmounts GanttView through the placeholder and nothing marks the refresh. Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN Co-authored-by: Claude <noreply@anthropic.com>
The A-prime half of ruling C+A-prime on objectui#7237. The fetch effect ran every reload non-silently, so a sort, filter or permissions change on a painted chart flipped `loading` and the early return swapped GanttView for the placeholder, losing scroll, collapsed groups and in-flight edits. `reload` now takes two independent options: `inPlace` (keep the chart mounted, set `refreshing`) and `silent` (a failure keeps the last good rows; implies in place). The fetch effect runs the initial load with the placeholder as before, and every run after rows have committed in place, not silent: a changed query that fails is still reported. The chart pane draws the shared RefreshIndicator while `refreshing` is set, named by the new `gantt.aria.refreshing` key in all ten locale packs. The objectui#7231 guard test generated its overlap from a post-paint filter change, which no longer holds `loading`; its pairs are regenerated from the ones that still put each flag in play, with every guard assertion kept and a post-paint `refreshing` twin added. Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: ① Derived judgmentsHead confirmed. A′ as ruled (5749198903), read against the diff:
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #7237
Clause-②: yes — published component behaviour changes: a real query change no longer tears the chart down to a placeholder.
This is the A′ half of ruling C+A′ (comment 5749198903, maintainer 「同意」). The C half landed as objectui#10407. This is the last half, hence line 1 above. Dispatched by the
domain:uiseat 2 PM (claim 5828052135), sessionsession_014mXUNuFomfj24w7s1pZzhN. The dispatch ran asmode:subagent, so that id is also this run's.What changes
ObjectGantt's fetch effect ran every reload non-silently. Once the chart had painted, a real change to the query (sort, filter or permissions) flippedloading. Theif (loading)early return then swappedGanttViewfor the "Loading Gantt chart..." placeholder, and the chart lost its scroll position, collapsed groups and in-flight edits.reloadtakes two independent options.inPlacekeeps the chart mounted and setsrefreshing.silentlogs a failure and keeps the last good rows, and impliesinPlace. Every existingsilent: truecaller behaves as before: the toolbar refresh, the four write-readbacks and the invalidation-bus reload.loadedOnceRefflips once, when a reload first commits rows. The fetch effect runs the initial load exactly as before, with the placeholder. Every later run is in place but not silent: a changed query that fails is still reported as an error, as on base. It does not leave the previous query's rows on screen with nothing marking them stale.RefreshIndicatorfrom@object-ui/componentswhilerefreshingis set. It is the same indeterminate bar thatObjectGrid,ListViewandObjectChartdraw over their rows, so there is no second convention. The pane gainsrelativeto anchor the bar. Every absolute element insideGanttViewandResourceWorkloadalready has a positioned ancestor of its own.gantt.aria.refreshing, inGANTT_DEFAULT_TRANSLATIONSand in all ten locale packs. Each value is copied from that pack's existinglist.refreshing, so no new translation was authored. The component's default English label would repeat the objectui#3546 class: an English accessible name in every language.reload's list and the fetch effect's list are byte-identical to base.refreshingtwin is added. The file header records why. Leg 2 and Leg 3 below show each case still fails when the guard is removed or narrowed to its own mode.Files (15, +502/−48):
packages/plugin-gantt/src/ObjectGantt.tsx,packages/plugin-gantt/src/useGanttTranslation.ts, newpackages/plugin-gantt/src/ObjectGantt.refreshInPlace-7237.test.tsx,packages/plugin-gantt/src/ObjectGantt.staleReloadFinally-7231.test.tsx,packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts(one line each), and.changeset/7237-gantt-refresh-in-place.md(@object-ui/plugin-ganttminor,@object-ui/i18npatch).Dispatch hypotheses, measured
origin/mainefbf18de5, which is after objectui#10494. The file already hadrefreshing,reload({ silent }), the sequence guard and the silent invalidation reload. The fetch effect calledreloadRef.current()non-silently. Measured on the base: a sort, filter or permissions change unmountsGanttView(the pins below).refreshingonly disabled the toolbar refresh button and setaria-busyon it. That is a dimmed icon with no animation, and it is absent entirely when there is noonRefresh(the inlinevalueprovider). So this reuses the repo's existingRefreshIndicatorand the existingrefreshingflag. No visible string is added, only the accessible name.reload({ silent: true }). The re-query is in place but not silent, because silent would keep the previous query's rows on a failure with nothing marking them stale. Pinned: Leg 4 below reds exactly that pin.ObjectGantt's record fetch still listsschema.filterandschema.sortby identity.ListView's filter hold shields it today. Neither dependency list was widened or narrowed.Pins, red on the base, green on the head
ObjectGantt.refreshInPlace-7237.test.tsx.GanttViewis a stand-in with a once-per-mount instance id, and everyfindis held open by hand. Cases:Sort, filter and permissions (through a real
PermissionProvider), each asserting:GanttViewinstance while the query is in flight and after it returns;The re-query carries the changed
$orderby.(d) Control: the initial load shows the placeholder and no refreshing state.
A failed re-query is reported, not hidden behind the previous rows.
The zh pack labels the bar
刷新中….Base (pin commit
68e742a16, base source):Tests 4 failed | 3 passed (7). Readings:(a) GanttView was unmounted by the re-query …: expected 'unmounted' to be '1'(b) no visible refreshing state on the chart …: expected 'absent' to be 'present'the re-query tore the chart down to the loading placeholder: expected 'Loading Gantt chart...' to be 'no placeholder'(a) after the answer: not the GanttView that painted the first rows: expected '2' to be '1'expected undefined to be '刷新中…'The (d) control, the re-query-carries-the-query case and the failure case are green on base, as expected.
Head
c1041d311: all 11 cases green across both files (Leg 5).Reverse run and mutations
All from the committed head
c1041d311. The two test files run with--reporter=verbose. Every leg restores from HEAD under a trap: blobcdd9bca99equals HEAD, andgit diff HEADis empty. Legs 2 to 4 went throughablation-replace.mjs, which checks the anchor count (1 → 0) and that the blob moved before the run.loadedOnceRef0,RefreshIndicator0Tests 5 failed | 6 passed (11), exactly those fivefinallyguard removed (if (true …))3 failed | 8 passed, exactly those three1 failed | 10 passed1 failed | 10 passed11 passed (11)Gates (at
c1041d311)Derived by hand from objectui's
package.jsonand.github/workflows/, becausedispatch-gates.mjsanswers only for objectstack.pnpm exec vitest run packages/plugin-gantt/ packages/i18n/:Test Files 147 passed (147),Tests 1762 passed (1762), VERDICT command-exit 0.turbo run build --filter='@object-ui/plugin-gantt^...' --concurrency=2: 12/12 tasks.pnpm --filter @object-ui/plugin-gantt --filter @object-ui/i18n run type-check: exit 0. Bothtsc --noEmitandtsconfig.test.jsonran, and the latter includessrc/**/*.test.tsx.run linton both packages: exit 0, 0 errors. Warnings are the existing ones (plugin-gantt 436, i18n 33). None sits on a line this diff added inObjectGantt.tsx, and none is in a locale pack.check-changeset-presence,-no-major,-overwrite,-fixed,check:changeset-claims,check:pending-changeset-literals;check:new-line-citations(0 new),check:control-bytes;check:i18n-keys,check:i18n-drift,check:i18n-designer-parity,check:i18n-dead-keys(the new key is not reported);check:unreferenced-sources,check:phantom-deps,check:self-import,check:esm-specifiers,check:side-effects-array;check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape.check-eager-locale-catalogues: NOT MEASURED. It answered PREREQUISITE NOT MET becauseapps/console/dist/eager-closure.jsonneeds a console production build, so it is left to the performance-budget workflow.check:changeset-claimsnamed three pending paragraphs that mention a touched file (6475, 6661, 6939). I re-read each: they covergetGanttConfig's key set,console.navstrings, and the record-source order. None is falsified.Acceptance notes
refreshingflag the toolbar button already read. The changeset says so.erroris never cleared by a later successful reload, so the chart stays on the error screen. Measured at head with a throwaway probe (deleted, never committed): a failed first load, then a sort change. The secondfindran with$orderby {start_date: desc}and succeeded, and the reading waserrorShown: true, chartMounted: false, recoveredRowShown: false. The first load is non-silent in both trees, so base behaves the same and this diff does not move it. Handed to the seat to file.Generated by Claude Code