Skip to content

Commit eeaa827

Browse files
refactor(plugin-grid): move ObjectGrid's authored-input diagnostics into a sibling hook (slice 3) (#26364)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi94Cr2fbhp665MVvsYFfqYFe)_ Part of #25994 (slice 3 of the move-only split, the first slice inside the `ObjectGrid` component body; slices 1 and 2 landed as #26323 and #26354, and the card stays open for the slices listed below). Clause-②: no Metadata-protocol: none ## What moved Base `222755eb` (slice 2 merged). `ui/packages/plugin-grid/src/ObjectGrid.tsx` is 6637 lines there: imports and re-exports, the module-level code that tests pin in place, and the `ObjectGrid` component from line 673 to the end. This slice moves concern **Q** of the seam table below, verbatim, into a custom hook `useObjectGridDiagnostics` in a new sibling module `ui/packages/plugin-grid/src/objectGridDiagnostics.ts`. Q is the three authored-input diagnostics, each a `useEffect` with one `console.warn`, with their whole comment blocks: - a `columns` entry the renderer cannot read (objectui#5349), through `describeUnresolvedColumns`; - an unusable `bulkActionDefs` member (objectui#8730), through `describeUnusableBulkActionDefs`; - a `pagination.pageSize` or flat `pageSize` that is not a positive integer (objectui#9853), through `describeNonPositivePageSize`. Q reads only `schema` and declares nothing any other region reads, so the hook takes `schema: ObjectGridSchema` and nothing else. It declares no state, adds no context and returns nothing. `ObjectGrid` calls `useObjectGridDiagnostics(schema)` at the exact place the moved lines stood (after the column summaries, before `generateColumns`), so the order of the component's hooks does not change. The three `describe*` import specifiers move with their only readers. No identifier is renamed. Nothing new is exported from `ObjectGrid.tsx`, and the package entry is untouched. `ObjectGrid.tsx` goes from 6637 to 6563 lines. Move-only proof: the multiset of non-blank lines of the base `ObjectGrid.tsx` equals the new `ObjectGrid.tsx` plus `objectGridDiagnostics.ts`. Nothing is missing. The only added lines are the new module's header comment, its five imports, the hook's docblock, signature and closing brace, and in `ObjectGrid.tsx` the new import plus the call with its two-line comment. The three edited import lines differ only by the removed specifiers. Kept away from #26341 (#25884): its `ObjectGrid.tsx` edits are `temporalGroupHeading` and the seven column-cell call sites inside `generateColumns` (region R). This slice touches neither, and its only hunk in the component body ends above `generateColumns`. ## Test edits None. No test reads the moved lines as source: I checked every test that reads `ObjectGrid.tsx` (the nine in `plugin-grid/src/__tests__` and the fourteen elsewhere that key a census row by that path), and none names `describeUnresolvedColumns`, `describeUnusableBulkActionDefs`, `describeNonPositivePageSize` or the three diagnostic slices. The behavioural tests that spy on `console.warn` render the real `ObjectGrid`, so they cover the hook. ## Seam table of the `ObjectGrid` component (measured on `222755eb`) Lines are in the base file. "Reads" names inputs from other regions; the props and `schema` are left out. "Read by" names the regions that use what a region declares. A region after the first early return (line 3758) cannot become a hook, because a hook may not run after a conditional return; it can become a plain function or a sub-component with explicit props. | # | Concern | Lines | Declares (main) | Reads | Read by | Slice | |---|---|---|---|---|---|---| | A | Props destructure | 673–709 | the host props | — | all | stays | | B | Core state: rows, loading, error, i18n, metadata, object schema, card view, refresh key, row versions, row conflict, export flags, row height, selection, bulk dialog, grouped paging | 710–763 | `data`, `loading`, `objectSchema`, `refreshKey`, `selectedRows`, `rowConflict`, … and setters | — | all | stays (shared state) | | C | Row-height sync effect | 765–776 | — | B | — | 8 (with AD) | | D | Column state persistence | 778–849 | `columnStorageKey`, `columnState`, `saveColumnState` | — | U, AA | 11 (pinned: `gridNonAuthorKeys` reads the `NON-AUTHOR SURFACE — columnState` block here) | | E | Pull to refresh, card-view breakpoint | 851–870 | `pullRef`, `isRefreshing`, `pullDistance` | B | AC, AH | 4 | | F | Record source: bound data, data config, inline data, host paging mode, object name and fields | 873–950 | `dataConfig`, `hasInlineData`, `externalManualPagination`, `objectName`, `objectFields` | B | most | stays (shared; `base-bind-declared` keys the `bind` read to this file) | | G | Permissions, row affordances, operations, row actions, CRUD verdicts | 951–1171 | `perms`, `operations`, `canEdit`, `canDelete`, `resolvedRowActionDefs`, `recordVerdict`, … | B, F | K, V, X, Y, AF | 11 (pinned: `NON-AUTHOR SURFACE — rowActionDefs` block) | | H | Projection keys (grouping, predicates, row colour, concurrency token) | 1172–1264 | `groupingProjectionKey`, `predicateProjectionKey`, `carriesConcurrencyToken`, `rowColorField` | B, F | K | 5 (with I) | | I | Filter, sort, search and server paging state | 1265–1390 | `schemaFilter`, `headerSort`, `searchTerm`, `querySearchTerm`, `serverPage`, `searchFieldsKey` | B, F | K, Z, AF | 5 (with H) | | J | Grouping decisions: masked grouping refusal, server-grouped fetch, header-query and whole-rows refusals, fetch window | 1391–1548 | `serverGroupedFetch`, `groupingNeedsHeaderQuery`, `groupingNeedsWholeRows`, `fetchWindow`, `groupRowQuery` | B, F, I | K, O, T | 6 (with O) | | K | The row load effect | 1549–2168 | `invalidationNonce` (the effect sets B's state) | B, F, G, H, I, J | O | 7 (largest effect; one hook with typed inputs) | | L | Host `findParams` sync | 2169–2187 | — | B | — | 4 (with E) | | M | Navigation overlay, action runtime, row colour, predicate scope, capability gate, row style | 2188–2276 | `navigation`, `executeAction`, `getRowClassName`, `mayInvokeRowAction`, `getRowStyle` | B, F | R, V, AA, AB, AC | stays (shared; pinned: `NON-AUTHOR SURFACE — onNavigate` block and the `onNavigate: onNavigate ?? schema.onNavigate` read) | | N | Group value formatter and temporal group faces | 2277–2381 | `groupValueFormatter`, `temporalGroupFaces` | B, F | O, AE | after #26341 lands (it edits `temporalGroupHeading`, which N calls) | | O | Server grouping: headers, rows, grouped data, grouped totals | 2382–2557 | `groups`, `isGrouped`, `toggleGroup`, `groupHeaders`, `serverGroupTotal` | B, F, I, J, K, N | P, T, W, Z, AA, AE | 6 (with J) | | P | Bulk fan-out params, column summaries | 2558–2624 | `bulkFanoutParams`, `summaries`, `hasSummary` | B, F, O | X, AE | 9 (with X) | | **Q** | **Authored-input diagnostics** | **2625–2702** | (nothing anyone reads) | (`schema` only) | none | **3, this PR** | | R | `generateColumns` | 2704–3590 | `generateColumns` | B, F, G, M | U | 12, after #26341 (it edits the seven cell call sites here; pinned by `columnReadBoundary-6458`, `relationalMetaCopySet`, `cellRendererResolutionBoundary-8920`, `percent-storage-callers-11475`, `column-identity.ratchet`) | | S | Export: formats, format warning, `handleExport` | 3591–3755 | `exportableFormats`, `handleExport` | B, F | AD | 8 (pinned: `ObjectGrid.exportOptionsKeys` reads the `schema.exportOptions` reads here) | | T | Early returns: filter refusal, whole-rows refusal, load error, loading, header-query refusal | 3756–3853 | — | B, I, J, O | — | 10 (sub-components) | | U | Column post-processing: persisted widths and order | 3854–4040 | `columns`, `persistedColumns` | D, R | V, W | 12 (with R) | | V | Actions column and pinning | 4041–4213 | `columnsWithActions`, pinned column sets | G, M, U | W, AA | 12 (pinned: `NON-AUTHOR SURFACE — maxInlineRowActions`) | | W | Density, sortability, column chrome, frozen columns, manual paging flags | 4214–4406 | `orderedColumns`, `manualPaginationOn`, `manualSearchOn`, `effectiveFrozenColumns` | B, F, O, U, V | Z, AA, AF | 12 | | X | Bulk actions and selection | 4407–4783 | `bulkActionDefs`, `selectionMode`, `dispatchBulkAction`, `handleBulkDialogClose`, … | B, F, G, P | AA, AG | 9 (with P) | | Y | Row save and optimistic-concurrency conflict | 4784–5012 | `defaultRowSave`, `defaultBatchSave`, `settleRowConflict` | B, F, G | AA, AG | 11 (pinned: `affordanceGrantMap-12082` keys `ObjectGrid > writeRowGuarded` and `ObjectGrid > resolveRowConflict` to this file) | | Z | Pagination, sort and search wiring | 5013–5118 | `pageSize`, `manualPage`, `manualSort`, `manualOnSortChange`, `manualSearch` | B, F, I, W | AA | 12 (with AA) | | AA | `dataTableSchema`, grouped table schema | 5119–5466 | `tableColumns`, `dataTableSchema`, `buildGroupedTableSchema` | most | AE, AH | 12 | | AB | Record detail overlay | 5467–5692 | `recordOverlayShellProps`, `renderRecordDetail` | B, F, M | AC, AH | 10 | | AC | Mobile card view (an early return) | 5693–6025 | — | B, E, M, AB | — | 10 (pinned: `clickableNonControlCensus-13461` keys its clickable `div` to this file) | | AD | Toolbar: row height toggle, export menu | 6026–6128 | `gridToolbar` | B, C, S | AH | 8 (with C, S) | | AE | Summary footer, group headers and footers, group bands, grouped pager, grid content | 6129–6408 | `summaryFooter`, `groupedPager`, `gridContent` | B, N, O, P, AA | AF | 10 (pinned: `iconOnlyButtonCensus-12143` counts two icon-only pagers in this file) | | AF | Empty states | 6409–6514 | `renderedGridContent` | B, G, I, W, AE | AH | 10 | | AG | Bulk and row-conflict dialogs | 6515–6553 | `bulkDialog`, `rowConflictDialog` | B, X, Y | AH | 9 (with X) | | AH | The final return | 6554–6637 | — | everything | — | stays | ## Remaining slices (one PR each; the last closes the card) - **Slice 4 — E + L:** pull to refresh, the card-view breakpoint effect and the host `findParams` sync into a sibling hook. - **Slice 5 — H + I:** the projection keys and the filter, sort, search and server-paging state into a sibling hook. - **Slice 6 — J + O:** the grouping decisions and server grouping into a sibling hook. - **Slice 7 — K:** the row load effect into a sibling hook, every input typed from its source. - **Slice 8 — C + S + AD:** the row-height sync, export and the toolbar into a hook plus a toolbar sub-component. S is pinned by `ObjectGrid.exportOptionsKeys`; this slice needs that test to read the new module too. - **Slice 9 — P + X + AG:** bulk fan-out, bulk actions, selection and the bulk dialog into a hook (above the early returns) plus plain functions or a sub-component (below them). - **Slice 10 — T + AB + AC + AE + AF:** the early-return panels, the record detail overlay, the mobile card view, the grouped content and the empty states into sub-components with explicit props. AC and AE carry census rows keyed to `ObjectGrid.tsx`. - **Slice 11 — D + G + Y:** the column-state persistence, the row affordances and the row save handlers. They are pinned by file and symbol (`gridNonAuthorKeys`, `affordanceGrantMap-12082`); moving them needs those rows re-keyed to the new file, which is more than the import-path edits the card allows. If the seat does not grant that, they stay in `ObjectGrid.tsx` as the component's core. - **Slice 12 — R + U + V + W + Z + AA (closes the card):** `generateColumns` and the column pipeline into the table schema, after #26341 lands. Five source-reading tests pin R, so this slice needs the same allowance as slice 11, or these regions stay and slice 11 or 10 closes the card. A, B, F and M stay in the component in every plan: they are the shared state most other concerns read. Each later slice re-measures the line spans and the source-scan pins at its claim, because the file moves daily. ## Verification Run on head `fb3f7f50`: - `pnpm --dir ui exec vitest run packages/plugin-grid/ --maxWorkers=2`, the whole package suite, under the verification lock: 251 of 251 files and 2247 of 2247 tests passed after the move. The before reading is the same suite on slice 2's tested tree (the package tree of `main` `222755eb`): 251 of 251 files, 2247 of 2247 tests, locally and in CI on #26354. - The 52 test files outside plugin-grid that read sources (the list slices 1 and 2 used): 52 of 52 files, 2258 of 2258 tests passed. - `pnpm --filter @object-ui/plugin-grid type-check` exit 0. - eslint on the two touched files: 0 errors. `objectGridDiagnostics.ts` draws no warning at all, so the hook's dependency arrays raise no `react-hooks/exhaustive-deps` finding. - `pnpm --dir ui run check:doc-example-readers` exit 0. - `node scripts/gen-sdui-manifest-node.mjs`: the manifest is byte-identical (sha256 `1f6e1ccb…`). Only `scripts/sdui-manifest.sources/plugin-grid.json` is re-recorded. `node scripts/check-sdui-manifest.mjs` exit 0. - `node scripts/pm/dispatch-gates.mjs --commands`: 41 derived gates, all exit 0; `--ran` reconciles 41 of 41 (0 NOT-MEASURED, 0 UNRUN). ## Acceptance notes - The moved comments still say "`resolvesToDataColumn` above" and "the export format warning below", about code that stays in `ObjectGrid.tsx`. They are kept verbatim because the slice is move-only. ## Landing On the plugin-grid line this goes after #26358 and #26341, so it stays a draft. Expect a merge of `main` and a re-record of `plugin-grid.json` before it lands. Changeset: `.changeset/25994-objectgrid-diagnostics-hook.md` (patch, `@object-ui/plugin-grid`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Bdrd2yH4eqnz2QobtN3haE --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: objectstack-fleet[bot] <332303061+objectstack-fleet[bot]@users.noreply.github.com>
1 parent a7f4b99 commit eeaa827

3 files changed

Lines changed: 117 additions & 81 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@object-ui/plugin-grid': patch
3+
---
4+
5+
refactor(plugin-grid): `ObjectGrid`'s authored-input diagnostics move into a sibling hook
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The three `console.warn` effects that report an unreadable `columns` entry, an unusable `bulkActionDefs` member and a non-positive page size move verbatim out of `ObjectGrid.tsx` into `useObjectGridDiagnostics` (`objectGridDiagnostics.ts`). `ObjectGrid` calls the hook where the effects stood, so its hook order, its warnings and its rendered output do not change. No name is renamed, and the package entry is unchanged.
11+
12+
What to do: nothing.

‎ui/packages/plugin-grid/src/ObjectGrid.tsx‎

Lines changed: 7 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ import { resolveRowCrudAffordances, resolveRowRecordCrudAffordance } from './row
7979
import { useRecordCrudVerdicts } from './hooks/useRecordCrudVerdicts';
8080
import { resolveLegacyRowActions } from './resolveLegacyRowActions';
8181
import { applyRelationalMeta } from './relationalMetaKeys';
82-
import { resolveBulkActions, describeUnusableBulkActionDefs, resolvePromotedBulkParams } from './resolveBulkActions';
82+
import { resolveBulkActions, resolvePromotedBulkParams } from './resolveBulkActions';
8383
import { partitionBulkRows } from './bulkEligibility';
84-
import { resolvesToDataColumn, describeUnresolvedColumns } from './columnSpellingDiagnostics';
84+
import { resolvesToDataColumn } from './columnSpellingDiagnostics';
8585
import { RowActionMenu, formatActionLabel, planRowActionMenu } from './components/RowActionMenu';
8686
import { BulkActionBar } from './components/BulkActionBar';
8787
import { BulkActionDialog } from './components/BulkActionDialog';
@@ -91,10 +91,11 @@ import { findParamsSignature, useResolvedGridFilters } from './objectGridQuery';
9191
import { type ObjectGridColumn, type ObjectGridColumnDraft, isChipFieldType, normalizeColumns, resolveColumnLabel } from './objectGridColumns';
9292
import { LinkCell, recordPointerFace, rowRecordId } from './objectGridLinkCell';
9393
import { useGridTranslation } from './objectGridTranslations';
94-
import { DEFAULT_DISPLAY_PAGE_SIZE, DEFAULT_FETCH_BATCH_SIZE, DEFAULT_SELECTION_TYPE, NO_GROUPING_REFUSAL, type RowHeightMode, describeNonPositivePageSize, resolvePageSize, resolveRowHeightMode } from './objectGridDefaults';
94+
import { DEFAULT_DISPLAY_PAGE_SIZE, DEFAULT_FETCH_BATCH_SIZE, DEFAULT_SELECTION_TYPE, NO_GROUPING_REFUSAL, type RowHeightMode, resolvePageSize, resolveRowHeightMode } from './objectGridDefaults';
9595
import { type BatchSaveOutcome, OCC_TOKEN_FIELD, type RowConflictChoice, type WrittenRowVersion, rowVersionOf, rowVersionToSend } from './objectGridRowVersion';
9696
import { type ObjectGridColumnState, type ObjectGridExternalPaginationProps, type ObjectGridFooterSummary } from './objectGridProps';
9797
import { FooterSummaryReport } from './objectGridFooterSummaryReport';
98+
import { useObjectGridDiagnostics } from './objectGridDiagnostics';
9899
export type { RetiredListColumnKey, ObjectGridColumnHolds, ObjectGridColumnDraft, ObjectGridColumn } from './objectGridColumns';
99100
export type { ObjectGridColumnState, ObjectGridFooterSummary, ObjectGridExternalPaginationProps } from './objectGridProps';
100101

