Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

plugin-charts: vertical bar charts drop most categorical x-axis labels in dashboard-width widgets (3 bars → 1 label, 5 bars → 2), horizontal bars keep all of them #7247

Closed

Description

@hotlong

What happens

Showcase dashboards at ~200-290px widget width (three-column dashboard grid in an ~800px console):

widget bars x labels drawn
Delivery Operations → Projects by Health 3 Yellow only
Delivery Operations → Tasks by Status 5 Backlog, To Do
Chart Gallery → Tasks by Status 5 Backlog, To Do
Chart Gallery → Tasks by Priority 4 High, Urgent
Chart Gallery → Hours by Status (horizontal) 5 all 5
report Hours by Status (Chart) at ~650px 5 all 5

So a categorical bar chart becomes unreadable exactly where dashboards put it: the bars have no names, and the legend does not help because a single-series bar chart has none. The thinning is width-driven (the same chart in the wide report view keeps every label) and only affects the vertical orientation.

Expected

Categorical axes keep every label while there is room to rotate or wrap them (3-5 rotated labels fit easily in 200px); thinning should be the last resort and should never leave bars without a name when a tooltip-free surface (screenshot, print, big-screen page) is the consumer. Rotate → shrink → truncate with ellipsis → thin, in that order.

Related polish seen on the same widgets (can ride along or split): count y-axes use fractional ticks (0 / 0.8 / 1.5 / 2.3 / 3) for integer data; radar and funnel labels clip (gent, In Revie); the sankey draws no node labels at all.

