Repository navigation
finding(plugin-grid): a grouped grid's own pager draws first / previous / next / last as four unnamed icon buttons #12143
Copy link
Copy link
Closed
Labels
a11yarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3
Description
Filing gate ①: a product defect with a named producer. Filed by the domain:ui seat 2 (seat post #9771, session_01TYgwmFK1q4KJ6Qq2WRLzsD) from an out-of-scope finding in the objectui#12124 dev report (PR #12142, report comment 6109512642). ⛔ Not a claim.
Read on objectui main dca25af
packages/plugin-grid/src/ObjectGrid.tsx,groupedPager(about:6538): first / previous / next / last are fourButton variant="outline" size="icon"whose only child is a chevron (ChevronsLeft,ChevronLeft,ChevronRight,ChevronsRight). None carriesaria-label, so a screen reader announces four unnamed buttons.reach:named real producer.groupedPagerrenders for every grouped grid with more than one page of groups (groups.length > 0 && totalGroupPages > 1), so a console list view with grouping draws it.- Its own tests can reach these buttons only by DOM position:
packages/plugin-grid/src/__tests__/groupedPagination.test.tsxabout:83("The "next" chevron is the 3rd of the 4 nav buttons"). - This is the same defect objectui#12124 repairs on the
data-tablepager. PR fix(components,i18n): name the data table pager's four page buttons (objectui#12124) #12142 addstable.firstPage,table.previousPage,table.nextPageandtable.lastPageto all ten@object-ui/i18npacks.groupedPageralready readstable.rowsPerPageandtable.pageInfothroughuseGridTranslation(GRID_DEFAULT_TRANSLATIONS, about:470), so the repair reuses those four keys and adds fourGRID_DEFAULT_TRANSLATIONSrows. It adds no new pack key. - Family scan, same read: every
ChevronsLeft/ChevronsRight/ChevronLeft/ChevronRightJSX use underpackages/*/srcandapps/*/src, tests excluded. The only other icon-only paging pair with no name ispackages/plugin-detail/src/DetailView.tsxrecordNavigation(about:1490, a tooltip only). No reach was measured for that pair:recordNavigationhas 0 producers inpackages/app-shell/srcandapps/console/src. It stays out of this card.
Done when
- Each of the four
groupedPagerbuttons carriesaria-labelread throughuseGridTranslationfrom the fourtable.*keys. No literal English. - Pins:
- a grouped grid with more than one page of groups: each button is found by role and name and driven to the page it names;
- under
zh, the names come from thezhpack; groupedPagination.test.tsx's positional lookup moves to a role and name query.
Who acts, and in what order
- The
domain:uiexecution seat, after triage grades this card. - Serial: after PR fix(components,i18n): name the data table pager's four page buttons (objectui#12124) #12142 lands (it adds the four keys), and after the in-flight claim on objectui#12110, whose file surface includes
ObjectGrid.tsx.
Dedupe
Two repo-scoped issue searches on objectui, open and closed:
- "grouped grid pager first previous next last buttons have no accessible name aria-label": 3 hits (objectui#12124, a11y (axe wcag2a/aa): unnamed buttons and broken tab roles in the view tab bar, unbound labels on the settings form, and unnamed percent progress bars #11690, finding(components): element:text / button / image / number apply
aria.ariaLabelasaria-arialabel, so the declared accessible name never reaches the DOM #11051). - "groupedPager ObjectGrid unnamed chevron buttons screen reader": 5 hits (objectui#12124, Grouped grid under a toolbar search: send the search on the group header and group row queries once objectstack honours
searchon grouped queries #11021, [Decision] A grouped grid over a data source with no server group query: keep page grouping with the Partial marker (K), or refuse grouping loudly and retire the marker (F)? #10881, finding(plugin-grid):pagination.pageSize: 0在扁平路径穿透成 10、在分组种子被原样留下 —— 于是作者一写grouping,表格一个分组都不渲染而页数是 Infinity #9853, Cross-page "Select all N matching" can never appear in the console —totalMatchingis only written by ObjectGrid's own loader, which never runs under ListView's external pagination #4464).
None of them is the grouped pager's names.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
a11yarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3
Activity
objectstack-fleet commentedon Oct 11, 2026
Triage: first grade,
bug·a11y·priority:p3·domain:ui·area:records, intopm:queue. The family's third member, so it carries the enumeration pinTriage seat (seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-11T13:56Z. ⛔ Not a claim, ⛔ not a dispatch.Lane:
groupedPagerinpackages/plugin-grid/src/ObjectGrid.tsxisdomain:ui.Why p3: a screen reader announces four unnamed buttons on every multi-page grouped grid. Sighted use is unaffected. That is the grade of a11y: the data table pager's first / previous / next / last buttons have no accessible name #12124 and a11y (axe wcag2a/aa): unnamed buttons and broken tab roles in the view tab bar, unbound labels on the settings form, and unnamed percent progress bars #11690.
Direction, as filed: an
aria-labelthroughuseGridTranslation, using the fourtable.*keys PR fix(components,i18n): name the data table pager's four page buttons (objectui#12124) #12142 adds, plus fourGRID_DEFAULT_TRANSLATIONSrows, with no literal English. The positional test lookup moves to a role and name query.The family: icon-only buttons with no accessible name: a11y (axe wcag2a/aa): unnamed buttons and broken tab roles in the view tab bar, unbound labels on the settings form, and unnamed percent progress bars #11690 (view tab bar), a11y: the data table pager's first / previous / next / last buttons have no accessible name #12124 (data-table pager), and now this card. On the third occurrence the fix includes an enumeration pin:
packages/*/src(tests excluded) of icon-onlyButtons, meaning those whose only child is an icon component, that carry neitheraria-label,aria-labelledby,titlenor visually hidden text;DetailView.tsxrecordNavigationpair the filer found goes into the census, ⛔ not into this card's fix.It is a test in the package's own suite, ⛔ not a new CI gate (「新增门禁默认否」).
Sequence: after PR fix(components,i18n): name the data table pager's four page buttons (objectui#12124) #12142 (which adds the keys) and objectui#12110's in-flight claim on
ObjectGrid.tsx. This is ordering, not a block.Clause-②: no.objectstack-fleet commentedon Oct 11, 2026
Deferred this fire, not claimed: serial behind objectui#12110 ·
domain:uiseat 1 ·session_016djJF12Qt14ejKR5Vjt4sK· 2026-10-11T14:52Z⛔ Not a claim. The card stays in
pm:queue, unassigned.6109778067): after PR objectui#12142 and after objectui#12110's in-flight claim onObjectGrid.tsx.dc6d28e); the fourtable.*keys are onmain.6108276574). Its branch head50c7747editspackages/plugin-grid/src/ObjectGrid.tsx, this card's one source file. Samearea:records, same file ⇒ not dispatched.aria-labelliterals. This card's icon-only-button census stays a separate test, as triage says.objectstack-fleet commentedon Oct 11, 2026
Claim: PM loop round 2 (name the grouped pager's four buttons through
useGridTranslation, move the positional test lookup to role and name, and add the icon-only-button census, as triage6109778067directs)Session:
session_016djJF12Qt14ejKR5Vjt4sKAccount:
os-steveBranch:
claude/issue-12143-grouped-pager-namesWorktree:
objectui-issue-12143Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/plugin-grid/src/ObjectGrid.tsx(groupedPager's four buttons and fourGRID_DEFAULT_TRANSLATIONSrows only);packages/plugin-grid/src/__tests__/groupedPagination.test.tsx; one new census test (icon-onlyButtons overpackages/*/src, tests excluded); new pins;.changeset/12143-grouped-pager-names.md(stop on breach; explain in the report)Container & model:
M(four names plus the census),mode:subagent,model: default tier — dispatch-gates --tier --repo objectstack-ai/objectui: "no path-derived mandate"Clause-②: no
Responsibility:
objectui's grid: ObjectGrid.tsx groupedPager draws first / previous / next / last as four icon-only Buttons with no aria-label | the four table.* pack keys PR objectui#12142 added, read through useGridTranslation, which groupedPager already uses for table.rowsPerPage / table.pageInfo | every screen-reader user of a grouped list view with more than one page of groupsThread-read: 6110275420
Serial constraints cleared: none blocking. Read at 2026-10-11T20:46Z on objectui
mainc0c2f79:table.*keys) merged, and so did seat 2's objectui#12110 PR objectui#12151 (2026-10-11T20:05Z), which editedObjectGrid.tsx. The dev starts from that merged code. What remains on console rendering, from the HotCRM pass: column label precedence, star rating widget, reference rail (nameField, refresh), gantt year, approval drawer approver and headline, public form heading and editor hints #12110 is not on this surface.StudioHomePage, the page the maintainer calls retired. Decide whether to retire it, and what the root shows instead #12113 touch none of these files. Epic objectui#7611's C9 reader list names none of them.packages/componentsandexamples/schema-catalogonly.Clause-②: no.GRID_DEFAULT_TRANSLATIONSis not exported fromplugin-grid's entry (read onmainc0c2f79), and no pack key is added: the fourtable.*keys exist. If the dev finds a pack key is needed after all, the report says so and the seat corrects this line.objectstack-fleet commentedon Oct 11, 2026
os-dev-report
{
"issue": 12143,
"status": "done",
"branch": "claude/issue-12143-grouped-pager-names",
"pr": "#12160",
"session": "session_016djJF12Qt14ejKR5Vjt4sK — subagent run, the parent seat's id (container CLAUDE_CODE_REMOTE_SESSION_ID cse_016djJF12Qt14ejKR5Vjt4sK; the relay stamped the same)",
"premise_still_valid": true,
"summary": "groupedPager in ObjectGrid.tsx now gives each of its four icon-only Buttons aria-label={t('table.firstPage' | 'table.previousPage' | 'table.nextPage' | 'table.lastPage')}, read through useGridTranslation. GRID_DEFAULT_TRANSLATIONS gains the same four rows, byte-identical to en. No pack key was added and nothing is exported. In groupedPagination.test.tsx, the 'next' and 'last' lookups are now role-and-name queries instead of positional ones. A new pin, groupedPagerNames-12143.test.tsx, walks all four buttons by name to the page of groups each names, once with no provider and once under zh. A new census, packages/components/src/tests/iconOnlyButtonCensus-12143.test.ts, judges every Button under packages//src (tests excluded) whose only child is an icon and that has no aria-label, aria-labelledby or title. It freezes the unnamed ones as an exact-match ratchet keyed by file and icon. The census counted 35 unnamed at BASE c0c2f79 (20 files) and counts 31 at HEAD 08a3765 (19 files); the difference is exactly groupedPager's four. The DetailView recordNavigation pair is in the frozen list, as triage ruled. H1, H2 and H3 all held.",
"tests": "All runs at HEAD 08a3765, from repo root, through os-verify-lock (slot dev-12143). (1) Targeted vitest, 12 files: census, groupedPagerNames-12143, groupedPagination, ObjectGrid.groupedPagerSelect-11865, serverGrouping-7189, pagerButtonNames-12124, footerSummaryScope-12081, ObjectGrid.overlayTitleNoProviderFallback, app-shell defaults-maps-mirror-en-pack, i18n fallback-placeholder-spelling-3512, i18n ariaLabelLiteralCensus-12145, scripts defaults-population-parity-7904. Result: 'Test Files 12 passed (12) / Tests 102 passed (102)', VERDICT command-exit 0. (2) Census at BASE, before the fix, with an empty backlog: 32 'frozen 0, found N' entries, 35 sites in total, including 'packages/plugin-grid/src/ObjectGrid.tsx · ChevronsLeft/ChevronLeft/ChevronRight/ChevronsRight'. (3) Reverse verification from committed 08a3765, in a script with trap restore EXIT INT TERM and an absolute-path 'git checkout HEAD --' restore. Leg A put ObjectGrid.tsx back to BASE. On-disk counts went from 4 aria-labels and 4 defaults rows to 0 and 0, and the disk blob 354a18c1 equals the BASE blob. Result: 'Tests 6 failed | 7 passed (13)'. The census was red with exactly the four ObjectGrid chevrons ('frozen 0, found 1'), and its lit control was red too. Both groupedPagerNames cases and the two moved groupedPagination cases were red. The restore was proven: blob 4e241b4f equals HEAD and git diff HEAD is empty. Leg B deleted only the four defaults rows, using objectstack scripts/ablation-replace.mjs in wrap mode (anchor x1 to x0, blob changed, restore proven by the tool). Result: 'Tests 3 failed | 10 passed (13)'. The no-provider walk and the two groupedPagination cases were red; zh and the census stayed green, which is the expected direction. No dist was involved: the tests import ../ObjectGrid from source and the census reads source.",
"gates": [
"1 build closure: turbo run build --filter='@object-ui/plugin-grid^...' --concurrency=2 → 'Tasks: 13 successful, 13 total', VERDICT command-exit 0",
"2 type-check: pnpm --filter @object-ui/plugin-grid --filter @object-ui/components run type-check → both script names echoed, VERDICT command-exit 0; --listFilesOnly shows each tsconfig.test.json compiles the new test files",
"2 targeted vitest: 12 files, 102 tests passed (see tests)",
"3 node scripts/check-changeset-presence.mjs → exit 0 ('4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)')",
"3 pnpm check:changeset-claims → exit 0; pnpm check:pending-changeset-literals → exit 0; pnpm check:control-bytes → exit 0",
"3 pnpm check:new-line-citations → exit 0 ('0 new citation(s)'); pnpm check:phantom-deps → exit 0; pnpm check:i18n-keys → exit 0",
"derived: pnpm check:test-path-roots, check:i18n-dead-keys, check:i18n-drift, check:unreferenced-sources → all exit 0",
"eslint narrowing (declared, measured): population is eslint.config.js files '**/.{ts,tsx}'; --format json reports 4 files, 0 errors; warnings unchanged from BASE (ObjectGrid 207=207, groupedPagination 2=2, read via --stdin on the BASE text); 0 on the new files; no parserOptions.project/projectService, so no untouched file's verdict can move",
"control-byte self-scan of the 5 changed files: grep exit 1 (none)",
"CI at 08a3765 when this report was written: 42 check runs, 20 success, 3 skipped, 19 in_progress, 0 failed (in_progress, not awaited)"
],
"line_budget": "n/a. The diff touches no skills/** or governed ledger. +533/−14 across 5 files: ObjectGrid.tsx +10/−4, groupedPagination.test.tsx +5/−10, plus three new files: the census (370), the pin (141) and the changeset (7)",
"files_changed": [
"packages/plugin-grid/src/ObjectGrid.tsx (groupedPager's four buttons and four GRID_DEFAULT_TRANSLATIONS rows only)",
"packages/plugin-grid/src/tests/groupedPagination.test.tsx",
"packages/plugin-grid/src/tests/groupedPagerNames-12143.test.tsx (new pin)",
"packages/components/src/tests/iconOnlyButtonCensus-12143.test.ts (new census)",
".changeset/12143-grouped-pager-names.md (@object-ui/plugin-grid patch)"
],
"deviations": [
"Census definition, a declared widening: besides an icon element as the only child, it also judges a {cond ? A : B} toggle whose every branch is an icon. These render as icon-only Buttons. The widening adds four backlog entries (ActionResultDialog, PasswordField, DetailSection, SharedViewLink). An 'icon component' is a tag imported from lucide-react, or a tag whose name ends in Icon.",
"Census placement: packages/components, the package that owns Button. This follows the 12145 precedent of putting the census in the package that owns what it judges or remedies.",
"Leg A of the ablation was hand-rolled with a trap, because objectui has no ablation-replace.mjs. Leg B used objectstack's copy of that script.",
"A stray copy of the BASE ObjectGrid.tsx was written into the worktree as packages/plugin-grid/src/__og_base_probe.tsx by mistake. It was deleted in the very next command, before any commit or push, and git status was confirmed clean. No test or gate read it: the closure build that was running excludes plugin-grid itself, and the check gates had already finished.",
"Declared narrowing: the full @object-ui/plugin-grid and @object-ui/components suites and pnpm lint are left to CI.",
"Commit trailers follow AGENTS.md's model-free pair, not the harness reminder's model-named Co-Authored-By line, per the precedence rule in os-dev.",
"No conflict with the dispatch. origin/main moved to faf4ce9 during the run; that commit touches no Button, so no merge was needed."
],
"mcp_calls": "0 — no MCP tool used",
"api_writes": "2 relay strokes, each one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) executed as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectui/pulls (draft) and POST /repos//issues/12160/assignees ['os-steve']; read back 9708 bytes identical. (2) This os-dev-report comment: POST /repos//issues/12143/comments. Plus git push (not REST): the empty-branch probe and 08a3765. Labels: zero writes; the PR's 'package: components' / 'plugin' / 'tests' labels come from the labeler workflow.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the census's frozen BACKLOG (31 unnamed icon-only Buttons in 19 files, including the DetailView recordNavigation pair) · noted, not filed. Triage ruled that they go into the census, not into cards. Dedupe words: icon-only Button census, unnamed icon button aria-label, recordNavigation chevrons",
"carrier: 承接者:无 · noted, not filed (an observation, not reproduced in a running app). In server-grouped mode, a group whose rows span more than one server page draws its own data-table pager. That pager uses the same four table.* names, so a grid with that and more than one page of groups shows two buttons named 'Next page'. It is in the PR's Acceptance notes. Dedupe words: server grouped pager duplicate accessible names, groupedPager serverGrouping Next page",
"carrier: 承接者:无 · bounds of the census, stated in its header: native button elements, apps/*, names passed through a spread, and icons that arrive as values are not counted"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 11, 2026
ACCEPT: PR objectui#12160 (head
08a3765dfc) ·domain:uiseat 1 ·session_016djJF12Qt14ejKR5Vjt4sK· 2026-10-11T21:07ZReviewed against GitHub and the diff, not the report.
Verdict
main. The first line isFixes #12143, andClause-②: nosits at a line start.os-steve.ObjectGrid.tsx+10/−4 is exactlygroupedPager's fouraria-labels and fourGRID_DEFAULT_TRANSLATIONSrows.packages/components, the package that ownsButton, following the objectui#12145 precedent.content/docs/releases/.check-governed-merges --pr objectstack-ai/objectui#12160: NOT governed, 547 changed lines.Clause-②: noholds: no pack key is added (the fourtable.*keys exist onmain),GRID_DEFAULT_TRANSLATIONSis module-private, and nothing is exported.@object-ui/plugin-gridpatch. "No language-pack key is added" and "No export or prop changes" both hold. The four English names match the defaults rows.c0c2f79the census was red with 35 unnamed icon-only Buttons in 20 files. At HEAD it reads 31 in 19 files, and the difference is exactly the fourgroupedPagerchevrons.src/tree).ObjectGrid.tsxat BASE) went 6 failed / 7 passed. Leg B (defaults rows removed) went 3 failed / 10 passed, red where predicted. Both restores were blob-equal to HEAD.Out-of-scope findings
DetailViewrecordNavigationpair: they are in the census's frozenBACKLOG, as triage ruled ("goes into the census, ⛔ not into this card's fix"). Not filed.data-tablepager, so two buttons can both be named "Next page": Acceptance notes. It was not reproduced in a running app.button,apps/*, names passed through a spread, icons passed as values): Acceptance notes.Acceptance notes
Landing: ready and auto-merge through the merge queue once every check on
08a3765issuccessor skipped by design.objectstack-fleet commentedon Oct 11, 2026
Landed: PR objectui#12160 →
main2381c07·domain:uiseat 1 ·session_016djJF12Qt14ejKR5Vjt4sK· 2026-10-11T21:41Zfaf4ce9. The card closed ascompletedthroughFixes #12143.08a3765dfc, which ACCEPT6113764522covers.mainnow:useGridTranslation;packages/components/src/__tests__/iconOnlyButtonCensus-12143.test.tsfreezes the 31 remaining unnamed icon-only Buttons, so a new one turns it red.pm:dispatchedis removed in this act.domain:ui,area:records,a11yand the grade stay.