Skip to content

Commit d5da45b

Browse files
fix(plugin-grid): over the 5000-record cap, the select-all-matching bulk bar says only the first 5000 matching records will be processed (#28606)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} --> _Requested by **Steedos** · [project thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbQQZgSv2t6j3vyRpg7oswBa)_ Fixes #28552 Clause-②: yes (widening) Metadata-protocol: none ## 维护者速读 - **改了什么**:列表「选择全部匹配记录」的批量栏,在匹配数超过 5000 时,横幅、「选择全部」按钮、已选计数和选区右键菜单都改为点名上限,例如「将处理前 5000 条(共匹配 7200 条记录)。」;5000 及以下文案不变。上限与实际取数(`resolveBulkRows`)读同一个常量 `BULK_SELECT_ALL_MATCHING_CAP`。 - **为什么改**:维护者裁决(#28345 第 ④ 条)。原来横幅按服务器总数说「已选择全部 7200 项」,批量删除实际只处理前 5000 条,屏幕上什么都不说,是破坏性路径上的静默部分执行。 - **风险与回滚**:低。只改 plugin-grid 的三处文案选择和十个语言包各加三条 `grid.*` 文案;取数逻辑、上限数值、接口都不变。回滚即还原本 PR。 - **先看**:`ui/packages/plugin-grid/src/components/BulkActionBar.tsx`(`overCap` 三处分支)、`ui/packages/plugin-grid/src/bulkSelectAllMatchingCap.ts`、`BulkActionBar.selectAllMatchingCap-28552.test.tsx`。 - **两分钟验证**:`pnpm --dir ui exec vitest run packages/plugin-grid/src/__tests__/BulkActionBar.selectAllMatchingCap-28552.test.tsx packages/plugin-grid/src/__tests__/bulkFanoutHostParams.test.tsx`,预期 2 个文件、16 个测试全绿。 - **需要你做的**:无,席位落地。 ## What changes - **One constant.** `ui/packages/plugin-grid/src/bulkSelectAllMatchingCap.ts` holds `BULK_SELECT_ALL_MATCHING_CAP` (5000) and `exceedsBulkSelectAllMatchingCap(totalMatching)`. `resolveBulkRows` in `ObjectGrid.tsx` stops its paged fan-out at that constant (it was a local literal), and every string below reads the same constant. The module is not exported from the package entry. - **Over the cap, the bar names it** (`BulkActionBar`): the select-all button reads `grid.bulkSelectAllMatchingCapped` ("Select the first 5000 of 7200 matching"), the banner after selecting reads `grid.bulkAllMatchingSelectedCapped` ("Only the first 5000 of 7200 matching records will be processed."), and the count chip reads `grid.bulkSelectedAllMatchesCapped` ("5000 selected (of 7200 matches)"). - **The selection's context menu** (`SelectionContextMenuItems` in `RowContextMenu.tsx`) counted `totalMatching` with the same `grid.bulkSelectedAllMatches` key; over the cap it now reads the same capped key. This is the same defect on the second surface that renders the count, so it is fixed here. - **At or under the cap, nothing changes**: the three existing keys, with `count`, as before. - **Strings** (`@object-ui/i18n`, `grid` namespace, all ten built-in packs): three new keys interpolating `cap` and `total`. They take no `count`, so they are not i18next count families. In `ru` and `ar` the total stands alone (in parentheses or after a preposition with no noun) so no noun has to agree with it; the cap is always 5000. - One root changeset: `@object-ui/plugin-grid` patch, `@object-ui/i18n` patch. No `packages/spec/**`, no server change, no metadata key. ## Why Ruling 6545938831 on #28345, item ④, verbatim: 「bulk-select-all-matching: file a product defect card — the banner names the cap (e.g. 「将处理前 5000 条」); the checklist gains one criterion pinning it.」 The checklist criterion rides #28551, not this PR. **Clause-② reading** (measured on the built declaration closure, objectstack `a2e75562`, `turbo run build --filter='@object-ui/plugin-grid^...'`, 49/49 tasks): plugin-grid exports no language pack (its `package.json` has only the `.` entry). The grid strings live in `@object-ui/i18n`, which publishes `./locales` and `./locales/*` and re-exports `en` and `type TranslationKeys` (`typeof en`) from its `.` entry; the built `dist/locales/en.d.ts` declares each leaf as a readonly string literal. So the three new keys widen the published declaration of `en` and `TranslationKeys`: `Clause-②: yes (widening)`, as amended on the card's Claim. Nothing is narrowed and no metadata author surface moves: `Metadata-protocol: none`, patch. ## Tests - New `ui/packages/plugin-grid/src/__tests__/BulkActionBar.selectAllMatchingCap-28552.test.tsx`, nine cases, strings read from the packs and the cap from the constant: - the constant is the 5000 the fan-out pin already holds; - over the cap (7200 matches, a 50-row page): the button reads the capped key and not today's; after selecting, the banner reads the capped key and contains the cap, and the chip reads the capped key; under `zh` the banner reads the `zh` pack; the selection's context menu reads the capped key; - exactly at the cap (5000) and under it (1200): the button, the banner and the chip read today's three keys. - `bulkFanoutHostParams.test.tsx`, "still honours HARD_CAP over a match set larger than the cap": in the same ObjectGrid run that delivers 5000 of 6000 records to the bulk delete, the banner now reads the capped key filled with `BULK_SELECT_ALL_MATCHING_CAP` and 6000, and the delivered length equals the constant. So the number shown and the number delivered are pinned in one run. ## Acceptance notes - The numbers are shown unformatted (5000, not 5,000), the same as every existing count in these strings. - The banner is only reachable when the host passes find params (`canOfferSelectAllMatching`), unchanged. ## Verification All at head `a9224125` (this branch merged `origin/main` `cd065d36`; the merge touched none of this diff's packages, and the delta against main is still these 17 files, +246 / -15), in a worktree, every build and test under `scripts/pm/os-verify-lock.sh`. <details><summary>Tests, ablations, type-check, lint</summary> - New file alone, verbose: `Tests 9 passed (9)`. - Ablation 1 (`node scripts/ablation-replace.mjs`, `BulkActionBar.tsx`, anchor ` const overCap = exceedsBulkSelectAllMatchingCap(totalMatching);` replaced by ` const overCap = false && exceedsBulkSelectAllMatchingCap(totalMatching);`, anchor x1 to x0) over the new file and `bulkFanoutHostParams.test.tsx`: `Tests 4 failed | 12 passed (16)`, the three bar cases over the cap and the fan-out case; the context-menu case stays green because it reads the cap on its own. Restored: blob equal to HEAD. - Ablation 2 (`RowContextMenu.tsx`, anchor ` ? exceedsBulkSelectAllMatchingCap(totalMatching)` replaced by ` ? false && exceedsBulkSelectAllMatchingCap(totalMatching)`) over the new file: `Tests 1 failed | 8 passed (9)`, the context-menu case. Restored: blob equal to HEAD. - `pnpm --dir ui exec vitest run packages/plugin-grid/` (before the merge of main, at `08c376a5`): `Test Files 270 passed (270)`, `Tests 2349 passed (2349)`. - `pnpm --dir ui exec vitest run packages/i18n/` (at `08c376a5`): `Test Files 94 passed (94)`, `Tests 1438 passed | 13 skipped (1451)`; this includes `count-families-11445` and `all-locales-key-parity`. - After the merge, at `a9224125`: the new file, `bulkFanoutHostParams`, `BulkActionBar` and `rowContextMenu-28434`: `Test Files 4 passed (4)`, `Tests 41 passed (41)`. - `pnpm --filter @object-ui/plugin-grid run type-check && pnpm --filter @object-ui/i18n run type-check`: `VERDICT command-exit 0`; plugin-grid's `tsconfig.test.json` lists both touched test files. - eslint on the five plugin-grid files and the `en` and `zh` packs: 0 errors; the 30 warnings are existing `any`s, and this diff adds no `any`. - `pnpm check:new-line-citations` (from `ui/`): `0 new citation(s)`. </details> <details><summary>Derived dispatch gates</summary> - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 35 commands over the 17 paths, the same list before and after the merge of main; every one exited 0, the 29 that are neither a suite nor a PR-body gate re-run at `a9224125`. `--ran`: 35 derived, 35 run, 0 NOT-MEASURED. The PR-body gates (`check-closing-keyword-parity`, `check-adr-0087-registration --base origin/main`, `check-changeset-no-major --base origin/main`, `check-empty-changeset --base origin/main`) ran against this body through `GITHUB_EVENT_PATH`. - `pnpm --dir ui exec vitest run scripts/` (at `08c376a5`; the merge of main changed no ui script): `Test Files 169 passed | 2 skipped (171)`, `Tests 4900 passed | 2 skipped (4902)`. - `node scripts/check-sdui-manifest.mjs`: present and intact (106 components); CI's Build & E2E UI regenerates and compares it. - `pnpm check:type-check-debt` (at `a9224125`): `0 raw tsc error(s) total, none above its recorded number`. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QsR2jTrUjyA3Jhqgsi86rz --- _Generated by [Claude Code](https://claude.ai/code/session_01QsR2jTrUjyA3Jhqgsi86rz)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 479ad08 commit d5da45b

17 files changed

Lines changed: 246 additions & 15 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/plugin-grid': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(plugin-grid): over the 5000-record cap, the "select all matching" bulk bar says that only the first 5000 matching records will be processed
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: none
10+
11+
A grid's "select all matching" bulk action hands the action at most 5000 records, but the bulk bar counted the whole match set. With 7200 matches it read "All 7200 matching records are selected.", and a bulk delete then processed the first 5000 and said nothing about the rest.
12+
13+
- **Over the cap, the bar names it.** When the match set is larger than 5000, the select-all button reads "Select the first 5000 of 7200 matching", the banner reads "Only the first 5000 of 7200 matching records will be processed.", and the selection count, in the bar and in the selection's context menu, reads "5000 selected (of 7200 matches)".
14+
- **At or under the cap, nothing changes.** The bar keeps today's text.
15+
- **One number.** The fan-out and every one of those strings read one constant in `@object-ui/plugin-grid`, so the screen and the action cannot disagree.
16+
- **New strings.** `@object-ui/i18n` gains `grid.bulkSelectAllMatchingCapped`, `grid.bulkAllMatchingSelectedCapped` and `grid.bulkSelectedAllMatchesCapped` in all ten built-in packs. They interpolate `cap` and `total`.

‎ui/packages/i18n/src/locales/ar.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,9 @@ const ar = {
506506
bulkAllOnPage_other: "تم تحديد جميع العناصر ({{count}}) في هذه الصفحة.",
507507
bulkSelectAllMatching: "تحديد كل النتائج المطابقة ({{count}})",
508508
bulkAllMatchingSelected: "تم تحديد جميع السجلات المطابقة ({{count}}).",
509+
bulkSelectAllMatchingCapped: "تحديد أول {{cap}} من النتائج المطابقة ({{total}})",
510+
bulkAllMatchingSelectedCapped: "ستتم معالجة أول {{cap}} سجل فقط من السجلات المطابقة ({{total}}).",
511+
bulkSelectedAllMatchesCapped: "تم تحديد {{cap}} (إجمالي النتائج المطابقة: {{total}})",
509512
bulkActions: "إجراءات جماعية",
510513
bulk: {
511514
confirmDefault: "سيُطبَّق هذا على {{count}} من السجلات.",

‎ui/packages/i18n/src/locales/de.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ const de = {
468468
bulkAllOnPage_other: "Alle {{count}} auf dieser Seite sind ausgewählt.",
469469
bulkSelectAllMatching: "Alle {{count}} Treffer auswählen",
470470
bulkAllMatchingSelected: "Alle {{count}} übereinstimmenden Datensätze sind ausgewählt.",
471+
bulkSelectAllMatchingCapped: "Die ersten {{cap}} von {{total}} Treffern auswählen",
472+
bulkAllMatchingSelectedCapped: "Nur die ersten {{cap}} von {{total}} übereinstimmenden Datensätzen werden verarbeitet.",
473+
bulkSelectedAllMatchesCapped: "{{cap}} ausgewählt (von {{total}} Treffern)",
471474
bulkActions: "Massenaktionen",
472475
bulk: {
473476
confirmDefault: "Dies wird auf {{count}} Datensätze angewendet.",

‎ui/packages/i18n/src/locales/en.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ const en = {
720720
bulkAllOnPage_other: 'All {{count}} on this page are selected.',
721721
bulkSelectAllMatching: 'Select all {{count}} matching',
722722
bulkAllMatchingSelected: 'All {{count}} matching records are selected.',
723+
bulkSelectAllMatchingCapped: 'Select the first {{cap}} of {{total}} matching',
724+
bulkAllMatchingSelectedCapped: 'Only the first {{cap}} of {{total}} matching records will be processed.',
725+
bulkSelectedAllMatchesCapped: '{{cap}} selected (of {{total}} matches)',
723726
bulkActions: 'Bulk actions',
724727
empty: 'Empty',
725728
yes: 'Yes',

‎ui/packages/i18n/src/locales/es.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,9 @@ const es = {
481481
bulkAllOnPage_other: "Están seleccionados los {{count}} de esta página.",
482482
bulkSelectAllMatching: "Seleccionar las {{count}} coincidencias",
483483
bulkAllMatchingSelected: "Están seleccionados los {{count}} registros coincidentes.",
484+
bulkSelectAllMatchingCapped: "Seleccionar las primeras {{cap}} de {{total}} coincidencias",
485+
bulkAllMatchingSelectedCapped: "Solo se procesarán los primeros {{cap}} de {{total}} registros coincidentes.",
486+
bulkSelectedAllMatchesCapped: "{{cap}} seleccionados (de {{total}} coincidencias)",
484487
bulkActions: "Acciones masivas",
485488
bulk: {
486489
confirmDefault: "Esto se aplicará a {{count}} registro(s).",

‎ui/packages/i18n/src/locales/fr.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ const fr = {
476476
bulkAllOnPage_other: "Les {{count}} éléments de cette page sont sélectionnés.",
477477
bulkSelectAllMatching: "Sélectionner les {{count}} correspondances",
478478
bulkAllMatchingSelected: "Les {{count}} enregistrements correspondants sont tous sélectionnés.",
479+
bulkSelectAllMatchingCapped: "Sélectionner les {{cap}} premières des {{total}} correspondances",
480+
bulkAllMatchingSelectedCapped: "Seuls les {{cap}} premiers des {{total}} enregistrements correspondants seront traités.",
481+
bulkSelectedAllMatchesCapped: "{{cap}} sélectionnés (sur {{total}} résultats)",
479482
bulkActions: "Actions groupées",
480483
bulk: {
481484
confirmDefault: "Ceci s'appliquera à {{count}} enregistrement(s).",

‎ui/packages/i18n/src/locales/ja.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ const ja = {
468468
bulkAllOnPage_other: "このページの {{count}} 件がすべて選択されています。",
469469
bulkSelectAllMatching: "該当する {{count}} 件すべてを選択",
470470
bulkAllMatchingSelected: "該当する {{count}} 件のレコードがすべて選択されています。",
471+
bulkSelectAllMatchingCapped: "該当する {{total}} 件のうち先頭の {{cap}} 件を選択",
472+
bulkAllMatchingSelectedCapped: "該当する {{total}} 件のうち、処理されるのは先頭の {{cap}} 件のみです。",
473+
bulkSelectedAllMatchesCapped: "{{cap}} 件を選択中(該当 {{total}} 件中)",
471474
bulkActions: "一括操作",
472475
bulk: {
473476
confirmDefault: "{{count}} 件のレコードに適用されます。",

‎ui/packages/i18n/src/locales/ko.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ const ko = {
468468
bulkAllOnPage_other: "이 페이지의 {{count}}건이 모두 선택되었습니다.",
469469
bulkSelectAllMatching: "일치하는 {{count}}건 모두 선택",
470470
bulkAllMatchingSelected: "일치하는 레코드 {{count}}건이 모두 선택되었습니다.",
471+
bulkSelectAllMatchingCapped: "일치하는 {{total}}건 중 처음 {{cap}}건 선택",
472+
bulkAllMatchingSelectedCapped: "일치하는 레코드 {{total}}건 중 처음 {{cap}}건만 처리됩니다.",
473+
bulkSelectedAllMatchesCapped: "{{cap}}건 선택됨(일치 항목 {{total}}건 중)",
471474
bulkActions: "일괄 작업",
472475
bulk: {
473476
confirmDefault: "레코드 {{count}}건에 적용됩니다.",

‎ui/packages/i18n/src/locales/pt.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,9 @@ const pt = {
476476
bulkAllOnPage_other: "Os {{count}} desta página estão selecionados.",
477477
bulkSelectAllMatching: "Selecionar todas as {{count}} correspondências",
478478
bulkAllMatchingSelected: "Todos os {{count}} registros correspondentes estão selecionados.",
479+
bulkSelectAllMatchingCapped: "Selecionar as primeiras {{cap}} de {{total}} correspondências",
480+
bulkAllMatchingSelectedCapped: "Apenas os primeiros {{cap}} de {{total}} registros correspondentes serão processados.",
481+
bulkSelectedAllMatchesCapped: "{{cap}} selecionados (de {{total}} correspondências)",
479482
bulkActions: "Ações em massa",
480483
bulk: {
481484
confirmDefault: "Isto será aplicado a {{count}} registro(s).",

‎ui/packages/i18n/src/locales/ru.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ const ru = {
487487
bulkAllOnPage_other: "Выбраны все записи на этой странице ({{count}}).",
488488
bulkSelectAllMatching: "Выбрать все совпадения ({{count}})",
489489
bulkAllMatchingSelected: "Выбраны все подходящие записи ({{count}}).",
490+
bulkSelectAllMatchingCapped: "Выбрать первые {{cap}} из совпадений ({{total}})",
491+
bulkAllMatchingSelectedCapped: "Будут обработаны только первые {{cap}} подходящих записей из {{total}}.",
492+
bulkSelectedAllMatchesCapped: "Выбрано: {{cap}} (всего совпадений: {{total}})",
490493
bulkActions: "Массовые действия",
491494
bulk: {
492495
confirmDefault: "Будет применено к записям: {{count}}.",

0 commit comments

Comments
 (0)