objectui @ 67dadd6, objectstack main @ a39b02a6b. Found during the objectstack pin-bump dogfood (objectstack#14295).

Generated by Claude Code

Activity

  1. self-assigned this
    on Sep 2, 2026
  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 2, 2026
  3. hotlong commented on Sep 2, 2026

    @hotlong
    ContributorAuthor

    Claim: PM loop round 3 (dogfood dispatch)
    Session: session_62849a16-0144-4728-8942-9f60bb3a73f1
    Branch: claude/issue-7247-bar-axis-label-thinning
    Worktree: objectui-issue-7247
    Domain: domain:ui
    File surface: packages/plugin-charts/src/** (the categorical x-axis label policy of the vertical bar family and its tests), .changeset/ (stop on breach; explain in the report)
    Container & model: M, model: opus, mode:subagent
    Clause-②: no
    Serial constraints cleared: #7248 (scatter, same package) is deliberately serialized AFTER this card; no open objectui PR touches plugin-charts (REST pulls list 2026-09-02 17:50); in-flight #7246 (fields) does not touch plugin-charts

    Maintainer direct-dispatch channel (2026-08-10 standing authorization); instruction in this session, verbatim: 「P1/P2 都开 issue,packages 500 单独修,服务先留着」 → 「使用 pm-dispatch 派发」. Dispatched by the dogfood session that filed the card, outside the lane seat; the seat holder keeps precedence on any later claim conflict.

    Generated by Claude Code

  4. hotlong commented on Sep 2, 2026

    @hotlong
    ContributorAuthor

    Claim: os-dev (developer seat) — recording the dev-side claim on the card. Posted late: this session was cut off by a usage wall mid-implementation and resumed from its transcript, so the marker lands alongside the finished work rather than ahead of it.

    Session: session_62849a16-0144-4728-8942-9f60bb3a73f1
    Branch: claude/issue-7247-bar-axis-label-thinning
    Worktree: objectui-issue-7247
    Assignee: untouched (PM owns that field)

    Generated by Claude Code

  5. hotlong commented on Sep 2, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 7247,
      "status": "done",
      "branch": "claude/issue-7247-bar-axis-label-thinning",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7385",
      "premise_still_valid": true,
      "summary": "Premise confirmed on current main and reproduced at the reported widths. Root cause: AdvancedChartImpl's shared xAxisCommonProps applied one tick policy to time and category axes alike (interval 'preserveStartEnd' + minTickGap 48) and keyed it to the VIEWPORT (isMobile), never the widget, so a 174px chart in an 800px console was charged a full desktop inter-label gap; horizontal bars escape only because their category axis is the y axis, which uses recharts' defaults. Fix: a categorical x axis of 5 buckets or fewer now draws every label (interval 0), rotating and ellipsising over-long names; the bound is derived from the narrowest shipped widget (142px plot / rotated labels collide on perpendicular separation bandWidth*sin(35deg) vs a 14px line box => buckets at most 5.8), so nothing is measured at runtime. Above the bound recharts' measured thinning is untouched, which keeps the old 'hundreds of points paint a dense black bar' guard. Change surface declared in the PR body: CHANGED xAxisCommonProps (bar/column, line, area, combo); ALREADY COMPLIANT ChartImpl.tsx (hard-codes interval 0) and the horizontal-bar category axis (both untouched, the latter pinned by a no-regression test); OUT OF SCOPE the scatter axes (#7248, serialized after this card) and the issue's three ride-along polish items. One thing deliberately NOT done here and filed instead: relaxing minTickGap 48 for categorical axes ABOVE the bound (same root cause, but the replacement value is a judgement call and its effect is unmeasurable in happy-dom, which reports zero text metrics).",
      "tests": "All on final commit 200063bc9 (git rev-parse --short HEAD after the last commit). RED FIRST: the new regression test ran against the unfixed source before the policy existed - 5 of 7 failed, e.g. expected ['Green','Red'] to deeply equal ['Green','Yellow','Red'] and expected ['Low','Urgent'] to deeply equal ['Low','Normal','High','Urgent']; the other 2 (high-cardinality thinning, horizontal-bar family) passed both before and after, which is their purpose. Dependency closure built once (pnpm --filter '@object-ui/plugin-charts^...' build, exit 0) before the red run and not rebuilt between red and green; the subject is imported from source, so both runs read the same tree. GREEN: pnpm exec vitest run packages/plugin-charts/ -> 'Test Files 41 passed (41)' / 'Tests 383 passed (383)'. pnpm --filter @object-ui/plugin-charts type-check -> exit 0 with 'tsc --noEmit' echoed (real run, not a zero-match filter); tsc --listFiles confirms the new test file IS in the type-check file set (1 hit), so its green covers the test too. pnpm --filter @object-ui/plugin-charts lint -> exit 0, '303 problems (0 errors, 303 warnings)', all pre-existing. check-control-bytes 'OK (scanned 6044 tracked text file(s))'; check-vi-mock-specifiers OK; check-vi-mock-inherit OK (this PR adds a vi.mock('recharts')); check-changeset-fixed OK; check-changeset-no-major OK. Exit codes were captured after redirect to a file, never through a pipe. BROWSER before/after, Chromium at an 800px viewport, plot box 174px, counted from the DOM: Projects by Health 3 bars 1 label -> 3; Tasks by Status 5 bars 2 labels -> 5; Tasks by Priority 4 bars 2 labels -> 4; Hours by Status horizontal 5 -> 5 unchanged; 654px report chart 3 -> 3 unchanged. The BEFORE column reproduces the issue's table exactly. That before-pass used the sanctioned mutate-then-restore shape (fix committed first): mutation 'git checkout origin/main -- FILE' confirmed on disk by blob hash 2acc581d3 -> aceb44fcb plus a zero count of the new policy identifier; restore 'git checkout HEAD -- FILE' proven by the on-disk hash matching the HEAD blob exactly and 'git diff HEAD' empty. DECLARED NARROWINGS/DEVIATIONS (all also in the PR body): (1) os-verify-lock.sh found no usable flock on this macOS host and ran in DECLARED UNLOCKED MODE - nothing was serialized against sibling agents; (2) the dispatched command 'pnpm --filter PKG exec vitest run FILE' is REFUSED by this repo's invocation guard (objectui#3378) because a package-cwd run silently executes @object-ui/console's 22 files and reports them green, so every run was started from the repository root, as CI does; (3) lint here is 'turbo run lint' i.e. a per-package 'eslint .', and this diff touches exactly one package, so the package lint run is the whole lint surface rather than a narrowing. Not measured: CI - reported at draft-PR time per contract.",
      "mcp_calls": "0 - every GitHub read and write in this run went through `gh api` (REST); no MCP GitHub call was made",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #7386: categorical x axes ABOVE the 5-bucket bound still lose labels because minTickGap 48 is a time-series budget charged to band axes - same root cause, left out because the replacement value is a judgement call and happy-dom's zero text metrics make it unmeasurable in unit tests (needs its own browser-verified change); filed unassigned, no label, for PM triage"
      ]
    }

    Generated by Claude Code

  6. hotlong commented on Sep 2, 2026

    @hotlong
    ContributorAuthor

    ACCEPT — reviewer of record: PM session session_62849a16-0144-4728-8942-9f60bb3a73f1.

    Checked against GitHub, not the report: draft PR #7385 targets main, first line Fixes #7247, no other closing keyword, changed files stay inside the claimed surface (AdvancedChartImpl.tsx + its density test + changeset; no governed path), 30 check-runs green at head 200063b. Spot-read: categorical x axes of ≤5 buckets draw every label (interval 0, rotate/ellipsis), bound derived from the narrowest shipped widget, high-cardinality thinning and the horizontal-bar family pinned as no-regression; browser counts 1→3, 2→5, 2→4 at an 800px console; finding #7386 filed for the >5-bucket minTickGap. Flipping to ready and arming queue merge.

    PR: #7385

    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

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions