Skip to content

fix(plugin-dashboard): one header spelling across the table widget family; pin dashboard dimension-member labels - #5445

Merged
os-support-ai merged 2 commits into
mainfrom
claude/issue-5425-dashboard-dimension-labels
Aug 20, 2026
Merged

fix(plugin-dashboard): one header spelling across the table widget family; pin dashboard dimension-member labels#5445
os-support-ai merged 2 commits into
mainfrom
claude/issue-5425-dashboard-dimension-labels

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Part of #5425. Read the first section before reviewing the diff — the card's primary premise did not survive re-measurement, and this PR deliberately does not implement what the card asks for.

Verification below was run on 231698b96, the branch head.

The fork: defect 1 no longer reproduces

The card was measured on 17.1.0. Re-measured on main (17.6.0), dashboard dimension members already resolve through the backing picklist field's declared options and therefore through its translations. The analytics label net that does this shipped in 17.5.0 — objectui#4030 / PR #4324 (charts and dotted dimensions), objectui#4330 (local selects on table and pivot), objectui#4389 (the shared React glue). Every non-metric dataset dimension is routed resolveDimensionFieldMeta to buildDimensionLabelMap to relabelDimensions with the locale bundle applied.

Measured as rendering, which is what the card asks for — a zh-CN session, the card's four widgets, the card's own member values:

widgetstored values17.1.0 (card)this branch
阶段管道分布NeedsAnalysisNegotiationProposalNeedsAnalysis · Negotiation · Proposal需求分析 · 谈判 · 提案
预测类别管道分布BestCaseCommitBestCase · Commit最佳情况 · 承诺
线索来源分布Content / BlogWebContent / Blog · … · Web内容 / 博客 · … · 网站
丢单原因分析Bad TimingPrice Too HighBad Timing · … · Price Too High时机不合适 · … · 价格过高

The "this branch" column is the same on origin/main — nothing here produces it. What was missing was a test stated in the card's terms, so a regression would have been silent. That is what this PR adds.

Defect 2 goes with it on that surface. The card reads NeedsAnalysis on the bar axis against Needs Analysis in the pivot row label; today both surfaces consume the samedimensionLabels map and fall back to the same stored value, so one value cannot carry two spellings there. Pinned explicitly, including the free-form case where no bundle entry exists.

Provenance of the two measured spellings

The card asked which function each spelling comes from. Run over the card's own stored value, the two functions git grep surfaces reproduce the reported pair exactly:

humanizeLabel ('NeedsAnalysis') -> 'NeedsAnalysis' packages/plugin-charts/src/ObjectChart.tsx
humanizeFieldKey ('NeedsAnalysis') -> 'Needs Analysis' packages/plugin-dashboard/src/utils.ts

humanizeLabel maps [_-] to spaces and title-cases word boundaries, so a PascalCase value passes through untouched; humanizeFieldKey also splits camelCase. The card's diagnosis was right for 17.1.0. Neither is reachable from a dataset dimension member today.

What this PR does change

The prettifier family the card pointed at survives over field keys, and there it still carried the card's defect class. ObjectDataTable derives column headers on two paths; the declared path and the static data-table half both used humanizeFieldKey, while the auto-derived path carried a third inline spelling that split camelCase but never turned _ into a space. Measured, rendered, over the same object's columns:

pathclose_dateneeds_analysisunitPrice
object-bound, auto-derived (before)Close_dateNeeds_analysisUnit Price
object-bound, auto-derived (after)Close DateNeeds AnalysisUnit Price
object-bound, declared columnsClose DateNeeds AnalysisUnit Price
static data-table, no columnsClose DateNeeds AnalysisUnit Price

One dashboard can hold all three widgets over one object, so a single field key rendered under two spellings, one of them leaving a raw underscore on screen. The odd path adopts the convention humanizeFieldKey's own docstring already declares itself the single home of, rather than the convention gaining a fourth dialect. A bundle entry still wins — only the fallback handed to fieldLabel changed. The camelCase column is where the old spelling and the convention coincided, which is why the snake_case divergence went unnoticed; it is pinned so this is not misread as a camelCase change.

Zero contract additions, as the ruling requires. No translation-schema key is added, and measure/dimension labels are untouched — that half stays deferred.

Reverse verification

Directions written before each leg ran; both legs read source directly (the root vitest.config.mts aliases every @object-ui/* specifier to that package's src/, and the tests import ../DatasetWidget / ../ObjectDataTable relatively), so no dist/ is involved in either the mutated or the restored leg. Restoration was confirmed by grepping the marker back out of each file.

Leg 1 — revert the ObjectDataTable fix. Predicted: the two auto-derived assertions RED, every boundary GREEN. Measured exactly that.

× AUTO-DERIVED columns spell the header with the shared convention
× AUTO-DERIVED, DECLARED and STATIC paths all agree, key for key
Test Files 1 failed | 1 passed (2)
Tests 2 failed | 10 passed (12)

Leg 2 — remove the locale seam (pass undefined for fieldOptionLabel into deriveDimensionLabelMaps), to show the new member pins are load-bearing rather than phantom. Predicted: the four zh-CN member pins RED, the bar/pivot agreement pin and both boundaries GREEN. Measured five red — the agreement pin went red too:

× 阶段管道分布 — an opaque stored code resolves through the option label
× 预测类别管道分布 — same, on the second opaque-code dimension
× 线索来源分布 — a value stored AS its display string still translates
× 丢单原因分析 — same, on the fourth measured dimension
× the bar axis and the pivot header agree on the same stored value
Test Files 1 failed | 1 passed (2)
Tests 5 failed | 7 passed (12)

That fifth failure is a sequencing dependency, not an assertion one: the pin captures the axis spelling by first waiting for the translated category, so it needs the bundle to clear its waitFor even though the property it asserts is direction-independent. The prediction is corrected in the test file's header rather than the run being repeated to fit it, and the free-form twin below it — which agrees over a value with no options, waits on no translation, and stayed GREEN in both legs — is what shows the agreement does not rest on a bundle entry existing.

Gates

scripts/pm/dispatch-gates.mjs does not exist in this repository (it is an objectstack-repo tool), so the family was derived from the changed paths: two plugin-dashboard test files, one plugin-dashboard source file, one changeset.

pnpm exec vitest run packages/plugin-dashboard/ packages/plugin-charts/
-> Test Files 100 passed (100) | Tests 870 passed (870)
pnpm --filter '@object-ui/plugin-dashboard^...' build -> exit 0 (deps first; without it type-check
pnpm --filter @object-ui/plugin-dashboard type-check -> exit 0 reports phantom TS2307s)
pnpm exec eslint <the 3 changed source/test files> -> 0 errors, 41 warnings (no-explicit-any,
matching the files' existing style;
lint.yml deliberately sets no --max-warnings)
pnpm check:control-bytes -> OK (4498 tracked text files scanned)
pnpm check:phantom-deps -> OK
pnpm check:self-import -> OK
pnpm check:esm-specifiers -> OK
node scripts/check-changeset-presence.mjs -> OK (3 source files of 1 released package, 1 changeset)
node scripts/check-changeset-no-major.mjs -> OK

Plus a manual control-byte scan over the changed files: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' returned no matches.

No test was skipped, disabled or quarantined.

Out of scope

Filed unassigned as #5444: the value-fallback prettifier humanizeLabel has two byte-identical copies (packages/fields, packages/plugin-charts) and disagrees with the key-fallback convention on camelCase input. It is deliberately not touched here — aligning only the plugin-charts copy would trade a latent disagreement for a live one against plugin-grid / plugin-gantt / plugin-detail, and packages/fields sits outside this card's file surface and was held by PR #5441 at the time.

For the PM

This is titled Part of #5425 rather than a closing keyword on purpose: the card's own two asks are satisfied by code that already shipped, so #5425 needs re-triage rather than being closed by this merge. #5444 is a new finding and is not addressed here.


Generated by Claude Code

…mily
`ObjectDataTable`'s auto-derived columns carried a third, inline spelling of
the field-key -> header convention that split camelCase but never turned `_`
into a space, so `close_date` rendered as `Close_date` beside the declared and
static paths' `Close Date`. One dashboard can hold all three widgets over one
object, so a single key appeared under two spellings.
The odd path adopts `humanizeFieldKey`, which already declares itself the
single home for this convention. A bundle entry still wins; only the fallback
handed to `fieldLabel` changed.
Also pins the card's first defect, which no longer reproduces: dataset
dimension members resolve through the picklist field's declared options and
locale bundle (the analytics label net, 17.5.0). Pinned over the four
dashboards the card measured, plus the property that a bar axis and a pivot
header cannot disagree about one stored value.
Part of #5425
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
The bar/pivot agreement pin was predicted GREEN in both legs and measured RED
in the mutated one: it sequences on the translated category before comparing
spellings, so it needs the bundle to get past its `waitFor` even though the
property it asserts is direction-independent. Correct the header rather than
re-run to fit the prediction, and name the free-form twin as the leg that is
genuinely direction-independent.
Part of #5425
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)25.3 KB350 KB
Entry fileindex-DzW9Pol6.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)10.04KB3.72KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)29.34KB7.05KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)6.35KB2.43KB
auth (index.js)2.77KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.89KB
auth (useIsWorkspaceAdmin.js)3.04KB1.45KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.94KB113.61KB
core (index.js)4.11KB1.62KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.80KB44.34KB
fields (index.js)237.21KB59.50KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.22KB3.08KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)30.51KB7.57KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.75KB18.37KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)128.37KB32.91KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)241.46KB60.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)124.19KB30.20KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)197.28KB53.06KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.64KB27.13KB
plugin-map (index.js)20.08KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.52KB20.67KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)36.10KB12.26KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-support-ai
os-support-ai marked this pull request as ready for review August 20, 2026 21:17
@os-support-ai
os-support-ai added this pull request to the merge queueAug 20, 2026
Merged via the queue into main with commit 56f4e34Aug 20, 2026
23 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-5425-dashboard-dimension-labels branch August 20, 2026 21:18
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-support-ai@claude