Skip to content

Commit c1b52de

Browse files
feat(ui): record pane v4 mechanics — split beside the list, non-modal drawer over a list, maximize in place, narrowed list faces (#27953)
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC <!-- claude-projects-attribution: {"github_login":"os-bill"} --> _Requested by **Bill** · [project thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgP6XkfpjCfyzzG2KwQPUZfy)_ Refs #27545 Clause-②: yes (widening) Metadata-protocol: none ## 维护者速读 - 改了什么:#27545 拆成顺序两段,本 PR 是 A(机制层)。`NavigationOverlay` 新增列表旁的分栏面(非模态、列表不重挂)、盖在列表上的非模态抽屉、⤢ 原地最大化(宽度 = 视口减实时侧栏宽)、拖拽 360px 到 60vw;列表的两种窄面(≥440px 保留前三列,更窄改卡片行)和当前行订阅;`ObjectGrid` 画这两种窄面;i18n 加两个键;Console 记录抽屉默认宽度统一 720px。 - 为什么改:维护者 10-29 裁决的 Target 1–3 需要这些机制。整卡测得 3421 行,超过 3000 行人工合并阈值,座位 2 批准按 Target 拆成顺序两段:A 合并后,B(Console 接线:默认分栏、行点击换根、当前行高亮、⋯「在整页打开」、显示菜单第三项「整页」、步进不空白)从新 main 开,写 `Fixes #27545`。 - 风险与回滚:A 单独合入后读者能看到三处变化,正文「What a console reader sees from A alone」逐条列出:抽屉的整页按钮图标 ⤢ 换成 ↗;抽屉宽固定 720px(原来按字段数 480–1200px,且被抬到至少 min(60vw, 880px));已选「分栏」偏好的读者看到新的侧栏面。不动 spec、不动服务端、不加元数据键;回滚即 revert 本 PR。 - 先看:`ui/packages/components/src/custom/navigation-overlay.tsx` → `record-list-face.tsx` → `ui/packages/plugin-grid/src/ObjectGrid.tsx` 的窄面 → 新测试 `navigation-overlay-split-27545.test.tsx`。 - 两分钟验证:`pnpm --dir ui exec vitest run packages/components/src/__tests__/navigation-overlay-split-27545.test.tsx packages/components/src/__tests__/record-list-face-27545.test.tsx packages/plugin-grid/src/__tests__/ObjectGrid.recordPaneFace-27545.test.tsx`,预期 3 个文件 25 条全过。 - 需要你做的:无。`Clause-②: yes (widening)` 由座位做声明审查;epic 座位在预览里验收后座位才 ACCEPT,本 PR 保持 draft。 ## What changes Part A of #27545, the mechanics. The console wires them in part B; until then it passes none of the new props, and its only change of its own is the pane's default width. **`@object-ui/components` — `NavigationOverlay`** - **Split face (Target 1).** The pane sits beside the list the host hands over (`mainContent`), at that list's full height, as a complementary landmark, not a dialog. The list is one element in every state (closed, open, another record, closed again), so the overlay never remounts it. Below a 1000 px content area (`RECORD_SPLIT_MIN_CONTENT_PX`, measured on the host, not the window) the split face falls back to the drawer. - **Drawer over a list (Target 2).** Over a `mainContent` list the drawer is non-modal and draws no dimming overlay: an outside click leaves it open, and the list behind it stays clickable; ✕ and Esc close it. A drawer with no list (plugin views, the record page's related drawer) is the modal sheet it was. - **Width.** The divider drags from 360 px to 60vw and is remembered per object under the key the host names; a remembered width is drawn inside that range (`min(Npx, 60vw)`, so a remembered 1300 clamps). An authored width is no longer floored at `min(60vw, 880px)`. - **New `maximizable` prop (Target 3).** It draws ⤢ / ⤡ and maximizes the pane in place: the pane's width becomes the viewport less the live sidebar width, read from the sidebar's own state on every render, never cached (`--sidebar-width`, or `--sidebar-width-icon` when collapsed; `SIDEBAR_WIDTH_FALLBACK` / `SIDEBAR_WIDTH_ICON_FALLBACK` where the variable does not reach a portalled drawer). The list stays mounted and inert under it. Esc restores and only the next Esc closes; ✕ closes the path; a pane closed while maximized opens at its own width next time. - **New `activeListRecordId` prop.** A list under the pane reads its narrowed face (`useRecordListFace()`: `columns` at 440 px and wider, `cards` below, `full` with no pane beside it) and which row is the open record (`ActiveListRow`, `useIsActiveListRecord`, `useScrollActiveListRecordIntoView`, `ActiveListRecordProvider`, `RecordListFaceProvider`). The open row is a per-row `useSyncExternalStore` subscription: a change re-renders the row it leaves and the row it reaches, never the list around them. - **`onExpand`** is drawn as ↗ (the full page), so ⤢ is free for maximize. - **`DataTable`** marks the open record's row (`data-active-record`, `aria-current`) and scrolls it into view. **`@object-ui/plugin-grid` — `ObjectGrid`.** Narrowed to `columns`, it keeps its first three view columns that are not avatars (by the column's type or the object field's), plus its row actions. Narrowed to `cards`, it draws one card row per record, the name and two chips, that opens the record like a table row. Under no pane nothing changes. **`@object-ui/i18n`.** `detail.maximize` and `detail.restoreSize` in all ten packs (en Maximize / Restore size, zh 最大化 / 还原). **`@object-ui/app-shell`.** The console's record pane defaults to `md` (720 px, at most 92vw) whatever the object's field count: `size: 'auto'` and no size both read as `md`; an authored `size` or `width` stands. This applies to the object page's drawer and to the record page's related-record drawer. It is in A because removing the 880 floor would otherwise leave a narrow object's drawer at 480 px. ### What a console reader sees from A alone 1. The record drawer's page button is drawn as ↗ instead of ⤢. It still opens the full page, under the same accessible name. 2. The record drawer is 720 px wide (at most 92vw) whatever the object's field count. Before, it took 480 to 1200 px by field count and was widened to at least `min(60vw, 880px)`: 864 px at a 1440 px viewport for an object of up to eight fields. 3. A reader who chose Split in the Display menu sees the pane as the new side panel: no title heading, a divider that drags from 360 px to 60vw, the list beside it narrowed to three columns (card rows below 440 px), and below a 1000 px content area a non-modal drawer over the list. The open row is not tinted yet: the console does not pass `activeListRecordId` until B. Nothing else a reader sees moves: the default preference, the row click, the ⤢ / full-page route hop and the record header menu are untouched until B. ### Split plan (approved by seat 2) The whole card measured 3421 changed lines, over the 3000-line human-merge threshold. Seat 2 approved a serial split along the Target items: this PR (A, `Refs #27545`) first; when it merges, the branch is reset to the new `main` and B opens with `Fixes #27545`. B is the console wiring: split as the default preference and the Display menu's third option 「整页」, the row click swapping the root, the open row tinted and followed by ↑ / ↓, ⤢ in the record top bar, the full page only from ↗ and the header ⋯ (「在整页打开」, with `handleExpandDrawer`'s route hop retired and 「收为抽屉」 only for ↗ arrivals), stepping that keeps the previous record and prefetches both neighbours, the `?recordId=` render-count test, the #26679 / #27031 pin updates, and the docs. B's commits are kept on a local branch and in a patch under the seat's project files. Each PR leaves `main` coherent. ## Tests New: - `components/src/__tests__/navigation-overlay-split-27545.test.tsx` (16): the pane beside the list with no dialog and the list clickable; one list element from closed to open to another record to closed; the narrowed faces; the fallback below 1000 px and the 1000 px control; the drawer over a list draws no overlay and survives an outside click, with the modal-sheet CONTROL for a drawer with no list; ⤢ / ⤡, the list inert while maximized, Esc restores before it closes, ✕ closes while maximized, maximize not remembered, the sidebar read live expanded and collapsed, the portal fallbacks equal the sidebar provider's widths; the drag range and the remembered-1300 clamp. - `components/src/__tests__/record-list-face-27545.test.tsx` (5): the open row follows the record and re-renders only the two rows that change (render counts, list count unchanged), scrolls into view, no provider means no open row, row ids, the 440 px face threshold; `DataTable` tints and names the open row and moves with it. - `plugin-grid/src/__tests__/ObjectGrid.recordPaneFace-27545.test.tsx` (4): `columns` keeps the first three non-avatar columns in view order; `full` CONTROL draws them all; `cards` draws name + two chips and opens the record; the open row is marked in both faces. Updated, not deleted (width now drawn as given, the dragged range `min(Npx, 60vw)`): `navigation-overlay-9299`, `gridNavigationMembers-8071`, the kanban / calendar / gantt `navWidthDefault` pins, and the kanban / calendar / timeline `NavigationMembers` pins. Runs (from `ui/`, `NODE_OPTIONS=--max-old-space-size=4096`): - The affected set (components overlay / data-table / record-list-face, app-shell `ObjectView.record*`, `recordDrawerStack*`, record navigator, interface list hydration, row-click payload, defaults-map mirror, plugin-kanban / gantt / calendar / timeline / tree, plugin-view split popover and record surface, react hooks, i18n): `Test Files 504 passed | 2 skipped (506)`, `Tests 4635 passed | 266 skipped (4901)`. - `packages/plugin-grid` (whole package): `Test Files 262 passed (262)`, `Tests 2297 passed (2297)` on the head after merging `main` (with `app-shell` `ObjectView.headerSelectionGate-27831`, the one `main` test that reads the merged `ObjectGrid` change); before the merge `261 / 2290`. - The three new files: `Test Files 3 passed (3)`, `Tests 25 passed (25)`. - Type-check (`pnpm run type-check`, both programs): components, plugin-grid, i18n, plugin-kanban, plugin-calendar, plugin-gantt, plugin-timeline, app-shell: exit 0 each. - `eslint` on the 26 changed source and test files: 0 errors. - i18n: `check-i18n-call-site-keys`, `check-i18n-en-drift`, `check-i18n-dead-keys`: exit 0. ## Gates Derived on head `afeb540a12` (`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`): 35 families, 1751 changed lines (+1395 / -356, 29 files) against the 3000 threshold. Each was run and its exit code recorded before any pipe; `--ran` reconciles **35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN**. Every one exited 0: `check-adr-0087-registration` (base + self-test), `check-changeset-no-major`, `check-closing-keyword-parity` (+ self-test), `check-comment-mask-corpus`, `check-empty-changeset` (base + self-test), `check-sdui-manifest` (+ self-test), `release-rehearsal-clone --self-test`, `release-pending-publish --self-test`, `pnpm --dir ui exec vitest run scripts/`, spec `check:duration-unit-keys`, and `check:` changeset-gate-self-tests, closed-ceremony-text, cross-package-test-inputs, dispatcher-error-vocabulary, driver-memory-census, engine-double-contract, error-status-conformance, gitlink-declared, nul-bytes, pm-changeset-deadline-census, published-files, query-options-erasure, refd-timer-probe, skill-identifier-liveness, test-source-alias, tier-file-adoption, type-check-coverage, type-check-debt, undeclared-dep-imports, watch-hint-literal, where-matcher. The record is `gates-ran-A.txt` in the seat's card folder. The pre-commit and pre-push checks (`check-regen-pending`, `check-commit-card-trailers --pre-push`) were run by hand: exit 0. Not run locally: the root `check-i18n-bundles` / `check-i18n-coverage` / `check-i18n-walk-parity` (they read server metadata bundles, not the ui packs; they answer exit 3 without the server closure's `dist`, and they are not in the derived list). Every family CI runs is cited from the PR's check-runs at ACCEPT, not re-run here. ## Acceptance notes - **Screenshots (Acceptance 2), deviation declared:** taken on `examples/app-crm`, not HotCRM (outside this session's repository scope). Saved under the seat's project files, `ui-seat2/card-27545/shots/`, at 1440 x 900 and 1000 x 800 on the `crm_lead` list (five seeded leads, the admin account, sidebar expanded at 1440, auto-collapsed to its icon rail at 1000): - What A alone shows (this head): `a-01-drawer-default-1440.png` (the default drawer, 720 px, ↗ in its bar), `a-02-split-preference-1440.png` (a reader on Split: the side panel, the list at three columns), `a-03-split-preference-fallback-1000.png` (the same reader below a 1000 px content area: a drawer with no dimming, the list visible and clickable behind it). - The card's Acceptance 2, the end state with B applied (taken from the local full-stack branch, not this head): `b-01-split-1440.png` (split by default, the open row tinted), `b-02-split-another-row-1440.png` (another row clicked: the root swapped, the tint followed, the URL's `recordId` changed), `b-03-maximized-1440.png` (⤢: the pane 1184 px = 1440 less the 256 px sidebar, the sidebar kept, the same URL, ⤡ in the bar), `b-04-overlay-fallback-1000.png` (the fallback drawer at 1000, the open row still tinted behind it). - **Two things the shots show that B fixes, not this PR:** the split face prints the record count twice (the console's own `record-count-footer` under the list and `ListView`'s count bar; this is already so on `main`'s Split preference); and at the default 720 px pane beside an expanded sidebar the narrowed list is 464 px against 487 px of columns, so the sticky Actions column covers the end of the Status chip (a 23 px scroll). - **Acceptance 3:** the epic seat verifies in the console preview before the seat's ACCEPT. This PR stays a draft; nobody is assigned or requested. - No metadata key; nothing under `packages/spec/**`, no server file, nothing under `examples/**`. ## Verification 1. Open an object list in the console with the Display menu on Split and click a row: the pane opens beside the list, the list narrows to three columns; drag the divider (it stops at 360 px and 60vw). 2. Narrow the window below a 1000 px content area: the pane becomes a drawer over the list with no dimming; click a list row behind it and the drawer stays open. 3. On the default (drawer) preference, open a record: the drawer is 720 px and its page button is ↗. --- _Generated by [Claude Code](https://claude.ai/code/session_01Utp6CY5hd7qEmdXd6Wg8Ap)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 847e424 commit c1b52de

29 files changed

Lines changed: 1395 additions & 356 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
'@object-ui/components': patch
3+
'@object-ui/plugin-grid': patch
4+
'@object-ui/i18n': patch
5+
'@object-ui/app-shell': patch
6+
---
7+
8+
feat(components,plugin-grid,i18n,app-shell): record pane v4 mechanics — the split pane beside the list, the non-modal drawer over it, ⤢ maximize in place, the drag range, and the list's narrowed faces (#27545, part A)
9+
10+
Clause-②: yes (widening)
11+
Metadata-protocol: none
12+
13+
No metadata key is added, removed or read differently. The console wires these mechanics in a following change; until then it passes none of the new props, and its only change here is the pane's default width.
14+
15+
- **`@object-ui/components`**: `NavigationOverlay`.
16+
- **Split face.** The pane sits beside the list the host hands over (`mainContent`), at that list's full height. The list is one element in every state, so it is not remounted by the overlay. Below a 1000 px content area (`RECORD_SPLIT_MIN_CONTENT_PX`) the split face falls back to the drawer.
17+
- **Drawer.** Over a `mainContent` list, the drawer is non-modal and draws no dimming overlay: an outside click leaves it open, and ✕ and `Esc` close it. A drawer with no list is the modal sheet it was.
18+
- **Width.** The divider drags from 360 px to 60vw. A remembered width is drawn inside that range (`min(Npx, 60vw)`). An authored width is no longer floored at `min(60vw, 880px)`.
19+
- **New `maximizable` prop.** It draws ⤢ / ⤡ and maximizes the pane in place. The pane's width becomes the viewport less the live sidebar width (`--sidebar-width`, or `--sidebar-width-icon` when collapsed; `SIDEBAR_WIDTH_FALLBACK` / `SIDEBAR_WIDTH_ICON_FALLBACK` where the variable does not reach). The list stays mounted and inert under it. `Esc` restores, and a closed pane forgets it.
20+
- **New `activeListRecordId` prop.** A list under the pane reads its narrowed face (`useRecordListFace()`: `columns` at ≥ 440 px, `cards` below, `full` with no pane beside it) and which of its rows is the open record (`ActiveListRow`, `useIsActiveListRecord`, `ActiveListRecordProvider`, `RecordListFaceProvider`). The open row is a per-row subscription, so a change re-renders the row it leaves and the row it reaches, not the list.
21+
- **`onExpand`** is drawn as ↗.
22+
- **`DataTable`** marks the open record's row (`data-active-record`, `aria-current`) and scrolls it into view.
23+
- **`@object-ui/plugin-grid`**: `ObjectGrid` narrowed to `columns` keeps its first three view columns that are not avatars, plus its row actions. Narrowed to `cards`, it draws one card row per record, with the name and two chips. Under no pane nothing changes.
24+
- **`@object-ui/i18n`**: `detail.maximize` and `detail.restoreSize` in all ten packs.
25+
- **`@object-ui/app-shell`**: the console's record pane defaults to 720 px (`md`, clamped to 92vw) whatever the object's field count. `size: 'auto'` and no size both read as `md`, and an authored `size` or `width` stands. This applies to the object page's drawer and to the record page's related-record drawer.
26+
27+
What a console reader sees from this change alone:
28+
29+
- The record drawer's page button is drawn as ↗ instead of ⤢. It still opens the full page, under the same name.
30+
- The record drawer is 720 px wide (at most 92vw) whatever the object's field count. Before, it took 480 to 1200 px by field count and was widened to at least `min(60vw, 880px)`: 864 px at a 1440 px viewport for an object of up to eight fields.
31+
- A reader who chose Split in the Display menu sees the pane as a side panel. It draws no title heading. Its divider drags from 360 px to 60vw. The list beside it narrows to three columns, or to card rows below 440 px. The open row is not tinted yet: the console does not pass `activeListRecordId` until the following change. Below a 1000 px content area, the pane becomes a non-modal drawer over the list.

‎ui/packages/app-shell/src/views/objectViewRecordNavigation.ts‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ import type { useObjectLabel, useObjectTranslation } from '@object-ui/i18n';
3131
import type { buildViewTabs } from './objectViewTabs.js';
3232
import type { ConsoleObjectViewProps } from './ObjectView.js';
3333

34+
type OverlaySizeBucket = 'sm' | 'md' | 'lg' | 'xl' | 'full';
35+
36+
/**
37+
* The record pane's size bucket (objectstack#27545): `auto` — or no size —
38+
* is `md` (720 px) whatever the object's field count; an authored bucket
39+
* stands as authored.
40+
*/
41+
function recordPaneSize(size: 'auto' | OverlaySizeBucket | undefined): OverlaySizeBucket {
42+
return !size || size === 'auto' ? 'md' : size;
43+
}
44+
3445
export function useObjectViewRecordNavigation({
3546
activeView,
3647
objectDef,
@@ -78,24 +89,26 @@ export function useObjectViewRecordNavigation({
7889
const authored = activeView?.navigation ?? objectDef.navigation;
7990
if (authored) {
8091
// Authored config wins. For an overlay, resolve the `size`
81-
// bucket (or 'auto') to a viewport-clamped width when no explicit
82-
// width was given — objectstack-ai/objectstack#2578: a pixel width can't be authored blind.
92+
// bucket to a viewport-clamped width when no explicit width was
93+
// given — objectstack-ai/objectstack#2578: a pixel width can't
94+
// be authored blind. `auto` is the pane's 720 (objectstack#27545).
8395
if (authored.mode === 'page') return authored;
8496
return {
8597
...authored,
8698
width: authored.width ?? overlayWidthFor(
87-
(authored as { size?: 'auto' | 'sm' | 'md' | 'lg' | 'xl' | 'full' }).size,
99+
recordPaneSize((authored as { size?: 'auto' | OverlaySizeBucket }).size),
88100
objectDef,
89101
),
90102
};
91103
}
92104
// No authored navigation: the drawer, whatever the object's field
93105
// count. Until objectstack#26677 a field-heavy object paged here by
94106
// itself (`deriveRecordSurface`, objectstack-ai/objectstack#2578), a
95-
// surface no author chose; `page` is now a declaration only. The
96-
// drawer's WIDTH still follows the field count (`size: 'auto'`),
97-
// clamped to the viewport.
98-
return { mode: 'drawer', width: overlayWidthFor('auto', objectDef) };
107+
// surface no author chose; `page` is now a declaration only. Since
108+
// objectstack#27545 the pane's width no longer follows the field
109+
// count either: `md` (720 px), clamped to the viewport, which the
110+
// reader drags.
111+
return { mode: 'drawer', width: overlayWidthFor(recordPaneSize(undefined), objectDef) };
99112
},
100113
[activeView?.navigation, objectDef]
101114
);

‎ui/packages/app-shell/src/views/recordDrawerStack.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
5353
import { ChevronRight, Minimize2 } from 'lucide-react';
5454
import { NavigationOverlay, type NavigationOverlayMode } from '@object-ui/components';
55+
import { overlayWidthFor } from '@object-ui/plugin-view';
5556
import type { FieldContainerLike } from '@object-ui/core';
5657
import { useObjectLabel, useObjectTranslation } from '@object-ui/i18n';
5758
import { prefetchRelatedCounts, relatedCountTargetsIn } from '@object-ui/plugin-detail';
@@ -625,7 +626,9 @@ export function RecordPageDrawerHost({
625626
);
626627

627628
const rootDef = root ? objects.find((o) => (o as { name?: unknown } | null)?.name === root.objectName) : undefined;
628-
const rootWidth = (rootDef as { navigation?: { width?: string | number } } | undefined)?.navigation?.width;
629+
// The pane's own width (objectstack#27545): an authored one, else `md`.
630+
const rootWidth = (rootDef as { navigation?: { width?: string | number } } | undefined)?.navigation?.width
631+
?? overlayWidthFor('md', rootDef);
629632

630633
return (
631634
<RecordDrawerContext.Provider value={api}>

‎ui/packages/components/src/__tests__/navigation-overlay-9299.test.tsx‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
* `RecordDetailDrawer`'s retired `objectui.drawerWidth.OBJECT`, must carry
2626
* over — ⛔ not be silently reset. The starting state of every case below is
2727
* therefore an EXISTING persisted width; a case that only checked the new key
28-
* would prove nothing about the migration.
28+
* would prove nothing about the migration. Since objectstack#27545 a
29+
* remembered width draws inside the drag range (`min(Npx, 60vw)`), and an
30+
* authored width is no longer floored at `min(60vw, 880px)`: the pane's
31+
* default is 720 and the reader drags it.
2932
*/
3033

3134
import React from 'react';
@@ -121,8 +124,9 @@ describe('objectui#9299 item 4 — the persisted width carries over, it is not r
121124
legacyStorageKey: LEGACY,
122125
});
123126
expect(await screen.findByTestId('payload')).toBeInTheDocument();
124-
// The user's width is what renders — not the authored default.
125-
expect(readWidth()).toBe('640px');
127+
// The user's width is what renders — not the authored default — bounded
128+
// by the drag range (objectstack#27545).
129+
expect(readWidth()).toBe('min(640px, 60vw)');
126130
// … and it has MOVED, so this is a migration and not a permanent second
127131
// read: the new key holds it and the retired one is gone.
128132
expect(window.localStorage.getItem(KEY)).toBe('640');
@@ -138,7 +142,7 @@ describe('objectui#9299 item 4 — the persisted width carries over, it is not r
138142
legacyStorageKey: LEGACY,
139143
});
140144
expect(await screen.findByTestId('payload')).toBeInTheDocument();
141-
expect(readWidth()).toBe('700px');
145+
expect(readWidth()).toBe('min(700px, 60vw)');
142146
});
143147

144148
it('CONTROL: with no persisted width at all the authored width is what applies', async () => {
@@ -151,7 +155,7 @@ describe('objectui#9299 item 4 — the persisted width carries over, it is not r
151155
legacyStorageKey: LEGACY,
152156
});
153157
expect(await screen.findByTestId('payload')).toBeInTheDocument();
154-
expect(readWidth()).toBe('max(min(960px, 60vw), min(60vw, 880px))');
158+
expect(readWidth()).toBe('min(960px, 60vw)');
155159
});
156160

157161
it('a legacy width below the shared floor is not adopted', async () => {
@@ -167,7 +171,7 @@ describe('objectui#9299 item 4 — the persisted width carries over, it is not r
167171
legacyStorageKey: LEGACY,
168172
});
169173
expect(await screen.findByTestId('payload')).toBeInTheDocument();
170-
expect(readWidth()).toBe('max(min(960px, 60vw), min(60vw, 880px))');
174+
expect(readWidth()).toBe('min(960px, 60vw)');
171175
expect(window.localStorage.getItem(KEY)).toBeNull();
172176
});
173177

0 commit comments

Comments
 (0)