@@ -2624,84 +2625,9 @@ export const ObjectGrid: React.FC<ObjectGridComponentProps> = ({
26242625
wholeSetSummary.row,
26252626
);
26262627

2627-
// An authored column the renderer cannot read now says so, instead of just
2628-
// not being there (objectui#5349 — the Q2 objectui#5068 deferred).
2629-
//
2630-
// #5068 made `ListColumnSchema`'s `field` / `label` the only spelling read
2631-
// here, which is right; what it left behind was the receipt. A column
2632-
// authored `{ accessorKey, header }` (or with no `field` at all) is dropped
2633-
// by `resolvesToDataColumn` above, and until this effect landed the author
2634-
// saw no error, no warning and no empty header — just a grid with its
2635-
// row-number column and no data columns. Same defect shape #5068 exists to
2636-
// fix, one level down: renderer and author disagree, author gets a success
2637-
// receipt.
2638-
//
2639-
// Scope, deliberately narrow: this reads the `columns` INPUT and nothing
2640-
// else. It never asks whether the grid found ROWS, because a grid legitimately
2641-
// draws them from five different places (inline `data` array, `data.provider:
2642-
// 'value'`, legacy `staticData`, `bind`, or a host that owns the fetch and
2643-
// passes the window down as a `data` React prop). A predicate that consulted
2644-
// those would eventually paint a configuration error over a working grid.
2645-
// `hidden: true` is authored intent and is never reported.
2646-
//
2647-
// Channel: the same one `ObjectGrid` already uses for "you declared it, the
2648-
// renderer dropped it" — a `useEffect` keyed on the schema slice and one
2649-
// `console.warn` prefixed `[ObjectUI] ObjectGrid <topic>:` (see the export
2650-
// format warning below) — rather than a second, differently-shaped one.
2651-
const columnDiagnosticBlockType = (schema as { type?: unknown }).type;
2652-
const columnDiagnosticLabel = schema.label ?? (schema as { title?: unknown }).title;
2653-
useEffect(() => {
2654-
const message = describeUnresolvedColumns(schema.columns, {
2655-
blockType: columnDiagnosticBlockType,
2656-
objectName: schema.objectName,
2657-
label: columnDiagnosticLabel,
2658-
});
2659-
if (message) console.warn(message);
2660-
}, [schema.columns, columnDiagnosticBlockType, schema.objectName, columnDiagnosticLabel]);
2661-
2662-
// [objectui#8730] The same channel, for the sibling failure on
2663-
// `bulkActionDefs`. A member that is not a usable def (a bare action name —
2664-
// the OTHER key's vocabulary — or `null`, a number, `{}`, `{ name: '' }`) is
2665-
// skipped by `resolveBulkActions`; before that guard it reached the bar and
2666-
// `formatActionLabel(undefined)` threw during render, so the author's first
2667-
// multi-row selection lost the whole selection bar.
2668-
//
2669-
// The skip alone would only relocate the failure into silence — which is what
2670-
// the mirror direction already does (`bulkActions: [{ name: 'approve' }]`,
2671-
// stepped over by the `typeof name !== 'string'` guard). Saying which member
2672-
// was dropped, and that a bare name belongs in `bulkActions`, is what makes
2673-
// this a diagnosis rather than a quieter version of the same defect. One
2674-
// `console.warn` per authored array, keyed on it — NOT a second guard: the
2675-
// predicate lives once, in `resolveBulkActions`, and this reads it.
2676-
const bulkDefsDiagnosticSlice = (schema as { bulkActionDefs?: unknown }).bulkActionDefs;
2677-
useEffect(() => {
2678-
const message = describeUnusableBulkActionDefs(bulkDefsDiagnosticSlice, {
2679-
blockType: columnDiagnosticBlockType,
2680-
objectName: schema.objectName,
2681-
label: columnDiagnosticLabel,
2682-
});
2683-
if (message) console.warn(message);
2684-
}, [bulkDefsDiagnosticSlice, columnDiagnosticBlockType, schema.objectName, columnDiagnosticLabel]);
2685-
2686-
// [objectui#9853] The same channel again, for a `pagination.pageSize` (or the
2687-
// deprecated flat shorthand) that is not a positive integer. `resolvePageSize`
2688-
// drops such a value at all three read points and uses the site's default; on
2689-
// its own that is a quieter version of the defect, because substituting a
2690-
// number the author never wrote is exactly what the flat site already did in
2691-
// silence. This is the half that makes it a diagnosis.
2692-
//
2693-
// Keyed on the authored slice, so it is one warning per declaration and not
2694-
// one per render. NOT a second guard — the predicate lives once, in
2695-
// `isUsablePageSize`, and this reads it.
2696-
const pageSizeDiagnosticObject = schema.pagination;
2697-
const pageSizeDiagnosticFlat = schema.pageSize;
2698-
useEffect(() => {
2699-
const message = describeNonPositivePageSize(
2700-
{ pagination: pageSizeDiagnosticObject, pageSize: pageSizeDiagnosticFlat },
2701-
{ blockType: columnDiagnosticBlockType, objectName: schema.objectName },
2702-
);
2703-
if (message) console.warn(message);
2704-
}, [pageSizeDiagnosticObject, pageSizeDiagnosticFlat, columnDiagnosticBlockType, schema.objectName]);
2628+
// The authored-input diagnostics (`columns`, `bulkActionDefs`, page size),
2629+
// three `console.warn` effects, run here in hook order (`./objectGridDiagnostics`).
2630+
useObjectGridDiagnostics(schema);
27052631

27062632
const generateColumns = useCallback((): ObjectGridColumnDraft[] => {
27072633
// objectui#11815 — the host's verdict on a declared deadline's
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* The authored-input diagnostics of `ObjectGrid` (`./ObjectGrid.tsx`): the three
3+
* `console.warn` effects that report an unreadable `columns` entry, an unusable
4+
* `bulkActionDefs` member and a non-positive page size, moved out of that file
5+
* verbatim into a hook (objectstack#25994, slice 3).
6+
*/
7+
8+
import { useEffect } from 'react';
9+
import type { ObjectGridSchema } from '@object-ui/types';
10+
import { describeUnresolvedColumns } from './columnSpellingDiagnostics';
11+
import { describeUnusableBulkActionDefs } from './resolveBulkActions';
12+
import { describeNonPositivePageSize } from './objectGridDefaults';
13+
14+
/**
15+
* Runs `ObjectGrid`'s authored-input diagnostics. It declares no state and
16+
* returns nothing. `ObjectGrid` calls it where the three effects used to stand,
17+
* so the order of the component's hooks does not change.
18+
*/
19+
export function useObjectGridDiagnostics(schema: ObjectGridSchema): void {
20+
// An authored column the renderer cannot read now says so, instead of just
21+
// not being there (objectui#5349 — the Q2 objectui#5068 deferred).
22+
//
23+
// #5068 made `ListColumnSchema`'s `field` / `label` the only spelling read
24+
// here, which is right; what it left behind was the receipt. A column
25+
// authored `{ accessorKey, header }` (or with no `field` at all) is dropped
26+
// by `resolvesToDataColumn` above, and until this effect landed the author
27+
// saw no error, no warning and no empty header — just a grid with its
28+
// row-number column and no data columns. Same defect shape #5068 exists to
29+
// fix, one level down: renderer and author disagree, author gets a success
30+
// receipt.
31+
//
32+
// Scope, deliberately narrow: this reads the `columns` INPUT and nothing
33+
// else. It never asks whether the grid found ROWS, because a grid legitimately
34+
// draws them from five different places (inline `data` array, `data.provider:
35+
// 'value'`, legacy `staticData`, `bind`, or a host that owns the fetch and
36+
// passes the window down as a `data` React prop). A predicate that consulted
37+
// those would eventually paint a configuration error over a working grid.
38+
// `hidden: true` is authored intent and is never reported.
39+
//
40+
// Channel: the same one `ObjectGrid` already uses for "you declared it, the
41+
// renderer dropped it" — a `useEffect` keyed on the schema slice and one
42+
// `console.warn` prefixed `[ObjectUI] ObjectGrid <topic>:` (see the export
43+
// format warning below) — rather than a second, differently-shaped one.
44+
const columnDiagnosticBlockType = (schema as { type?: unknown }).type;
45+
const columnDiagnosticLabel = schema.label ?? (schema as { title?: unknown }).title;
46+
useEffect(() => {
47+
const message = describeUnresolvedColumns(schema.columns, {
48+
blockType: columnDiagnosticBlockType,
49+
objectName: schema.objectName,
50+
label: columnDiagnosticLabel,
51+
});
52+
if (message) console.warn(message);
53+
}, [schema.columns, columnDiagnosticBlockType, schema.objectName, columnDiagnosticLabel]);
54+
55+
// [objectui#8730] The same channel, for the sibling failure on
56+
// `bulkActionDefs`. A member that is not a usable def (a bare action name —
57+
// the OTHER key's vocabulary — or `null`, a number, `{}`, `{ name: '' }`) is
58+
// skipped by `resolveBulkActions`; before that guard it reached the bar and
59+
// `formatActionLabel(undefined)` threw during render, so the author's first
60+
// multi-row selection lost the whole selection bar.
61+
//
62+
// The skip alone would only relocate the failure into silence — which is what
63+
// the mirror direction already does (`bulkActions: [{ name: 'approve' }]`,
64+
// stepped over by the `typeof name !== 'string'` guard). Saying which member
65+
// was dropped, and that a bare name belongs in `bulkActions`, is what makes
66+
// this a diagnosis rather than a quieter version of the same defect. One
67+
// `console.warn` per authored array, keyed on it — NOT a second guard: the
68+
// predicate lives once, in `resolveBulkActions`, and this reads it.
69+
const bulkDefsDiagnosticSlice = (schema as { bulkActionDefs?: unknown }).bulkActionDefs;
70+
useEffect(() => {
71+
const message = describeUnusableBulkActionDefs(bulkDefsDiagnosticSlice, {
72+
blockType: columnDiagnosticBlockType,
73+
objectName: schema.objectName,
74+
label: columnDiagnosticLabel,
75+
});
76+
if (message) console.warn(message);
77+
}, [bulkDefsDiagnosticSlice, columnDiagnosticBlockType, schema.objectName, columnDiagnosticLabel]);
78+
79+
// [objectui#9853] The same channel again, for a `pagination.pageSize` (or the
80+
// deprecated flat shorthand) that is not a positive integer. `resolvePageSize`
81+
// drops such a value at all three read points and uses the site's default; on
82+
// its own that is a quieter version of the defect, because substituting a
83+
// number the author never wrote is exactly what the flat site already did in
84+
// silence. This is the half that makes it a diagnosis.
85+
//
86+
// Keyed on the authored slice, so it is one warning per declaration and not
87+
// one per render. NOT a second guard — the predicate lives once, in
88+
// `isUsablePageSize`, and this reads it.
89+
const pageSizeDiagnosticObject = schema.pagination;
90+
const pageSizeDiagnosticFlat = schema.pageSize;
91+
useEffect(() => {
92+
const message = describeNonPositivePageSize(
93+
{ pagination: pageSizeDiagnosticObject, pageSize: pageSizeDiagnosticFlat },
94+
{ blockType: columnDiagnosticBlockType, objectName: schema.objectName },
95+
);
96+
if (message) console.warn(message);
97+
}, [pageSizeDiagnosticObject, pageSizeDiagnosticFlat, columnDiagnosticBlockType, schema.objectName]);
98+
}

0 commit comments

Comments
 (0)