Skip to content

fix(components,i18n): name the data table pager's four page buttons (objectui#12124) - #12142

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-12124-pager-button-names
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-12124-pager-button-names

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #12124

Clause-②: yes

The data-table renderer's pager, which an ungrouped paged ObjectGrid draws, rendered first / previous / next / last as four Button size="icon" whose only child was a chevron icon. None had an accessible name, so a screen reader announced four unnamed buttons. Each now carries an aria-label read through the file's own useTableTranslation. No literal English. A grouped grid with more than one page of groups draws ObjectGrid's own group pager instead; that pager is not covered here and is tracked as objectui#12143.

Button Key en zh
first table.firstPage First page 第一页
previous table.previousPage Previous page 上一页
next table.nextPage Next page 下一页
last table.lastPage Last page 最后一页

What changed

  • packages/components/src/renderers/complex/data-table.tsx: four aria-label={t('table.KEY')} attributes on the pager buttons, and the four rows in TABLE_DEFAULT_TRANSLATIONS, the provider-less stand-in for the en pack.
  • packages/i18n/src/locales/*.ts: the four keys in the table block of all ten packs, each a real translation, placed after pageInfo. The names follow the namespace's camelCase and the previousPage / nextPage spelling the packs already use elsewhere.
  • .changeset/12124-pager-button-names.md: @object-ui/i18n minor (four pack keys), @object-ui/components patch.

Clause-②: yes is the claim's reading (comment 6108325399): the four keys widen @object-ui/i18n's published packs, so TranslationKeys (derived from the as const en pack) gains four members. Triage graded it no; the isolated contract review decides.

Pins

  • New packages/plugin-grid/src/__tests__/pagerButtonNames-12124.test.tsx, through a real paged ObjectGrid (12 rows, pages of 5):
    • no provider: getByRole('button', { name }) finds each of the four by the en pack's value, and each is driven. A click lands on the page its name promises; first and previous are disabled on page 1, next and last on the last page. So two swapped keys go red, not only a missing one.
    • under zh: each is found by the zh pack's value, each zh value differs from the en one, and no button carries the English name.
  • Switched: packages/plugin-grid/src/__tests__/footerSummaryScope-12081.test.tsx, the objectui#12123 test. Its next-page lookup document.querySelector('button svg.lucide-chevron-right.h-4')?.closest('button') is now screen.getByRole('button', { name: en.table.nextPage }).

Evidence (HEAD cae900c)

The defect, and each pin's ability to fail. Each leg was run from the committed fix and restored; after each, the file's blob equals the HEAD blob 1d85896 and git diff HEAD is empty.

Leg Mutation (landing proven on disk) Result
A, the BASE renderer data-table.tsx replaced by its BASE blob fbc18df (packs and tests at HEAD); pager aria-label count on disk 0 3 failed / 18 passed: Unable to find an accessible element with the role "button" and name "First page", the same for "Next page" in the switched footer test, and for "下一页" under zh
B, one label ablated aria-label={t('table.nextPage')} to data-ablated=… via objectstack's scripts/ablation-replace.mjs: anchor 1 to 0, blob 1d85896 to 475ebf9 3 failed: "Next page" twice, "下一页" once
C, first and last swapped two nested ablation-replace runs, each anchor 1 to 0 2 failed, both at expect(button('firstPage')).toBeDisabled(): Received element is not disabled

At HEAD the same two files run 21 passed (2 files).

Gates (all at HEAD cae900c)

  • Build: dependency closure pnpm --filter '@object-ui/components^...' run build, then the rest of @object-ui/plugin-grid's closure (components, fields, permissions, providers, mobile, plugin-detail). Both exited 0; packages/i18n/dist/locales/en.d.ts carries firstPage.
  • Type-check: pnpm --filter @object-ui/i18n type-check, @object-ui/components type-check and @object-ui/plugin-grid type-check all exit 0. The plugin-grid test project lists both touched test files (tsc --listFilesOnly).
  • Tests, repo-root form: packages/i18n/ plus RelatedList.serverpagination.test.tsx (its /next/i role query shares a screen with a data-table): 83 files passed. packages/components/ in 4 shards: 94, 94, 94 and 92 passed (1 skipped), 375 files in all. Ratchets column-identity.ratchet.test.ts and one-authority-per-exported-name-6273.test.ts: 2 passed. Root suite scripts/__tests__/ in 3 shards: 60 (1 skipped), 59 (1 skipped) and 60 passed, 181 files in all.
  • i18n family: check:i18n-keys (every createSafeTranslation defaults row repeats the en value), check:i18n-drift (4 keys added, 0 en values changed), check:i18n-dead-keys (report; none of the four listed), check:i18n-designer-parity: all exit 0.
  • Others, all exit 0: check-changeset-presence.mjs (13 source files of 3 released packages, 1 changeset), changeset:check, check:changeset-claims, check:pending-changeset-literals, check:new-line-citations (0 new), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:phantom-deps.
  • Lint, package form (eslint from each package dir, inline config honoured) on the 13 touched files: 0 errors. 34 warnings, all in data-table.tsx and none on a changed line; BASE's data-table.tsx has the same 34.
  • Eager closure. Measured at the package dist, 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. The console-build delta (minified, chunked) and check:eager-locale-catalogues are NOT MEASURED. Reason: both need the whole workspace plus apps/console built. No import or specifier moved, so the catalogue composition that gate weighs is unchanged by construction. performance-budget.yml measures both in CI.

Acceptance notes

Two findings outside this card, left untouched. Both are handed to the seat in the report.

  • The grouped grid's pager has the same four unnamed buttons. ObjectGrid's own groupedPager (in packages/plugin-grid/src/ObjectGrid.tsx) draws first / previous / next / last as icon-only Buttons with no name. It renders for every grouped grid with more than one page of groups. It already reads table.rowsPerPage and table.pageInfo, so the four keys this change adds are the ones it needs. Not edited here: the file is outside the claim's file surface. Its own tests find these buttons by DOM position (groupedPagination.test.tsx: "The next chevron is the 3rd of the 4 nav buttons"). The in-process role-query probe is NOT MEASURED: the verify lock answered queue-timeout twice.
  • The row-actions trigger's name is literal English. The data table's row menu trigger carries aria-label="Row actions" in every locale. A localization gap, not a missing name. Code read only; the zh probe is NOT MEASURED for the same reason.

Not in this change: the other tests that reach a pager button by position (groupedPagination.test.tsx, serverPagination.test.tsx, data-table-manual-pagination.test.tsx) keep their lookups. The card names only the objectui#12123 test.

Session: https://claude.ai/code/session_01TYgwmFK1q4KJ6Qq2WRLzsD


Generated by Claude Code

…objectui#12124)

The data-table pager drew first / previous / next / last as icon-only
Buttons with no accessible name. Each now reads its aria-label through the
renderer's own useTableTranslation, from four new table.* keys added to
TABLE_DEFAULT_TRANSLATIONS and to all ten locale packs.

Pins: a real paged ObjectGrid's four buttons are found by role and name and
driven to the page each names, provider-less (en defaults) and under zh;
the objectui#12081 footer suite's chevron-icon lookup becomes the role query.

Claude-Session: https://claude.ai/code/session_01TYgwmFK1q4KJ6Qq2WRLzsD
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 290 chunks) 3175.1 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-BJl5kjoi.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.31KB 141.42KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.07KB 70.00KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 249.83KB 65.92KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.80KB 46.04KB
plugin-gantt (index.js) 179.37KB 45.17KB
plugin-grid (index.js) 255.15KB 70.92KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.77KB 31.19KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…ds are not covered (objectui#12124)

The changeset said no type member is added, but the four keys add members
to the exported en pack and so to TranslationKeys. It also said every paged
ObjectGrid draws the data-table pager; a grouped grid with more than one page
of groups draws its own group pager, which stays unnamed (objectui#12143).

Claude-Session: https://claude.ai/code/session_01TYgwmFK1q4KJ6Qq2WRLzsD
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 290 chunks) 3175.1 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-BJl5kjoi.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.31KB 141.42KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.07KB 70.00KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 249.83KB 65.92KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.80KB 46.04KB
plugin-gantt (index.js) 179.37KB 45.17KB
plugin-grid (index.js) 255.15KB 70.92KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.77KB 31.19KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1b02eac7144ceed219fa51ef2945f81c7973e493
Local-runs: none

Rendered by an isolated subagent of the seat session, read-only, from these inputs and nothing else: card objectui#12124 (body and all three comments: triage 6106865988, claim 6108325399, dev report 6109512642), PR objectui#12142 (body, file list, net diff against main at the head, and its two commits), the head's check-runs, and the head's package.json publish fields, src/index.ts, src/locales/index.ts and src/locales/en.ts of @object-ui/i18n plus data-table.tsx of @object-ui/components. Nothing built, run or re-run.

① Derived judgments

Head versus the report. The PR holds two commits: the report's cae900c and 1b02eac on top. The compare between them touches only .changeset/12124-pager-button-names.md, two prose lines (the grouped-pager carve-out and the TranslationKeys sentence). So the report's evidence at cae900c describes this head's code byte for byte; its "nothing pushed after" predates that one changeset edit. Not a defect.

Every accept-set and public-surface change the diff implies, each judged:

  1. @object-ui/i18n: a WIDENING, judged RIGHT. Four keys (table.firstPage, table.previousPage, table.nextPage, table.lastPage) are added to the table block of all ten packs. en is as const and export type TranslationKeys = typeof en (locales/en.ts); the entry re-exports that type (src/index.ts through i18n.ts) and so does the ./locales subpath (locales/index.ts); en itself is exported at ., ./locales and ./locales/en (exports in package.json), and builtInLocales holds all ten. So the published TranslationKeys type and every exported pack gain four members, and a t typed over that object accepts four keys it refused before. Purely additive: no key renamed, moved or removed, and no existing value changed (each pack's hunk is four + lines and nothing else). The ten values are real translations, not English, as triage required; every pack adds the same four keys, so pack parity holds by construction.

  2. @object-ui/components: NO public-surface change, judged RIGHT as a behaviour fix. TABLE_DEFAULT_TRANSLATIONS is a module-local const, typed as a Record of string keys to string values and not exported (the file's seven export declarations are unchanged and none names it); it feeds createSafeTranslation, whose defaults parameter is already string-keyed, so four more rows widen no type. The four aria-label={t('table.KEY')} attributes change the rendered DOM of the pager (each icon button gains an accessible name) and nothing else: no prop, export, schema key or package.json publish field moves. Each new defaults row repeats the en value for its key, the invariant the dev's check:i18n-keys holds. The name is read through the file's own useTableTranslation, the hook triage named, with no literal English.

  3. @object-ui/plugin-grid: nothing published changes; the diff touches only two files under src/__tests__/. The switched objectui#12123 test moves from a chevron-icon selector to the role query triage asked for. The new pin finds each of the four by role and the pack's name through a real paged ObjectGrid, drives each to the page its name promises, checks the disabled state at both ends (so two swapped keys go red, as the dev's leg C shows), and under zh asserts the zh names and the absence of the English ones. Names are read from the packs, never from literals. Both tests import from @object-ui/i18n/locales, the all-ten door, which is the documented use for a test.

Check-runs on the head, read at 2026-10-11T13:49Z: 42 runs. 35 completed with conclusion success, among them Lint, Type Check, Build & E2E, Test (dist pins), Test (shard 1/8), Test (shard 6/8), Test (shard 7/8), Test (shard 8/8), Spec Main Shape Gate, Governed Surface Queue Guard, Line Citation Gate, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read, Changeset Overwrite Report, Docs Route Eager Closure Check, Bundle Analysis, Inert vi.mock Specifier Check, Control Byte Scan. 3 completed with conclusion skipped: Test (coverage), the coverage shard matrix placeholder, and dependabot, none a gate verdict for this PR. 4 in_progress with no conclusion yet: Test (shard 2/8), Test (shard 3/8), Test (shard 4/8), Test (shard 5/8). 0 with conclusion failure. The four open shards are the gate on the full plugin-grid suite the dev narrowed locally; this record does not stand in for them. in_progress is not a pass, and the seat reads them concluded success before any ready or queue step.

② Semver level

Changeset .changeset/12124-pager-button-names.md: @object-ui/i18n: minor, @object-ui/components: patch, no major (objectui's fixed-group rule). @object-ui/plugin-grid is not named, and rightly: its two touched files are tests, which publish nothing (files publishes dist), and the fixed group carries it with the others. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check and Changeset Claim Re-read are success on this head.

  • minor for @object-ui/i18n matches what the diff publishes: a widening of an exported type and of every exported pack, more than a patch and not breaking.
  • patch for @object-ui/components matches: a user-visible fix with no surface change.
  • The changeset body says what an upgrader reads: the four keys, the renderer's defaults, that the exported en pack and so TranslationKeys gain four table members, that no export or prop is added, and that the grouped pager is not covered. Model-free; cites the card as objectui#12124, no line address.

Clause-②: line. The PR body carries Clause-②: yes, no arm. Judged RIGHT. The widening in ① item 1 is a published-surface widening of @object-ui/i18n, so the answer is yes, and yes takes at least minor, which the i18n entry gives. Triage (6106865988) graded no; the claim (6108325399) read yes on the same direction and deferred to this review; the dev kept yes and said why in the body. This record settles it: yes. Had the four keys stayed inside components' own defaults and no pack moved, no would have been the line; that is not this diff.

③ Boundary flags

open_questions: none declared. Every dev flag (the report's deviations and out_of_scope_findings, and the PR body's NOT MEASURED lines), answered or escalated:

  1. File surface: all 14 files sit inside the claim's named surface; the two plugin-grid files are the switched objectui#12123 test the claim names and the new pins. Answered.
  2. Red-on-BASE measured as a BASE-blob swap of data-table.tsx inside the worktree, not a separate BASE checkout: the blob was checked on disk and the three legs went red in the predicted direction, an acceptable shape for a single-file renderer fix. Answered.
  3. Whole packages/plugin-grid/ suite not run locally; only the two touched tests ran. Escalated to the head's check-runs: Test (shard 1/8), 6/8, 7/8, 8/8 and Test (dist pins) are success; shards 2, 3, 4 and 5 are in_progress at the reading in ①. The seat waits for them; nothing here vouches for a shard that has not concluded.
  4. check:eager-locale-catalogues and the console-build delta NOT MEASURED: the dev's reason stands on the diff. No import or specifier moves, only keys inside existing object literals, so the catalogue composition that gate weighs is unchanged by construction; Docs Route Eager Closure Check and Bundle Analysis are success on the head. Answered.
  5. Out-of-scope finding A, the grouped grid's own pager (groupedPager in packages/plugin-grid/src/ObjectGrid.tsx), the same defect on a sibling pager, outside the claim's surface and not edited: the PR body and the changeset name its carrier as objectui#12143. That card is outside this record's inputs and was not opened. Answered by a named carrier.
  6. Out-of-scope finding B, the data table's row-actions trigger carrying aria-label="Row actions" as literal English in every locale (present at the head, unchanged): a localization gap outside the card, noted in the PR's acceptance notes with no carrier. ESCALATED to the seat: a user-visible defect takes a card rather than an acceptance note, so the seat files it or records why not. It does not block this PR, whose card is the pager's missing names.
  7. The in-process probes for A and B NOT MEASURED (verify-lock queue timeout twice, the lock held by another dev's chain): both findings are declared as code reads, which is what they are. Answered as declared.
  8. origin/main not merged; mergeable_state reads behind, mergeable true, and the dev's compare shows no overlap with the touched files. The queue rebuilds on current main. Not a contract matter.
  9. Commit trailers written in objectui AGENTS.md's model-free pair rather than the harness reminder's form: the repo rule outranks the reminder, so this is correct. The PR body's footer is the session-URL form a PR body takes. Answered.
  10. Worktree removed after the PR opened: housekeeping. Answered.

Not governed: no path in the file list is a governed surface (Governed Surface Queue Guard is success on the PR leg), 210 changed lines, head repo equals base repo. The PR is a dispatched dev's draft; readying and queueing stay the seat's acts, after the four open shards conclude.

Implemented-by: claude/issue-12124-pager-button-names
Reviewed-by: session_01TYgwmFK1q4KJ6Qq2WRLzsD

VERDICT: PASS


Generated by Claude Code

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants