Skip to content

Commit acc4328

Browse files
fix(plugin-view): hand ObjectGallery the gallery block nested, so coverFit, cardSize and visibleFields take effect (objectui#12053) (#12069)
Fixes #12053 Clause-②: no Implemented by the os-dev dispatched from seat `domain:ui#3`, session `session_01CGZy1BGCjdN5cXqL9cnvB8`, on the claim in comment `6086543001`. ## What changed `generateViewSchema`'s gallery branch (`packages/plugin-view/src/ObjectView.tsx`) now hands `ObjectGallery` the gallery block **nested**, as the node's own `gallery`. That is the shape `ListView` already hands the same renderer. Before, the branch built a flat node that carried `imageField` (from `coverField`) and `titleField` only, so a gallery view handed to plugin-view's `ObjectView` lost `coverFit`, `cardSize` and `visibleFields`. That held for the host `views` prop and for a named `listViews` entry alike. - The keys are picked **by name** through a new module-local table, `GALLERY_CONFIG_SPELLING`, with the existing `pickFlatMapConfig` helper. The gantt and timeline branches already use this mechanism (objectui#6152 round 14). The table is a `satisfies Record` keyed by every `keyof` of the spec's gallery block (`SpecListView['gallery']`), so a key the spec adds or drops fails `tsc` here. ⛔ No raw spread: an undeclared key in the authored block does not reach the node, and a block key named like a node key cannot override the node's own value. - **One spelling per key.** The flat `imageField` and `titleField` are gone from the node, because `ObjectGallery` reads the nested `coverField` and `titleField` ahead of them (measured below). The branch's existing `'name'` title floor moves into the block with the key it floors, so an undeclared title still resolves to `name`. - `ObjectGallery`, `ListView` and every other branch of `generateViewSchema` are untouched. No export, prop, type member or language-pack key changes. `GALLERY_CONFIG_SPELLING` is not exported. ## Dispatch zone 2: the three hypotheses, measured 1. **What `ObjectGallery` reads, and in what order.** These are the read points in `packages/plugin-list/src/ObjectGallery.tsx`, in the block under the comment `Resolve GalleryConfig with backwards-compatible fallbacks`: - `coverField`: `gallery?.coverField ?? schema.imageField ?? 'image'` (nested first) - `coverFit`: `gallery?.coverFit ?? 'cover'` (nested only) - `cardSize`: `gallery?.cardSize ?? 'medium'` (nested only) - `titleField`: `gallery?.titleField ?? schema.titleField` (nested first) - `visibleFields`: `gallery?.visibleFields` (nested only) ⇒ once the block is nested, the flat pair can only restate a nested value, so it is dropped (one spelling per key). 2. **The declared set.** `@objectstack/spec` 17.7.0, as plugin-view resolves it, declares `GalleryConfigSchema` (strict) as `ListViewShapeSchema.gallery`. That shape feeds `ListViewSchema` and the named-view `ObjectListViewSchema`. Its keys are `coverField`, `coverFit`, `cardSize`, `titleField` and `visibleFields`. `ObjectGallery` reads exactly these five off `schema.gallery`. No key is on one side only, so this is not a stop. The objectui node type `ObjectGallerySchema.gallery` is typed as the spec's `GalleryConfig`. 3. **Named `listViews`.** `canonicalViewKindBlocks.gallery` folds a named view's `gallery` into the same `viewOptions.gallery` the host `views` entry feeds, so both reach the same branch. Pinned: every route pin in the new test file runs on both routes. ## End-to-end reading through the real `ObjectGallery` This is a scratch probe and is not committed. It uses the real `SchemaRenderer`, imports `@object-ui/plugin-list` (through the root vitest alias) for its `object-gallery` registration, and mounts plugin-view's `ObjectView` with the gallery block `{ coverField: 'photo', coverFit: 'contain', cardSize: 'small', visibleFields: ['stage'] }` and one row that carries a cover URL. | route | before: the flat node (ablation A1 below) | after: HEAD | | --- | --- | --- | | host `views` prop | `object-cover` img, medium grid (`lg:grid-cols-4`), no `stage` on the card | `object-contain` img, small grid (`xl:grid-cols-6`), `stage` value shown | | named `listViews` entry | same as the row above | same as the row above | **Why the probe is not committed:** plugin-view does not depend on `@object-ui/plugin-list`, and its type-checked test program (`tsconfig.test.json`, `paths: {}`) cannot resolve that package. The committed pins read the generated node at the `SchemaRenderer` seam, as every sibling file in this package does. The app-shell import-side run below does reach the real `ObjectGallery` through this branch, with no gallery block. ## Tests - **New:** `packages/plugin-view/src/__tests__/ObjectView.galleryBlockNested-12053.test.tsx`. It holds triage's pins (comment `6080379082`). Each route pin runs on the host `views` prop and on a named `listViews` entry: - `coverFit: 'contain'` reaches `gallery.coverFit`; - `cardSize` and `visibleFields` reach `gallery` the same way; - every key the spec declares on the block, driven off `ListViewSchema.shape.gallery`, reaches `gallery`; - one spelling per key: no flat `imageField` or `titleField`; - an undeclared title keeps the `name` floor inside the block; - control: an undeclared key (and a block `objectName`) reaches neither `gallery` nor the node. - **Updated, re-spelled from flat to nested** (both pinned the flat `imageField` this card retires): - `ObjectView.readersRetired-6152.test.tsx`, section 3 and its header bullet; - `ObjectView.namedViewProtocolKeys-8980.test.tsx`, the `gallery` case. - **Control route (`ListView`), unchanged:** plugin-list `ListView.galleryOptionsBagCanonical-6152`, `ListView.readersRetired-6152`, `ListView.test.tsx` and the twelve `ObjectGallery.*` files: 15 files, 227 tests passed. ## Reverse verification (ablation) Predictions were written before either run. The mutation went through `ablation-replace.mjs` from the objectstack checkout (objectui has none), in wrap mode, with blob-equal restores. The tests import `../ObjectView` relatively, from source, and no alias or `dist/` is involved, so no rebuild was needed. Runs were on HEAD `a31f6aa85`, the fix commit. - **A1, rebuild the flat node** (`imageField` from `coverField` plus a flat `titleField`, no nested block). Predicted: all 11 new pins red, both re-spelled pins red, the probe red on both routes. **Observed: 16 failed, 48 passed (64).** Exactly that set, and the probe readings in the table above. Mutation landed: anchor 1 to 0, blob `5fae6dbb4b45` to `abf92739b839`. Restored: blob equals HEAD (`5fae6dbb4b45`), `git diff HEAD` empty. - **A2, raw spread inside the nested block** (`...viewOptions.gallery` in place of the pick). Predicted: the new control red, the round-14 gallery arm (`objectName` hijack) red, the retired-`imageField` pin red, everything else green. **Observed: 3 failed, 61 passed (64).** Exactly those three. Restored: blob equals HEAD, `git diff HEAD` empty. ## Gates (union run after the final commit, HEAD `c346c41f7`) | command | exit | the gate's own verdict | | --- | --- | --- | | `pnpm exec vitest run packages/plugin-view/` | 0 | `Test Files 74 passed (74)`, `Tests 691 passed (691)` | | plugin-list `ListView` / `ObjectGallery` control set | 0 | `Test Files 15 passed (15)`, `Tests 227 passed (227)` | | import side: app-shell `PageView.refreshInPlace-10519` (drives `defaultViewType: 'gallery'` through this branch into the real `ObjectGallery`), app-shell `ObjectView.titleFieldConvergence`, `examples/schema-catalog/test/catalog-gallery-render` | 0 | `Test Files 3 passed (3)`, `Tests 603 passed (603)` | | `pnpm --filter @object-ui/plugin-view type-check` (after `pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-view^...' build`, exit 0) | 0 | `tsc --noEmit && tsc -p tsconfig.test.json` clean; `--listFiles` counts all three touched test files in the test program | | `pnpm exec eslint` on the 4 touched source files | 0 | 0 errors, 101 warnings (`no-explicit-any` and kin; warnings carry no ratchet) | | `pnpm check:control-bytes` | 0 | `check-control-bytes: OK` | | `pnpm check:test-path-roots` | 0 | `check-test-path-roots: OK` | | `pnpm check:changeset-claims` | 0 | `No pending changeset names a file this change touches.` | | `pnpm check:pending-changeset-literals` | 0 | `No test source names a pending changeset.` | | `pnpm check:new-line-citations` | 0 | `VERDICT new-cross-file-line-citations: 0 new citation(s)` | | `pnpm check:vi-mock-specifiers` / `check:vi-mock-inherit` / `check:vi-mock-override-shape` | 0 / 0 / 0 | `OK` each | | `pnpm check:shell-escape-residue` / `check:handler-key-reads` / `check:unreferenced-sources` / `check:metadata-write-doors` | 0 each | `OK` each | | `node scripts/check-changeset-presence.mjs` / `check-changeset-no-major.mjs` | 0 / 0 | presence satisfied by `.changeset/12053-view-gallery-block.md` (patch); `No changeset declares a major bump.` | **The eslint narrowing is a measurement, not a skip.** - Population: `eslint.config.js` lints `**/*.{ts,tsx}`. - The JSON output counts 4 files, none ignored. - Type-aware linting is off (no `parserOptions.project`, no `projectService`), and no rule under `eslint-rules/` reads another file. So this diff cannot move an untouched file's verdict. The repo-wide `pnpm lint` is CI's. **NOT MEASURED:** - `check:eager-closure`, reason: it needs the console build and belongs to CI. The diff adds one five-entry const and moves no import. - i18n gates, reason: no language pack is touched. - `check:readme-exports`, reason: no export changes. ## Acceptance notes - **`ListView`'s gallery branch (out of scope here)** still builds its nested block by spreading `schema.options?.gallery` and `schema.gallery` whole. It also writes a flat `titleField` beside `gallery.titleField`, so there are two spellings on that route. The undeclared keys it carries are inert there: `ObjectGallery` reads five keys off the block, and nested keys cannot override node keys. Observation only; there is no wrong answer at a public door. Carrier: none. - **The `name` title floor.** Both routes floor `titleField` at the literal `'name'`, while `ObjectGallery`'s own ADR-0079 comment prefers `getRecordDisplayName` to a literal `'name'` floor. This PR keeps the floor as existing behaviour. Observation, not measured at a public door. Carrier: none. - No committed test drives the real `ObjectGallery` through plugin-view's `ObjectView` (see the probe section for why). Carrier: none. --- _Generated by [Claude Code](https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d03b022 commit acc4328

5 files changed

Lines changed: 220 additions & 20 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@object-ui/plugin-view': patch
3+
---
4+
5+
A gallery view handed to `ObjectView` now draws with the `coverFit`, `cardSize` and `visibleFields` its `gallery` block declares (objectui#12053).
6+
7+
`ObjectView` builds the `object-gallery` node for a gallery view, whether the view comes from the host `views` prop or from a named `listViews` entry. It built a flat node that carried the cover field and the title field only, while `ObjectGallery` reads `coverFit`, `cardSize` and `visibleFields` from the node's nested `gallery` block. So `coverFit: 'contain'` drew covers cropped, `cardSize` was ignored and cards showed none of their `visibleFields`.
8+
9+
The node now carries the block nested, the shape `ListView` already hands `ObjectGallery`. It holds the five keys the spec's gallery block declares (`coverField`, `coverFit`, `cardSize`, `titleField`, `visibleFields`), each copied by name. A key the spec does not declare is not carried. The node no longer carries the flat `imageField` and `titleField`, because `ObjectGallery` reads the nested values first. A gallery view with no `titleField` still titles its cards by `name`.
10+
11+
Nothing is added to the package entry: no export, prop, type member or language-pack key.

‎packages/plugin-view/src/ObjectView.tsx‎

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,21 @@ const TIMELINE_CONFIG_SPELLING = {
220220
scale: 'scale',
221221
} as const satisfies Record<keyof NonNullable<SpecListView['timeline']>, string>;
222222

223+
/**
224+
* The gallery block's declared keys; see {@link GANTT_CONFIG_SPELLING}. One
225+
* difference from the two tables above: the picked keys go out NESTED, as the
226+
* node's own `gallery` block, because that is where `ObjectGallery` reads all
227+
* five (`schema.gallery.KEY`, objectui#12053). The spec's block and the
228+
* renderer's reads are the same five keys, so this table is both.
229+
*/
230+
const GALLERY_CONFIG_SPELLING = {
231+
coverField: 'coverField',
232+
coverFit: 'coverFit',
233+
cardSize: 'cardSize',
234+
titleField: 'titleField',
235+
visibleFields: 'visibleFields',
236+
} as const satisfies Record<keyof NonNullable<SpecListView['gallery']>, string>;
237+
223238
/**
224239
* Copy the declared keys an author actually wrote onto the flat product,
225240
* each under its flat spelling — the map's keys by default, another block's
@@ -2349,22 +2364,31 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
23492364
};
23502365
}
23512366
case 'gallery':
2367+
// objectui#12053 — the block goes out NESTED, as the node's own
2368+
// `gallery`, which is the shape `ListView` already hands
2369+
// `ObjectGallery`. `ObjectGallery` reads `coverFit`, `cardSize` and
2370+
// `visibleFields` off `schema.gallery` and nowhere else, so the flat
2371+
// node this branch used to build (`imageField` from `coverField`, plus
2372+
// `titleField`) dropped those three: a view declaring
2373+
// `coverFit: 'contain'` drew its covers cropped.
2374+
//
2375+
// ONE SPELLING PER KEY: no flat `imageField` or `titleField` beside the
2376+
// block. `ObjectGallery` reads `gallery.coverField` ahead of
2377+
// `imageField` and `gallery.titleField` ahead of `titleField`, so the
2378+
// flat pair could only restate the nested values. The branch's own
2379+
// `'name'` title floor moves into the block with the key it floors.
2380+
//
2381+
// ⛔ No raw spread of the block (objectui#6152 round 14): it carried
2382+
// every undeclared key, and a block key named like a node key
2383+
// (`objectName`) overrode the node's own value. The keys are picked by
2384+
// name through `GALLERY_CONFIG_SPELLING`, the spec's declared set.
23522385
return {
23532386
type: 'object-gallery',
23542387
...baseProps,
2355-
// `coverField` is the spec key. It goes out as the FLAT `imageField`
2356-
// prop, because this route hands `ObjectGallery` no nested `gallery`
2357-
// block and that flat prop is the one it reads after the nested
2358-
// `coverField`. objectui#6152 round 14 retired the pre-#2231
2359-
// `gallery.imageField` alias rung that followed it here.
2360-
imageField: viewOptions.gallery?.coverField,
2361-
titleField: viewOptions.gallery?.titleField || 'name',
2362-
// ⛔ No raw spread of the block onto this node (objectui#6152 round
2363-
// 14). `ObjectGallery` reads a FLAT `imageField` and `titleField`
2364-
// and nothing else flat from the block's vocabulary, so the spread
2365-
// delivered no declared key the two lines above do not; it delivered
2366-
// every undeclared one, and a block key named like a node key
2367-
// (`objectName`) overrode the node's own value.
2388+
gallery: {
2389+
...pickFlatMapConfig(viewOptions.gallery, GALLERY_CONFIG_SPELLING),
2390+
titleField: viewOptions.gallery?.titleField || 'name',
2391+
},
23682392
};
23692393
case 'timeline': {
23702394
// `04a67b9dc` (step ③): the SECOND route to `ObjectTimeline`, fixed the
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/**
2+
* ObjectUI
3+
* Copyright (c) 2024-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* objectui#12053 — `generateViewSchema` hands `ObjectGallery` the gallery block
11+
* NESTED, as the node's own `gallery`.
12+
*
13+
* `ObjectGallery` reads `coverFit`, `cardSize` and `visibleFields` off
14+
* `schema.gallery` and nowhere else (`coverField` and `titleField` too, ahead
15+
* of the flat `imageField` / `titleField`). This branch built a FLAT node
16+
* carrying `imageField` and `titleField` only, so a gallery view handed to
17+
* plugin-view's `ObjectView` drew `coverFit: 'contain'` covers cropped, at the
18+
* default card size, with no card body. `ListView`, the other route to the
19+
* same renderer, already hands it the nested block.
20+
*
21+
* The node is read at the `SchemaRenderer` seam, the sink every sibling file
22+
* here uses. The end-to-end reading through the real `ObjectGallery` is on the
23+
* pull request: this package does not depend on `@object-ui/plugin-list`, so
24+
* its type-checked test program cannot import the renderer.
25+
*/
26+
27+
import React from 'react';
28+
import { describe, it, expect, vi, beforeEach } from 'vitest';
29+
import { render, waitFor, cleanup } from '@testing-library/react';
30+
import { ListViewSchema as SpecListViewSchema } from '@objectstack/spec/ui';
31+
import { ObjectView } from '../ObjectView';
32+
import type { NamedListView, ObjectViewSchema } from '@object-ui/types';
33+
34+
const HIJACK = 'HIJACK_FROM_THE_BLOCK';
35+
const rendered: any[] = [];
36+
37+
vi.mock('@object-ui/react', async (importOriginal) => {
38+
const ReactMod = await import('react');
39+
return {
40+
...(await importOriginal<Record<string, unknown>>()),
41+
SchemaRenderer: ({ schema }: any) => {
42+
rendered.push(schema);
43+
return <div data-testid="schema-renderer">{schema?.type}</div>;
44+
},
45+
SchemaRendererContext: ReactMod.createContext(null),
46+
subscribeDataChanges: () => () => {},
47+
notifyDataChanged: () => {},
48+
};
49+
});
50+
vi.mock('@object-ui/plugin-grid', async (importOriginal) => ({
51+
...(await importOriginal<Record<string, unknown>>()),
52+
ObjectGrid: () => <div data-testid="object-grid" />,
53+
}));
54+
vi.mock('@object-ui/plugin-form', async (importOriginal) => ({
55+
...(await importOriginal<Record<string, unknown>>()),
56+
ObjectForm: () => <div data-testid="object-form" />,
57+
}));
58+
59+
const dataSource = (): any => ({
60+
find: vi.fn().mockResolvedValue({ data: [], total: 0 }),
61+
findOne: vi.fn(),
62+
create: vi.fn(),
63+
update: vi.fn(),
64+
delete: vi.fn(),
65+
getObjectSchema: vi.fn().mockResolvedValue({ name: 'task', fields: { name: { name: 'name', type: 'text' } } }),
66+
});
67+
68+
const NODE = { type: 'object-view', objectName: 'task' } as unknown as ObjectViewSchema;
69+
70+
/**
71+
* Mount and return the last node handed to `SchemaRenderer`. `cleanup()` is
72+
* load-bearing: a previous mount keeps pushing into the sink otherwise
73+
* (objectui#9242).
74+
*/
75+
async function mountAndRead(ui: React.ReactElement): Promise<any> {
76+
cleanup();
77+
rendered.length = 0;
78+
render(ui);
79+
await waitFor(() => expect(rendered.length).toBeGreaterThan(0));
80+
const node = rendered[rendered.length - 1];
81+
expect(node.type).toBe('object-gallery');
82+
return node;
83+
}
84+
85+
/** The host `views` prop route: one gallery view carrying `gallery`. */
86+
function viaViewsProp(gallery: Record<string, unknown>): Promise<any> {
87+
return mountAndRead(
88+
<ObjectView
89+
schema={NODE}
90+
views={[{ id: 'v', label: 'Cards', type: 'gallery', gallery }] as any}
91+
dataSource={dataSource()}
92+
/>,
93+
);
94+
}
95+
96+
/** The named `listViews` route: the same block on a named view. */
97+
function viaNamedView(gallery: NonNullable<NamedListView['gallery']>): Promise<any> {
98+
return mountAndRead(
99+
<ObjectView
100+
schema={{ ...NODE, listViews: { cards: { label: 'Cards', type: 'gallery', gallery } } } as unknown as ObjectViewSchema}
101+
dataSource={dataSource()}
102+
/>,
103+
);
104+
}
105+
106+
const ROUTES: Array<[string, (gallery: any) => Promise<any>]> = [
107+
['the host `views` prop', viaViewsProp],
108+
['a named `listViews` entry', viaNamedView],
109+
];
110+
111+
beforeEach(() => {
112+
rendered.length = 0;
113+
});
114+
115+
for (const [route, mount] of ROUTES) {
116+
describe(`${route}: the gallery block reaches ObjectGallery nested (objectui#12053)`, () => {
117+
it('`coverFit: contain` reaches `gallery.coverFit`', async () => {
118+
const node = await mount({ coverField: 'photo', coverFit: 'contain' });
119+
expect(node.gallery.coverFit).toBe('contain');
120+
expect(node.gallery.coverField).toBe('photo');
121+
});
122+
123+
it('`cardSize` and `visibleFields` reach `gallery` the same way', async () => {
124+
const node = await mount({ cardSize: 'small', visibleFields: ['stage', 'amount'] });
125+
expect(node.gallery.cardSize).toBe('small');
126+
expect(node.gallery.visibleFields).toEqual(['stage', 'amount']);
127+
});
128+
129+
it('EVERY key the spec declares on the block reaches `gallery`', async () => {
130+
const declared = Object.keys(SpecListViewSchema.shape.gallery.unwrap().shape);
131+
expect(declared).toContain('coverFit');
132+
const block = Object.fromEntries(declared.map((k) => [k, `V_${k}`]));
133+
const node = await mount(block);
134+
for (const k of declared) expect(node.gallery[k], k).toBe(`V_${k}`);
135+
});
136+
137+
it('one spelling per key: no flat `imageField` or `titleField` beside the block', async () => {
138+
const node = await mount({ coverField: 'photo', titleField: 'subject' });
139+
expect(node.gallery.titleField).toBe('subject');
140+
expect(node).not.toHaveProperty('imageField');
141+
expect(node).not.toHaveProperty('titleField');
142+
});
143+
144+
it('an undeclared title keeps the branch\'s `name` floor, inside the block', async () => {
145+
const node = await mount({ coverField: 'photo' });
146+
expect(node.gallery.titleField).toBe('name');
147+
});
148+
});
149+
}
150+
151+
describe('control: an undeclared key in the authored block does not reach the node (objectui#12053)', () => {
152+
it('neither inside `gallery` nor on the node', async () => {
153+
const node = await viaViewsProp({ coverFit: 'contain', zzzUndeclared: 1, objectName: HIJACK });
154+
expect(node.gallery.coverFit).toBe('contain');
155+
expect(node.gallery).not.toHaveProperty('zzzUndeclared');
156+
expect(node).not.toHaveProperty('zzzUndeclared');
157+
expect(node.objectName).toBe('task');
158+
expect(JSON.stringify(node)).not.toContain(HIJACK);
159+
});
160+
});

‎packages/plugin-view/src/__tests__/ObjectView.namedViewProtocolKeys-8980.test.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ describe('objectui#8980 — a canonical top-level view-kind block reaches the re
201201
it('`gallery` → `object-gallery` (ObjectGallery)', async () => {
202202
const node = await generatedNodeFor({ label: 'Cards', type: 'gallery', gallery: { coverField: 'photo' } });
203203
expect(node.type).toBe('object-gallery');
204-
expect(node.imageField).toBe('photo');
204+
// Nested since objectui#12053: the node's own `gallery`, where
205+
// `ObjectGallery` reads the cover binding.
206+
expect(node.gallery.coverField).toBe('photo');
205207
});
206208

207209
it('`timeline` → `object-timeline` (ObjectTimeline)', async () => {

‎packages/plugin-view/src/__tests__/ObjectView.readersRetired-6152.test.tsx‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
* 2. The DECLARED keys keep their route, each beside the undeclared key the
2222
* spread used to carry with it. The gantt and timeline arms are driven off
2323
* the spec's own block shapes.
24-
* 3. The gallery cover still goes out as the FLAT `imageField` this route has
25-
* always used (it hands `ObjectGallery` no nested block), from `coverField`
26-
* only.
24+
* 3. The gallery cover goes out from `coverField` only. Since objectui#12053
25+
* it rides the node's nested `gallery` block (the shape `ListView` hands
26+
* `ObjectGallery`), not the flat `imageField` this round left in place;
27+
* `ObjectView.galleryBlockNested-12053.test.tsx` pins the rest of the block.
2728
*/
2829

2930
import React from 'react';
@@ -151,13 +152,15 @@ describe('2 · the declared keys keep their route; the undeclared ones do not (o
151152
});
152153

153154
describe('3 · the gallery cover goes out from `coverField` only (objectui#6152 round 14)', () => {
154-
it('`coverField` becomes the flat `imageField` the gallery reads', async () => {
155+
it('`coverField` becomes the nested `gallery.coverField` the gallery reads (objectui#12053)', async () => {
155156
const node = await generatedNode('gallery', { gallery: { coverField: 'photo' } });
156-
expect(node.imageField).toBe('photo');
157+
expect(node.gallery.coverField).toBe('photo');
157158
});
158159

159160
it('the retired `imageField` alias binds no cover', async () => {
160161
const node = await generatedNode('gallery', { gallery: { imageField: 'photo' } });
161-
expect(node.imageField).toBeUndefined();
162+
expect(node.gallery).not.toHaveProperty('coverField');
163+
expect(node.gallery).not.toHaveProperty('imageField');
164+
expect(node).not.toHaveProperty('imageField');
162165
});
163166
});

0 commit comments

Comments
 (0)