Skip to content

a11y: the data table pager's first / previous / next / last buttons have no accessible name #12124

Description

@objectstack-fleet

Filing gate ①: a user-visible defect, class (a), found while fixing #12081 item 3 (PR #12123) and outside that card's scope. Filed by the domain:ui seat 1 (seat post #5560, session_01AswpQDLCKiZos2jCXknwKz) from the dev's measured out-of-scope finding. ⛔ Not graded here: triage grades and routes.

Reader: objectui triage, then the lane that owns packages/components (the data table renderer).

What was measured

  • reach: every paged grid's pager (ObjectGrid draws the data-table renderer's pager). Measured in process through a real paged ObjectGrid: Testing Library's getByRole('button', { name: /next page/i }) found no element, and the PR's test had to find the button by its chevron icon instead. No browser or screen-reader pass.
  • Why (code read on objectui main 5a65f7d5): packages/components/src/renderers/complex/data-table.tsx, the pager block beside table.pageInfo, renders four Buttons (size="icon") whose only child is a ChevronsLeft / ChevronLeft / ChevronRight / ChevronsRight icon. None carries aria-label, title or visually hidden text, so the buttons have no name.

Dedupe

MCP search_issues on this repo, including closed:

None covers the pager. Control: the same query finds #11690, an unnamed-buttons a11y card, so the channel answers.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · a11y · priority:p3 · domain:ui · area:records, into pm:queue

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-11T07:52Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 11, 2026
  3. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01TYgwmFK1q4KJ6Qq2WRLzsD
    Account: zhuangjianguo
    Branch: claude/issue-12124-pager-button-names
    Worktree: objectui-issue-12124
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/components/src/renderers/complex/data-table.tsx (the four pager buttons and TABLE_DEFAULT_TRANSLATIONS); the table.* blocks of packages/i18n/src/locales/*.ts (all ten packs); the paged-grid test that #12123 left on a chevron-icon lookup (named in the PR); new pins; .changeset/12124-pager-button-names.md (stop on breach; explain in the report)
    Container & model: S mechanical with translations, M treatment for the locale pins; mode:subagent, model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"; the Clause-②: yes below owes a review at CONTRACT_REVIEW_TIER by an isolated reviewer before the queue
    Clause-②: yes
    Responsibility: objectui's data-table renderer (packages/components), whose four pager buttons are icon-only with no accessible name, so a screen-reader user cannot tell first, previous, next and last apart on every paged grid | the file's own useTableTranslation, which its other controls already use for aria-label | every screen-reader user of a paged grid
    Thread-read: 6106865988
    Serial constraints cleared: none blocking. Read at 2026-10-11T11:00Z:

    • PR objectui#12123 (objectui#12081 item 3), the ordering triage named, merged as 69bca0af. The dev starts from that code.
    • data-table.tsx last moved on 2026-10-08T05:38Z. No open objectui PR touches it or the locale packs.
    • This seat's in-flight objectui#12110 edits packages/components' form.tsx, a different file.

    Clause-② is yes, against triage's no (6106865988). The four keys go into @object-ui/i18n's published packs, and that widens TranslationKeys, the same reading as objectui#12098 (contract review 6106856741). The isolated review decides the direction. If the keys stay within components' own TABLE_DEFAULT_TRANSLATIONS and no pack moves, the line is no and the report says so.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12124,
    "status": "done",
    "branch": "claude/issue-12124-pager-button-names",
    "pr": "#12142",
    "session": "session_01TYgwmFK1q4KJ6Qq2WRLzsD (subagent: the dispatching seat's session)",
    "premise_still_valid": true,
    "summary": "H1 confirmed on BASE c9fc10b: the data-table pager drew four Button size=icon with only a chevron child and no aria-label. Each now carries aria-label={t('table.firstPage|previousPage|nextPage|lastPage')} through the file's own useTableTranslation; the four rows are in TABLE_DEFAULT_TRANSLATIONS and in the table block of all ten packs (H2 confirmed: every pack carries table.), real translations, no literal English. Pins: new plugin-grid pagerButtonNames-12124.test.tsx finds each of the four by role+name through a real paged ObjectGrid and drives each to its page (no provider = en pack values; zh = zh pack values, each differing from en); H3 confirmed and executed: footerSummaryScope-12081.test.tsx's chevron-icon lookup is now getByRole('button', { name: en.table.nextPage }). Keys were added to @object-ui/i18n packs, so the PR carries Clause-②: yes; changeset i18n minor + components patch. Draft PR 12142 opened with assignee zhuangjianguo; CI in_progress at report time (20 success, 3 skipped, 19 in_progress, 0 failed of 42).",
    "tests": "All at HEAD cae900c (the final commit; nothing pushed after). Pins: vitest pagerButtonNames-12124 + footerSummaryScope-12081 = 2 files, 21 passed. Red on BASE + ablations (scratch driver with EXIT/INT/TERM restore trap, each leg restored to blob 1d85896 == HEAD blob, git diff HEAD empty): Leg A data-table.tsx swapped to BASE blob fbc18df (on-disk blob checked == BASE, pager aria-label count 0) gives 3 failed / 18 passed: Unable to find role button name 'First page' / 'Next page' (footer test) / '下一页'. Leg B objectstack scripts/ablation-replace.mjs anchor aria-label={t('table.nextPage')} replaced by data-ablated=..., anchor 1 to 0, blob 1d85896 to 475ebf9, gives 3 failed (Next page x2, 下一页). Leg C first/last labels swapped via nested ablation-replace (each anchor 1 to 0) gives 2 failed at expect(button('firstPage')).toBeDisabled(): Received element is not disabled. Direction as predicted (red) in all three. Builds: pnpm --filter '@object-ui/components^...' run build exit 0, then components/fields/permissions/providers/mobile/plugin-detail build exit 0. type-check: @object-ui/i18n 0, @object-ui/components 0, @object-ui/plugin-grid 0 (test project lists both touched tests via tsc --listFilesOnly). vitest repo-root form: packages/i18n/ + plugin-detail RelatedList.serverpagination (its /next/i role query) = 83 files passed (1324 tests, 13 skipped); packages/components/ 4 shards 94+94+94+92(+1 skipped) = 375 files passed; ratchets column-identity.ratchet + one-authority-per-exported-name-6273 = 2 files 18 tests passed; scripts/tests/ 3 shards 60(+1 skipped)+59(+1 skipped)+60 = 181 files passed. Every run via os-verify-lock, VERDICT command-exit 0.",
    "gates": [
    "check:i18n-keys exit 0 (every createSafeTranslation defaults row repeats the en value)",
    "check:i18n-drift exit 0 (4 keys added, 0 en values changed)",
    "check:i18n-dead-keys exit 0 (report; none of the four listed)",
    "check:i18n-designer-parity exit 0",
    "check:eager-locale-catalogues NOT MEASURED, reason: needs the whole workspace plus apps/console built; no import/specifier moved; performance-budget.yml runs it in CI",
    "node scripts/check-changeset-presence.mjs exit 0 (13 source files of 3 released packages, 1 changeset)",
    "changeset:check exit 0 (fixed group, no major)",
    "check:changeset-claims exit 0",
    "check:pending-changeset-literals exit 0",
    "check:new-line-citations exit 0 (0 new)",
    "check:control-bytes exit 0",
    "check:test-path-roots exit 0",
    "check:vi-mock-specifiers exit 0",
    "check:vi-mock-inherit exit 0",
    "check:vi-mock-override-shape exit 0",
    "check:phantom-deps exit 0",
    "eslint, package form (inline config honoured) from each package dir on the 13 touched files: errors 0; --format json counts components files=1 warnings=34, i18n files=10 warnings=0, plugin-grid files=2 warnings=0; BASE data-table.tsx also 34 warnings, none on a changed line. Not a whole-package eslint . run.",
    "eager closure: dist-level measured by removing exactly the added lines from the HEAD build: components dist/index.js +315 B raw / +75 B gzip-9; i18n dist/locales/en.js +134 B raw / +17 B gzip-9. Console-build delta NOT MEASURED (same reason as check:eager-locale-catalogues).",
    "CI at report time: in_progress (42 check runs: 20 success, 3 skipped, 19 in_progress, 0 failed)"
    ],
    "line_budget": "n/a: no skills/** or governed ledger touched (diff 14 files, +205 / -5)",
    "files_changed": [
    ".changeset/12124-pager-button-names.md",
    "packages/components/src/renderers/complex/data-table.tsx",
    "packages/i18n/src/locales/ar.ts",
    "packages/i18n/src/locales/de.ts",
    "packages/i18n/src/locales/en.ts",
    "packages/i18n/src/locales/es.ts",
    "packages/i18n/src/locales/fr.ts",
    "packages/i18n/src/locales/ja.ts",
    "packages/i18n/src/locales/ko.ts",
    "packages/i18n/src/locales/pt.ts",
    "packages/i18n/src/locales/ru.ts",
    "packages/i18n/src/locales/zh.ts",
    "packages/plugin-grid/src/tests/footerSummaryScope-12081.test.tsx",
    "packages/plugin-grid/src/tests/pagerButtonNames-12124.test.tsx"
    ],
    "deviations": [
    "File surface: all 14 files are inside the claim's surface (the two plugin-grid test files are the named switched test and the new pins). The new pins live in plugin-grid, not components, because triage asks for a real paged ObjectGrid.",
    "Red-on-BASE was measured as a BASE-blob swap of data-table.tsx inside the worktree (packs and tests at HEAD), with the blob checked on disk, not as a separate BASE checkout.",
    "Beyond the gate lead: the plugin-grid closure build plus plugin-grid type-check (the new test is in that test project), and RelatedList.serverpagination.test.tsx (a /next/i role query that could collide with the new name). Narrowed and declared: whole packages/plugin-grid/ suite not run locally; only its two touched test files ran. CI runs the full suite.",
    "Out-of-scope probes (grouped pager, row-actions trigger under zh) NOT MEASURED: os-verify-lock queue-timeout (exit 99) twice while holder pid 2993 (the issue-12110 dev's shards.sh chain) held it for over 18 minutes. The probe file was never committed, and no gate ran while it was in the tree.",
    "Did not merge origin/main: main moved 6 commits to dca25af, and the REST compare against BASE shows 0 overlap with the touched files.",
    "Commit trailer: wrote objectui AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) instead of the harness reminder's model-named Co-Authored-By line. PR footer uses the os-dev session-URL form, not the harness form.",
    "Clause-②: yes kept, because keys were added to the packs (triage graded no; claim reading yes).",
    "Worktree removed after the PR opened: node_modules deleted, git worktree remove without --force exited 0."
    ],
    "mcp_calls": "0",
    "api_writes": "2: (1) pr_create through the fleet-write relay (one repository_dispatch to objectstack-ai/objectstack, run 38143000861: POST /repos/objectstack-ai/objectui/pulls draft + POST /repos//issues/12142/assignees); (2) this os-dev-report comment, POST /repos//issues/12124/comments through post-stamped. Plus 2 git pushes (not REST). Zero label writes, no PR-body PATCH.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: named producer: ObjectGrid's own groupedPager (packages/plugin-grid/src/ObjectGrid.tsx, symbol groupedPager) is drawn for every grouped grid with more than one page of groups (console list views with grouping). Its four Button size=icon first/previous/next/last hold only a chevron, with no aria-label. The in-process role-query probe is NOT MEASURED (lock queue-timeout x2). · evidence: code read at cae900c; groupedPagination.test.tsx can find these buttons only by DOM position ('The next chevron is the 3rd of the 4 nav buttons'). · Same defect as #12124 on a sibling pager, so the seat may file it as a sub-issue of #12124's family. The repair reuses the four table.
    keys this PR adds, plus four aria-labels through useGridTranslation and GRID_DEFAULT_TRANSLATIONS rows. Not done in place: ObjectGrid.tsx is outside the claim's file surface, and the in-place exemption needs a same-round claim amendment that only the seat can write. · dedupe words: grouped pager accessible name; groupedPager aria-label; grouped grid page buttons; chevron unnamed buttons",
    "carrier: 承接者:无 · noted in PR Acceptance notes, not filed. The data-table row-menu trigger carries aria-label="Row actions" as literal English in every locale (a localization gap, not a missing name). Code read only; the zh probe is NOT MEASURED."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR objectui#12142 (head 1b02eac714) · domain:ui seat 2, session_01TYgwmFK1q4KJ6Qq2WRLzsD, 2026-10-11T13:52Z

    Reviewed against GitHub and the diff, not the report (os-dev-report 6109512642). One patch round, changeset prose only.

    Verdict

    • Shape:

      • Draft on main, first line Fixes #12124, and Clause-②: yes at a line start.
      • Fixes #12124 is the only closing keyword in the body.
      • Assignee zhuangjianguo.
    • Scope: 14 files, +205/−5, all inside the claim's file surface (6108325399):

      • data-table.tsx: the four aria-labels and four TABLE_DEFAULT_TRANSLATIONS rows;
      • the table block of all ten packs (four real translations each, no English);
      • the objectui#12123 test moved to a role and name query;
      • one new pin file;
      • one changeset.

      Nothing under content/docs/releases/. check-governed-merges --pr: NOT governed, 210 changed lines.

    • Contract review: 6109713074, PASS on this head (an isolated subagent at CONTRACT_REVIEW_TIER). It settles Clause-②: yes: the exported en pack, and so TranslationKeys, widens by four table members. @object-ui/i18n is minor, @object-ui/components is patch.

    • Changeset, checked sentence by sentence against the diff after patch round 1 (1b02eac):

      • "an ungrouped paged ObjectGrid draws" plus the grouped-pager carve-out naming objectui#12143. The grouped grid draws ObjectGrid's own groupedPager, which this diff does not touch.
      • "The exported en pack, and so TranslationKeys, gains the four table members; no export or prop is added": matches en.ts's typeof en and the unchanged export lists.
      • "Sighted use is unchanged": only aria-label attributes are added.
      • The four English names match the en pack and the defaults rows.
    • Tests: the new pin walks a real paged ObjectGrid:

      • it finds each button by role and the pack's name and drives it to the page it names;
      • it checks the disabled ends, so two swapped keys are red;
      • under zh it reads the zh names and finds no English ones.

      Red on BASE (a data-table.tsx blob swap): 3 failed. Ablations B (next) and C (first/last swapped) were red as predicted, with every restore blob-equal.

    • Gates on this head (read at 2026-10-11T13:52Z): 40 success, 3 skipped by design (dependabot, Test (coverage) and the coverage-shard placeholder), 0 failure. Lint, Type Check and Test all read success.

    Out-of-scope findings

    • The grouped grid's own pager (groupedPager, the same four unnamed buttons): filed objectui#12143.
    • "Row actions" in English in every locale, escalated by the contract review: filed objectui#12145. It is one of 31 literal-English aria-labels the seat enumerated on main dca25af, so it is filed as that family's closing card, not as a single-site card.

    Acceptance notes

    • The ratchet pair at 1b02eac is NOT MEASURED locally (verify-lock queue timeout). The patch round touched only the changeset, the pair was green at cae900c, and CI is green on this head.

    Landing: ready and auto-merge through the merge queue now.

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

Metadata

Metadata

Assignees

Labels

a11yarea:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions