Skip to content

A NULL first-dimension value is still dropped by the multi-dimension pivot branch of buildChartSeries (the sibling of #4466) #4497

Description

@yinlianghui

What

#4466 fixed the single-dimension branch of packages/core/src/utils/chart-series.ts: a row whose category value is null is now mapped to an explicit bucket label so the group renders instead of vanishing. The 2+ dimensions, single measure pivot branch, ten lines above it, still has the original shape and was deliberately left out of that card's ruled scope.

Mechanism

The pivot branch buckets by String(xRaw ?? '') but writes the RAW value into the emitted row:

const xId = String(xRaw ?? '');
if (!byX.has(xId)) byX.set(xId, { [xKey]: xRaw });

So a null first-dimension value produces { status: null, Low: 3 }, which reaches recharts with a null category — the same input that drew zero marks in #4466. Pinned as current behaviour (not asserted as correct) in packages/core/src/utils/chart-series.nullCategory.test.ts, in the case "leaves the multi-dimension pivot branch exactly as it was", so any future change to it has to be deliberate.

The second dimension has a related but distinct question: gId is String(row[groupKey] ?? '') and a '' group is skipped entirely (if (gId !== '' ...)), so a null SERIES value is dropped from the series list while its measure column is still written onto the row under the '' key. Whether that should also become a bucket, or stay dropped, is a separate judgement from the x-axis one.

Why it was not fixed in #4466's PR

The card's ruling scoped the fix to the located cause (the single-dimension branch) and listed "multi-dimension branch unaffected" among its must-not-change controls. Applying the single-dimension answer to the pivot on the way past would have changed a branch nothing had measured, so it was pinned instead.

Suggested shape (not prescriptive)

NULL_CATEGORY_LABEL / ChartSeriesOptions.nullCategoryLabel already exist in @object-ui/core and both consumers already pass a localized label, so the x-axis half is likely small. It needs its own measurement first: the pivot's xId map key and the emitted row value are two different things today, and the drill-through inverse (findChartSeriesRow's multi-dimension arm) has to move with whatever is decided.

Found while implementing #4466; filed unassigned rather than fixed, per that card's surface.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 12, 2026
  2. yinlianghui commented on Aug 12, 2026

    @yinlianghui
    CollaboratorAuthor

    CLAIM — session session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4497-pivot-null-bucket.

    Ruling (delegated authority; open veto window): extend the #4466 doctrine (PR #4498) to the pivot branch — the mechanism is the same and so is the answer: the emitted row's first-dimension value gets the bucket label (via the same nullCategoryLabel option / NULL_CATEGORY_LABEL default), instead of the current split where the map KEY is String(xRaw ?? '') but the ROW carries raw null to recharts. The filing's own condition is binding: findChartSeriesRow's multi-dimension arm moves WITH the emitted-row change so the newly-visible bar keeps its drill (the #4498 dead-click rule), measured against how the pivot's drillRawRows indexing actually works — the filing notes map key and row value differ, so measure before assuming the single-dimension pattern transplants cleanly; if the pivot's drill indexing makes the label-match ambiguous (two raw groups mapping to one label), STOP on that half and report.

    The deliberate-pin discipline: #4498's suite pinned the pivot's CURRENT null-drop behavior precisely so a change would be deliberate — update that pin citing this card, in the same commit as the fix. Red-first: pivot rows with a null first-dimension value → pre-fix the pin shows {status: null, …} emitted and the DOM drops the bar (capture); post-fix bucket-labeled, rendered, drill resolving. Must-not-change (green both sides): non-null pivot groups byte-identical; the single-dimension branch untouched (its #4498 pins stay green); empty result still the designed empty state.

    Grading: patch @object-ui/core + patch @object-ui/plugin-charts (reuses the existing option/constant — no new export expected; .d.ts measured both ways, minor only if surface grows). Never major.

    Surface (mutual exclusion): packages/core/src/utils/chart-series.ts + its tests, packages/plugin-charts/** tests (+ renderer only if measurement demands — report first), one changeset. ⛔ NOT: plugin-dashboard source (#4500 is its own pooled card), DashboardFilterBar (in-flight #4465), plugin-grid/plugin-list (in-flight #4501), console/** (in-flight #4467/#4475), the sweep file, content/docs/releases/.


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions