fix(plugin-gantt): tooltip currency re-formats when the tenant currency resolves (#4542) - #4554
Merged
Merged
Conversation
…cy resolves (#4542) The `tasks` memo builds every tooltip string eagerly inside its callback, and the `'currency'` case resolves its code down to the tenant default via `resolveFieldCurrency(def, tenantCurrency)`. `tenantCurrency` was read but not watched: it was missing from the memo's dependency array, which ESLint's `react-hooks/exhaustive-deps` was already reporting on `origin/main`. The tenant default arrives from `GET /api/v1/auth/me/localization`, which is cosmetic and non-blocking and therefore answers AFTER first paint. The context change re-rendered ObjectGantt, but with no dependency changed the memo handed back its cached task array, so the tooltip kept its pre-resolution rendering. Not covered by the `displayLocale` dependency #4272 (PR #4544) added to this same array: the producer writes currency and locale from one response, so a tenant configuring BOTH re-runs the memo through the locale channel — but a tenant configuring a currency and no locale (the common shape) leaves `displayLocale` untouched and the currency stale. The new test resolves currency alone for exactly that reason. Module-local: the package's nine `.d.ts` files are byte-identical across the change, so the changeset is a patch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
PM step-7 复核 — ACCEPT (session_017Qqyix2QcnpUC9XeYVDzx3)
Auto-merge armed (squash) — landing verified per the merge-queue discipline. Generated by Claude Code Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 13, 2026 08:13
This was referenced Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4542
The currency twin of #4272 / PR #4544, in the same
useMemo.The defect, measured on
origin/main(479cc7b)packages/plugin-gantt/src/ObjectGantt.tsx— thetasksmemo builds every tooltip string eagerly inside its callback, and the'currency'case resolves its code down to the tenant default:tenantCurrency(L432, fromuseLocalization()) was read but not watched — the array was[data, ganttConfig, objectSchema, displayLocale].Independent confirmation before writing a line: ESLint on the unfixed file already reported it, and the fix removes exactly that one message (0 errors both sides, 99 warnings then 98):
Identity-stability measurement (the STOP condition in the ruling)
The ruling said to stop rather than add a dead dependency if the memo reads currency through a reference that never changes identity. It does change:
LocalizationContext.tsxL45-48 memoizes the context object on[value.currency, value.locale], so it takes a new identity exactly when either primitive changes, which re-renders every consumer.tenantCurrencyitself is a primitivestring | undefined. React compares deps withObject.is, soundefinedto'EUR'is a genuine change.Premise holds; the dependency is live. Producer timing confirms the window:
apps/console/src/LocalizationFetchProvider.tsxL89 does one asyncsetValueafter mount, because the endpoint is cosmetic and non-blocking.Two masking paths, both measured — they shape the test
displayLocaledep and the staleness never shows. It is observable on a tenant that configures a currency and no locale — the common shape, sinceuseDisplayLocaledocuments the tenant locale as "frequentlyundefined", in which case it falls back to the unchanged UI language. The repro therefore resolves{ currency: 'EUR' }alone.ganttConfigidentity.getGanttConfigreturns a fresh object literal on the flattened top-level (console ListView) path, which invalidates this memo every render by itself; on theschema.ganttpath it returnsschema.ganttby reference. Only the latter is memoized in practice, so the cases useschema.ganttwith a module-constant schema and dataSource.Red-first: predicted split, written before the run, then measured
displayLocaledep still re-formats on a late tenant locale (pin)Predicted failure text, and the verbatim failure that came back — identical:
Reverse-verified after the fix by removing it with
git checkout origin/main --(notgit stash) and re-running: the same one case went red, the four pins stayed green, and the restore was sha256-verified byte-identical.Verification
pnpm exec vitest run --maxWorkers=2 packages/plugin-gantt— 43 files, 378 tests, all green (includesObjectGantt.dateLocale.test.tsx, PR fix(fields): the date formatter's last three en-US channels thread the display locale (#4272) #4544's pins).pnpm --filter @object-ui/plugin-gantt type-check— both passes (tsc --noEmitandtsc -p tsconfig.test.json) green.origin/mainbaseline, theexhaustive-depsone quoted above. No other rule id changed in the multiset.check-control-bytesOK,changeset:checkOK, changeset presence OK,check-phantom-dependenciesOK..d.tsdiff both ways withdist/andtsconfig.tsbuildinfocleared between builds: all 9 declaration files byte-identical, which is what makespatchthe right bump. No consumer sweep was needed for the same reason — nothing crosses the package boundary.Surface
ObjectGantt.tsx(the dependency array plus its comment), one new test file, one changeset. Nothing else touched.Out of scope, filed not fixed
#4553 — the same tooltip formatter's number / currency / percent rows pass no locale to
formatNumber/formatCurrency/formatPercent, so they render in the MACHINE locale right beside date rows that follow the display locale. A different defect at a different site (WHICH locale the string is built in, versus this card's WHEN it is rebuilt); this PR does not move it.Generated by Claude Code