Skip to content

Commit 1b8d384

Browse files
committed
refactor(spec): deprecate userFilters element 'toggle' (ADR-0047 §3.4a)
Authoring offers None/Tabs/Dropdown only (Airtable parity). The element enum keeps 'toggle' for back-compat (existing configs keep rendering), documented deprecated in the schema + ADR §3.4a; page.form helpText and the §3.4a rationale updated to drop Toggle from the offered modes.
1 parent 8248501 commit 1b8d384

3 files changed

Lines changed: 26 additions & 11 deletions

File tree

docs/adr/0047-object-ui-run-modes.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,8 @@ have; we do not rely on lint to forbid what the type can simply not express.
156156
### 3.4a "No filter bar" is omission, not a literal `element: 'none'`
157157

158158
Airtable's User-filters control is a single tri-state selector
159-
(**None / Tabs / Dropdown**). We deliberately do **not** mirror that as a
160-
literal enum value. The `userFilters.element` enum stays
161-
`dropdown | tabs | toggle`; **"none" is the ABSENCE of `userFilters`**.
159+
(**None / Tabs / Dropdown**). We deliberately do **not** mirror "none" as a
160+
literal enum value: **"none" is the ABSENCE of `userFilters`**.
162161

163162
Rationale (declarative-metadata hygiene):
164163

@@ -171,13 +170,23 @@ Rationale (declarative-metadata hygiene):
171170
- **Cleaner diffs / overlays.** Disabling the bar is a key deletion (ADR-0005
172171
overlay semantics), not a value mutation dragging stale sub-config along.
173172
- **Orthogonal axes.** "Is there a filter bar?" (presence) and "what style?"
174-
(`element`) are independent; one enum would couple them, and we carry four
175-
styles (`+toggle`) to Airtable's three.
173+
(`element`) are independent; one enum would couple them.
174+
175+
**`toggle` is deprecated; authoring offers Airtable's three.** The `element`
176+
enum keeps `dropdown | tabs | toggle` for back-compat (existing configs keep
177+
rendering), but `toggle` is **not** an authoring choice: it overlaps `tabs`
178+
(presets) and `dropdown` (per-field values) without adding expressive power,
179+
needs per-field `defaultValues` to be useful at all, and was the least-
180+
exercised path. A homogeneous "everything is a toggle" bar only fits all-boolean
181+
field sets — a narrow case better served by letting field *type* drive the
182+
control inside `dropdown`. If stackable one-click quick-filters become a
183+
validated need, design them explicitly (à la Linear filter chips), not via the
184+
half-spec'd `toggle`.
176185

177186
**Storage and authoring UI are separate layers.** The Studio editor exposes a
178-
first-class **None / Tabs / Dropdown / Toggle** segmented selector (the
179-
`filter-mode` widget) — selecting *None* writes `onChange(undefined)`, removing
180-
the key. Authors get Airtable's explicit affordance; the protocol stays clean.
187+
first-class **None / Tabs / Dropdown** segmented selector (the `filter-mode`
188+
widget) — selecting *None* writes `onChange(undefined)`, removing the key.
189+
Authors get Airtable's explicit affordance; the protocol stays clean.
181190

182191
If a "disable but remember the configured fields/tabs" need ever arises, the
183192
right shape is a separate `enabled: false` flag — never `element: 'none'`.

packages/spec/src/ui/page.form.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ export const pageForm = defineForm({
5252
helpText:
5353
'source/sourceView bind the object view (columns, base filter and sort are inherited — the iron rule); appearance.allowedVisualizations whitelists renderers (one entry = locked); userActions toggles the toolbar.',
5454
// Explicit sub-fields so `userFilters` can use the dedicated
55-
// filter-mode selector (None / Tabs / Dropdown / Toggle, ADR-0047).
55+
// filter-mode selector (None / Tabs / Dropdown, ADR-0047 §3.4a).
5656
// None maps to ABSENCE of userFilters — the protocol stores
5757
// "no filter bar" as omission, not a literal element: 'none'.
58+
// (`element: 'toggle'` stays valid but deprecated — not offered.)
5859
// Keep this list in sync with InterfacePageConfigSchema.
5960
fields: [
6061
{ field: 'source' },
@@ -65,7 +66,7 @@ export const pageForm = defineForm({
6566
{
6667
field: 'userFilters',
6768
widget: 'filter-mode',
68-
helpText: 'End-user filter bar: None (no bar) / Tabs (named presets) / Dropdown (per-field) / Toggle. None removes the config.',
69+
helpText: 'End-user filter bar: None (no bar) / Tabs (named presets) / Dropdown (per-field). None removes the config.',
6970
},
7071
{ field: 'userActions', type: 'composite' },
7172
{ field: 'addRecord', type: 'composite' },

packages/spec/src/ui/view.zod.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,13 @@ export const UserFilterFieldSchema = lazySchema(() => z.object({
309309
* @see Airtable Interface → "User filters" panel (Elements: tabs / dropdowns)
310310
*/
311311
export const UserFiltersSchema = lazySchema(() => z.object({
312+
// `toggle` is DEPRECATED (ADR-0047 §3.4a): it overlaps `tabs` (presets) and
313+
// `dropdown` (per-field values) without adding expressive power, needs
314+
// per-field defaultValues to be useful, and authoring tooling no longer
315+
// offers it (None / Tabs / Dropdown only). Kept in the enum so existing
316+
// configs keep rendering; do not author new `toggle` filters.
312317
element: z.enum(['dropdown', 'tabs', 'toggle']).default('dropdown')
313-
.describe('Filter control style: dropdown selectors per field, tab presets, or on/off toggles'),
318+
.describe('Filter control style: "dropdown" (per-field value selectors) or "tabs" (named presets). "toggle" is deprecated.'),
314319
fields: z.array(UserFilterFieldSchema).optional()
315320
.describe('Fields exposed as quick filters (dropdown/toggle elements)'),
316321
tabs: z.array(ViewTabSchema).optional()

0 commit comments

Comments
 (0)