You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 50d05cb
Browse filesBrowse the repository at this point in the historyBrowse files
fix(fields): a boolean field draws a checkbox and a toggle field a switch, on the canvas and in the form (#27665)
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_016iMXWSZDzqWRWvWzdNo8wg6eGuL2tQm3aMEz4jsPF5R2)_Fixes#27519
Clause-②: yes (widening) — `@object-ui/fields` gains two public exports,
`resolveBooleanControl` and `type BooleanControl`
Metadata-protocol: none
## 维护者速读
- **改了什么**:Studio 里以「Checkbox — shown as a checkbox」(`boolean`)创建的字段,现在在
Studio 表单画布、运行时新建/编辑表单和表格行内编辑里都画成复选框;只有「Toggle — shown as a
switch」(`toggle`)才画开关。字段显式写了 `widget`(`checkbox`/`boolean` 或
`switch`/`toggle`)时仍以 `widget` 为准。
- **为什么改**:两种类型都落到同一个 `field:boolean` 控件,它只认 `widget:
'checkbox'`,否则一律画开关;画布又各自手写一份,`toggle`
还掉成了文本框。分诊裁定:画布与运行时表单共用一张「类型→控件」映射,不做分面覆盖。
- **风险与回滚**:所有表单里声明为 `boolean` 的字段(含 Setup
的元数据表单)都会从开关变成复选框,这正是分诊要求的行为;想保留开关的字段应声明为 `toggle` 或写 `widget:
'switch'`。只改前端渲染,元数据与存储值不变。回滚即 revert 本 PR。
-
**先看**:`ui/packages/fields/src/widgets/booleanControl.ts`(唯一的映射);`BooleanField.tsx`
里改为读它的那一行;`ObjectFormDesigner.tsx` 的 `FieldControlPreview` 布尔分支。
- **两分钟验证**:`pnpm --dir ui exec vitest run
packages/fields/src/__tests__/booleanControl-27519.test.tsx
packages/app-shell/src/views/studio-design/ObjectFormDesigner.booleanControl-27519.test.tsx`,预期全部通过。
- **需要你做的**:无,席位落地。
## What changes
One type-to-widget resolution for the two boolean types,
`resolveBooleanControl` (new
`ui/packages/fields/src/widgets/booleanControl.ts`, exported from
`@object-ui/fields`), answering `'checkbox' | 'switch'`:
1. an explicit `widget` naming a boolean face wins: `checkbox` /
`boolean` draw a checkbox, `switch` / `toggle` a switch;
2. otherwise the field's `type`, through the same four names: `boolean`
draws a checkbox, `toggle` a switch;
3. anything else that reaches the boolean widget (the bare
`field:boolean` id of a hand-authored form entry, no type) draws a
checkbox, the `boolean` type's own face.
A `widget` naming anything else is not a boolean face and leaves the
type to answer, which is the spec's `FieldSchema.widget` degrade.
Readers:
- `BooleanField` (the record form's `field:boolean` widget, and the
grid's inline editor through `FieldEditWidget`'s `boolean` / `toggle`
rows) draws the control the resolution names. It used to draw a switch
unless `widget === 'checkbox'`, so the authored `boolean` was lost.
- The Studio canvas's `FieldControlPreview` (in
`ObjectFormDesigner.tsx`; only that function and one import line)
previews `boolean` as a checkbox and `toggle` as a switch from the same
resolution. It used to draw the switch pill for `boolean` and the empty
text box for `toggle`.
Widget spellings: `checkbox` and `switch` are what `paramToField` and
`ScreenView` stamp and what `form.tsx`'s built-in arms are named; in the
form those two still take the built-in `checkbox` / `switch` arms before
the registry (`resolveRenderableWidget`, unchanged), and they draw the
same control the resolution names. `toggle` and `boolean` are accepted
as widget names so a widget naming a type means that type's face.
Not changed: `field-type-alias.ts` keeps both types on the one
`field:boolean` widget (comment updated to say the widget splits them);
`form.tsx` needed no change, since `BOOLEAN_WIDGET_TYPES` only seeds
`false` for two-state rows and its built-in arms are the explicit
widgets above; `BooleanCellRenderer` (see Acceptance notes).
Changeset: `@object-ui/fields` and `@object-ui/app-shell` patch.
`Clause-②: yes (widening)`: the published `@object-ui/fields` surface
gains `resolveBooleanControl` and `type BooleanControl`; nothing is
removed or narrowed. `Metadata-protocol: none`: what an author can write
and what `boolean` / `toggle` mean are unchanged (the spec already
declares them distinct UIs); the renderer now honours the declared type.
## Tests
- New `ui/packages/fields/src/__tests__/booleanControl-27519.test.tsx`:
the resolution's table (type defaults, the four widget overrides, a
non-boolean widget leaving the type to answer, missing input),
`BooleanField` drawing `checkbox` / `switch` per type and per widget
override, `FieldEditWidget` drawing the same per type.
- New
`ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.booleanControl-27519.test.tsx`:
a `boolean` and a `toggle` field on the canvas preview a checkbox and a
switch, equal to `resolveBooleanControl`'s answer.
- Updated, not deleted, to the new faces:
`boolean-label-association-e2e.test.tsx` (the form-hosted suite now uses
the `ObjectForm` entry shape, `field:boolean` plus the object field, as
a `toggle`; the two-field case hosts a `boolean` checkbox beside a
`toggle` switch; the standalone and host-id cases take `toggle` for the
switch variant and `boolean` for the checkbox variant),
`standard-widgets.test.tsx` (BooleanField cases cover both types),
`formPageRequiredMarker-10178.test.tsx` and
`FormPage.sharedWidgets-10179.test.tsx` (a `boolean` row is a checkbox),
`InlineFieldInput.delegation.test.tsx` (`boolean` routes to a checkbox;
the `toggle` switch case unchanged),
`RequiredFieldsDialog.ariaInvalidText-7126.test.tsx` (one checkbox and
one switch carry `aria-invalid`). `field-type-coverage.test.ts` needed
no change and is green.
Ablation (`scripts/ablation-replace.mjs`, the resolution's return
replaced by the old `widget === 'checkbox' ? 'checkbox' : 'switch'`,
anchor 1 to 0, restored to the HEAD blob with `git diff HEAD` empty):
the two new files and the label-association file go red, 13 of 28 cases;
the canvas case is among them, which shows the canvas reads the shared
resolution.
## Acceptance notes
- **Record detail "empty circle".** Not the same collapse, and not
reproduced in a browser here. An editable boolean row on the record page
draws `BooleanCellRenderer`'s disabled checkbox (`DetailSection`, "An
editable boolean keeps the checkbox"), whose primitive is a square
(`rounded-sm`) at `disabled:opacity-50`; a value that is not a boolean
draws the em-dash `EmptyValue`. The only round boolean face in the tree
is the completion indicator, gated on the names `completed` /
`is_completed` / `done` / `is_done`, which `Urgent` is not. Recorded as
a finding for the seat, unfiled.
- **Grid and detail cells.** `BooleanCellRenderer` draws a disabled
checkbox for both `boolean` and `toggle`; a `toggle` cell therefore
reads as a checkbox in the grid. The ruling names the canvas and the
runtime form, so the read-only cell face is left as it is; whether a
`toggle` cell should draw a disabled switch is the seat's call.
- **Canvas and an explicit widget.** `FieldControlPreview` is handed the
field's `type` only (its caller in `SortableField` passes
`type={type}`), so the canvas previews by type; an authored `widget`
override would need that one caller line, outside the function agreed
with seat 3.
- `paramToField` and `ScreenView` still stamp `widget: 'checkbox'` on
boolean params; with the type default now a checkbox the stamp is
redundant but harmless, and both files are outside this card.
## Verification
<details><summary>Targeted tests and typecheck</summary>
- Ten boolean-related files (the two new ones and the eight above):
`Test Files 10 passed (10)`, `Tests 227 passed (227)`.
- `pnpm --dir ui exec vitest run --maxWorkers=2 packages/fields/
packages/plugin-detail/ packages/plugin-kanban/ packages/plugin-form/
packages/components/src/renderers/form/`: `Test Files 989 passed | 2
skipped (991)`, `Tests 10835 passed | 43 skipped (10878)` (before the
`origin/main` merge).
- After merging `origin/main` (head `302bcb25`, closure rebuilt):
`packages/components/src/renderers/form/`, `packages/plugin-form/`,
`packages/app-shell/src/views/studio-design/` and the ten boolean files:
`Test Files 555 passed (555)`, `Tests 4955 passed | 18 skipped (4973)`.
- `type-check` for `@object-ui/fields`, `@object-ui/app-shell`,
`@object-ui/plugin-detail`, `@object-ui/plugin-kanban`: exit 0.
- `eslint --no-inline-config` on the 13 changed ui files: 0 errors
(warnings only, pre-existing `any`).
- `pnpm check:new-line-citations`: 0 new citations.
</details>
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands` on head `302bcb25`: 34
families, each run with its exit code recorded, all `exit 0` (including
`node scripts/check-sdui-manifest.mjs`, `pnpm check:type-check-debt`
under the verify lock, and `pnpm --dir ui exec vitest run scripts/`: 169
files passed). `--ran` reconciliation: 34 derived, 34 run, 0
NOT-MEASURED, 0 UNRUN.
Current head `f2a547f9` adds one commit on top of `302bcb25` that
changes only the changeset's `Clause-②` line; on it the changeset
families were re-run, all `exit 0`: `check-adr-0087-registration --base
origin/main`, `check-changeset-no-major --base origin/main`,
`check-empty-changeset --base origin/main`,
`check:changeset-gate-self-tests`, `check:pm-changeset-deadline-census`,
`check:closed-ceremony-text`, `check:nul-bytes`. The tests above were
not re-run for it (no code or test line moved).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CYXVQFhXabW3ZQcxWt)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(fields): a `boolean` field draws a checkbox and a `toggle` field a switch, in the record form, the grid's inline editor and the Studio canvas preview
7
+
8
+
Clause-②: yes (widening) — `@object-ui/fields` gains two public exports, `resolveBooleanControl` and `type BooleanControl`
9
+
Metadata-protocol: none
10
+
11
+
Studio's type picker offers "Checkbox — shown as a checkbox" (`boolean`) and "Toggle — shown as a switch" (`toggle`), but both types reached the one `field:boolean` widget, which drew a switch unless the field carried `widget: 'checkbox'`. A field authored as Checkbox therefore drew a switch on the Studio canvas and in the runtime create form. `resolveBooleanControl` (exported from `@object-ui/fields`) is now the one answer: `boolean` draws a checkbox, `toggle` a switch, and an explicit `widget` of `checkbox` / `boolean` or `switch` / `toggle` overrides the type. `BooleanField` and the canvas's control preview both read it.
12
+
13
+
Forms that relied on a `boolean` field drawing a switch now draw a checkbox; declare the field as `toggle`, or set `widget: 'switch'`, to keep the switch.
0 commit comments