@qoretechnologies/reqraft 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +153 -94
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +130 -94
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +181 -45
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts +6 -3
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +70 -48
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/readFirst.d.ts +19 -0
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +22 -1
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.js +80 -0
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.js +138 -0
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.js +139 -0
- package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
- package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantModel.js +133 -0
- package/dist/components/form/engine/variants/variantModel.js.map +1 -0
- package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantParts.js +191 -0
- package/dist/components/form/engine/variants/variantParts.js.map +1 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +2 -2
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/engine/CompactRow.tsx +256 -234
- package/src/components/form/engine/CompactToolbar.tsx +108 -68
- package/src/components/form/engine/FormEngine.stories.tsx +127 -110
- package/src/components/form/engine/FormEngine.tsx +248 -67
- package/src/components/form/engine/compactRowStyles.ts +207 -134
- package/src/components/form/engine/compactToolbarContext.ts +1 -0
- package/src/components/form/engine/readFirst.ts +35 -0
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +119 -0
- package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
- package/src/components/form/engine/variants/VariantCards.tsx +212 -0
- package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
- package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
- package/src/components/form/engine/variants/focusDemo.ts +145 -0
- package/src/components/form/engine/variants/variantModel.ts +216 -0
- package/src/components/form/engine/variants/variantParts.tsx +313 -0
- package/src/components/form/fields/auto/AutoFormField.tsx +5 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Compact FormEngine → "Focus" redesign — migration plan
|
|
2
|
+
|
|
3
|
+
**Goal:** make the real Compact FormEngine *look* like the `Focus` variant
|
|
4
|
+
prototype (`src/components/form/engine/variants/VariantFocus.tsx`) while keeping
|
|
5
|
+
**100% of its current functionality** and a **green story/test suite**.
|
|
6
|
+
|
|
7
|
+
**Principle (the user's, and the right one):** _bring our design to the engine,
|
|
8
|
+
not the engine's functionality to our design._ We re-skin the working engine. We
|
|
9
|
+
do **not** rebuild validation, dependent-reset, templates/`on_change`, operators,
|
|
10
|
+
`arg_schema` nesting, focused editing, or draft/commit into the prototype.
|
|
11
|
+
|
|
12
|
+
The engine is already visually sophisticated and already owns every hard piece:
|
|
13
|
+
|
|
14
|
+
| Capability | Where it already lives |
|
|
15
|
+
|---|---|
|
|
16
|
+
| Status/intent per row | `CompactRow.tsx` `worstIntent` / `intentColor` / `rowStripeColor` (≈L533, L996) → painted via `--readfirst-stripe` |
|
|
17
|
+
| Sticky glass header | `compactRowStyles.ts` `StyledCompactPanel` + FormEngine scroll-wrap `position: sticky` (≈L193) |
|
|
18
|
+
| Revert-to-loaded | `CompactRow` `revertButton` `.options-readfirst-revert` (L423) |
|
|
19
|
+
| Clear value | `CompactRow` `clearValueButton` `.options-readfirst-clear` (L460) |
|
|
20
|
+
| Focused (fullscreen) editing | `CompactRow` `.options-readfirst-fullscreen` → `setFocusedEditing` → `<FocusedEditing>` (L820, L852) |
|
|
21
|
+
| Dependency lock + nav | `CompactRow` `dependsOnChip` `.options-readfirst-lock-deps` (L922) |
|
|
22
|
+
| Required one-of groups | rail + cluster nodes (`readfirst-cluster-*`, `.options-readfirst-node`) |
|
|
23
|
+
| short_desc inline | `.options-readfirst-label-desc` (revealed by ⓘ `.options-readfirst-info-toggle`) |
|
|
24
|
+
| long desc | `?` help `.options-readfirst-help` + `<Description>` inside `FocusedEditing` |
|
|
25
|
+
|
|
26
|
+
So this redesign is mostly **subtractive** (flatten the recessed value surface +
|
|
27
|
+
intent stripe → calm flat rows) plus **one structural move** (re-group fields
|
|
28
|
+
into the Needs-attention / Set / Optional boxes, each carrying thin schema-group
|
|
29
|
+
labels) plus **one viz swap** (the required-group rail → the "One of the below is
|
|
30
|
+
required" cluster box).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Answers to the five questions
|
|
35
|
+
|
|
36
|
+
### 1. How do we show `desc` (long description)?
|
|
37
|
+
Unify the two existing description affordances:
|
|
38
|
+
|
|
39
|
+
- **`short_desc`** → the toolbar **Descriptions toggle** (icon-only, `info`
|
|
40
|
+
intent when active — exactly the prototype's button), mapped to the engine's
|
|
41
|
+
existing `showAllDescriptions`. Renders inline under the field name via
|
|
42
|
+
`.options-readfirst-label-desc`. The per-field ⓘ stays as a local override.
|
|
43
|
+
- **`desc` (long, markdown)** → stays where you actually read/edit the field:
|
|
44
|
+
rendered by `<Description longDescriptionShownByDefault>` inside
|
|
45
|
+
**FocusedEditing**, plus the quick `?` help (`.options-readfirst-help`). No
|
|
46
|
+
second always-on description button in the row.
|
|
47
|
+
|
|
48
|
+
### 2. Dependent fields / fields with dependents?
|
|
49
|
+
Engine already models both — we restyle, not rebuild:
|
|
50
|
+
|
|
51
|
+
- **Disabled-by-dependency** (`depends_on` unmet): keep the **lock + "Depends on"
|
|
52
|
+
navigable chip** (`.options-readfirst-lock-deps`); clicking a blocker flashes
|
|
53
|
+
it (`readfirst-row-flash`). These are a distinct **blocked** state — neither
|
|
54
|
+
todo/set/optional — so they render **dimmed in place in their schema group**
|
|
55
|
+
(not pulled into Needs-attention; you can't act on them yet).
|
|
56
|
+
- **Fields with dependents**: no read-first badge needed (the engine resets
|
|
57
|
+
dependents on change). Optional later nicety: an "affects N fields" hint while
|
|
58
|
+
editing.
|
|
59
|
+
|
|
60
|
+
### 3. Optional fields wrapper (basic dark)
|
|
61
|
+
The `optional` group (everything `getOptionGroup` routes there — non-preselected,
|
|
62
|
+
non-required, unset) renders in a **collapsible box like the others but a neutral
|
|
63
|
+
/ dark tint** (not amber/green), collapsed by default. Same `ReqorePanel`
|
|
64
|
+
treatment as Set, different intent.
|
|
65
|
+
|
|
66
|
+
### 4. Focused editing / reset-to-default / clear
|
|
67
|
+
Keep the engine's existing edit-card actions, restyled to match:
|
|
68
|
+
- **Focused editing** (`.options-readfirst-fullscreen` → `<FocusedEditing>`) — unchanged.
|
|
69
|
+
- **Clear value** (`.options-readfirst-clear`) — unchanged.
|
|
70
|
+
- **Revert** (`.options-readfirst-revert`) reverts to the *loaded* value.
|
|
71
|
+
- **NEW: reset-to-default** — `handleValueChange(name, schema.default_value)`,
|
|
72
|
+
shown only when `default_value` exists and differs. Small addition.
|
|
73
|
+
|
|
74
|
+
### 5. Sticky header (search + progress)
|
|
75
|
+
Already sticky (`StyledCompactPanel` + the owned scroll context). The redesign
|
|
76
|
+
keeps it; the `CompactToolbar` (completion meter + filter + Descriptions toggle +
|
|
77
|
+
Fields menu) stays pinned. Only its contents/skin change.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Visual-delta map (Focus feature → engine change → test impact)
|
|
82
|
+
|
|
83
|
+
Legend: **🟢 safe** = CSS-only / additive, no tested DOM·text·interaction change.
|
|
84
|
+
**🟡 needs test updates** = changes asserted text/structure; update listed play
|
|
85
|
+
functions and re-run `test:stories`.
|
|
86
|
+
|
|
87
|
+
| # | Focus feature | Engine change | File · symbol | Impact |
|
|
88
|
+
|---|---|---|---|---|
|
|
89
|
+
| 1 | Calm flat rows (no recessed surface, no intent stripe) | drop/soften the value-surface `::before` + `border-left` stripe | `compactRowStyles.ts` `StyledGroupBody > *::before` | 🟢 safe |
|
|
90
|
+
| 2 | Status **dot** (one mark, colour = severity) | render a dot from `rowStripeColor`/`worstIntent`; additive element | `CompactRow.tsx` (row label cell) + styles | 🟢 safe (additive class, no removed hooks) |
|
|
91
|
+
| 3 | Thin uppercase group labels | restyle the group header (name → 10px/upper/letter-spaced; drop hairline+chip or calm them) | `FormEngine.tsx` group render + `StyledGroupHeader`/`StyledGroupHeaderLine` | 🟢 mostly (verify no story asserts the chip text) |
|
|
92
|
+
| 4 | Empty value shows `—` not "Not set" | swap the empty-value copy | `CompactRow.tsx` value render | 🟡 7 assertions use `Not set` |
|
|
93
|
+
| 5 | Needs-attention / Set / Optional **boxes** (status grouping, schema labels inside) | regroup: bucket rows by status across schema groups; render 3 `ReqorePanel`s; thin schema labels within | `FormEngine.tsx` group-render loop | 🟡 group-structure & badge ("N to resolve") assertions |
|
|
94
|
+
| 6 | "One of the below is required" **cluster box** | replace rail + cluster nodes with the cluster box | `CompactRow.tsx` cluster nodes/rail + `compactRowStyles.ts` `readfirst-cluster-*`, `StyledClusterNode` | 🟡 `Covered by` / `Covers` / `One of` assertions |
|
|
95
|
+
| 7 | Neutral/dark Optional wrapper | intent/skin on the optional `ReqorePanel` | `FormEngine.tsx` | 🟢 safe |
|
|
96
|
+
| 8 | reset-to-default action | new button calling `handleValueChange(name, default_value)` | `CompactRow.tsx` | 🟢 additive |
|
|
97
|
+
|
|
98
|
+
**Use Reqore throughout** — `ReqorePanel` (boxes/collapsible), `ReqoreControlGroup`,
|
|
99
|
+
`ReqoreInput`, `ReqoreButton`, `ReqoreTag`, `ReqoreIcon`, `ReqoreCollapsibleContent`,
|
|
100
|
+
`useReqoreTheme` for every colour. No bespoke widgets where a Reqore one fits.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Phased plan (each phase ends green)
|
|
105
|
+
|
|
106
|
+
- **P1 — Calm re-skin. ✅ DONE + validated.** Flattened the recessed value
|
|
107
|
+
surface + intent stripe (`StyledGroupBody > *::before` now transparent) and
|
|
108
|
+
added the trailing **status dot** (`StyledStatusDot`, derived from
|
|
109
|
+
`worstIntent`/`empty`/`required`/`coveredByLabel`/`groupResolved`). CSS +
|
|
110
|
+
additive DOM only — every test hook preserved. **Validated: `FormEngine.stories.tsx`
|
|
111
|
+
= 68 pass / 1 pre-existing fail ("Option With Any Type"), no new failures;**
|
|
112
|
+
`build:test` + `eslint` green; visually confirmed on :6008. *(Thin group labels
|
|
113
|
+
deferred to P3, where the group headers change anyway.)*
|
|
114
|
+
|
|
115
|
+
> **Validation harness (important):** `vitest run --project storybook <file>`
|
|
116
|
+
> runs stories in headless chromium and does **NOT** spawn a server on :6008 —
|
|
117
|
+
> a live `yarn storybook` is untouched. So every phase below is validatable
|
|
118
|
+
> without disturbing a running Storybook.
|
|
119
|
+
- **P3 — Status boxes. ✅ DONE + validated.** Regrouped into **Needs attention /
|
|
120
|
+
Set / Optional** boxes (intent-tinted `ReqorePanel`s), each with thin schema
|
|
121
|
+
sub-labels (`StyledStatusBoxGroupLabel`); neutral Optional. Key decisions:
|
|
122
|
+
- **One shared status helper** (`getReadFirstStatus`/`getReadFirstBucket` in
|
|
123
|
+
`readFirst.ts`) drives BOTH the row dot and the box bucket — they can't disagree.
|
|
124
|
+
- **One-of required-group members travel together** (bucket the whole group by
|
|
125
|
+
its satisfaction) so the existing rail + "One of"/"Covers"/"Covered by" chips
|
|
126
|
+
stay intact. Rail→cluster-box swap deferred to P4.
|
|
127
|
+
- **Freeze-while-editing** (`settledBucket` ref keyed by option, gated on
|
|
128
|
+
`expandedOptions`): an edited field — or any member of an edited one-of group —
|
|
129
|
+
keeps its box until the edit ends, so a status flip can't remount it mid-edit
|
|
130
|
+
and steal focus. (This fixed a real regression the suite guards.)
|
|
131
|
+
- Only **one** test legitimately changed (sort order is now status-first).
|
|
132
|
+
- **Validated: 68 pass / 1 pre-existing fail.** Visually confirmed on :6008.
|
|
133
|
+
- **P2 — Copy + empty state (🟡).** `Not set` → `— <reason>`. Update the ~10
|
|
134
|
+
assertions (`Not set` ×7, `Required — not set`, `Not in form — add`, covered-by).
|
|
135
|
+
- **P4 — Required cluster (🟡).** Rail → cluster box. Update `Covered by`/`Covers`/
|
|
136
|
+
`One of` assertions.
|
|
137
|
+
- **P5 — Polish.** reset-to-default action, depends-on dimmed-in-place styling,
|
|
138
|
+
responsive pass, qlip/Chromatic re-baseline.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Test-impact appendix (the audit)
|
|
143
|
+
|
|
144
|
+
`test:stories` runs `vitest run --project storybook`, and the storybook-vitest
|
|
145
|
+
plugin's `storybookScript` is `yarn storybook --no-open` → it wants **:6008**,
|
|
146
|
+
which collides with a running `yarn storybook`. **To validate without disturbing a
|
|
147
|
+
live :6008**, run the suite against a separate port (temporarily point the dev
|
|
148
|
+
script / `storybookScript` at e.g. :6010, or stop the live instance first).
|
|
149
|
+
|
|
150
|
+
Asserted strings that constrain changes (must be preserved, or the test updated):
|
|
151
|
+
`Not set` (×7), `Required — not set`, `Covered by "…"`, `Covers`, `Draft`,
|
|
152
|
+
`Focused Editing`, `Description`, type labels (`<string>`, `<rgbcolor>`),
|
|
153
|
+
`N to resolve` group badges, `1 unsaved change`, plus the class/`data-field`
|
|
154
|
+
hooks listed in the table above. Preserve every `options-readfirst-*` /
|
|
155
|
+
`readfirst-*` class that a play function queries; restyle via CSS rather than
|
|
156
|
+
renaming.
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# FormEngine Compact — UX Improvement Plan
|
|
2
|
+
|
|
3
|
+
> Status: **proposal / for review.** Nothing here is implemented yet.
|
|
4
|
+
> Scope: the compact ("read-first") `FormEngine` that will replace the classic
|
|
5
|
+
> engine in the IDE. Goal: keep its density but make it feel **calm, scannable,
|
|
6
|
+
> and guiding** instead of crowded and alarming.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 0. TL;DR
|
|
11
|
+
|
|
12
|
+
The compact engine's *information architecture is right* (read-first rows you
|
|
13
|
+
drill into). What's wrong is **visual signal management**: it currently runs
|
|
14
|
+
**five overlapping status systems at once** and treats *untouched draft fields*
|
|
15
|
+
as **hard red errors**. The result reads like a form that's on fire when it's
|
|
16
|
+
actually just unfinished.
|
|
17
|
+
|
|
18
|
+
Three changes do ~80% of the work:
|
|
19
|
+
|
|
20
|
+
1. **Collapse the 5 status systems into 1.** One global completion signal at the
|
|
21
|
+
top; demote/remove the redundant red banner and the per-group/per-row status
|
|
22
|
+
pills.
|
|
23
|
+
2. **Reframe "errors" as "to-dos."** Don't show red validation errors on fields
|
|
24
|
+
the user hasn't touched. *Unset ≠ error.* Validate on interaction/submit.
|
|
25
|
+
3. **Lock a strict one-line row template** and push everything variable
|
|
26
|
+
(descriptions, error reasons, nested previews, default hints) **behind
|
|
27
|
+
progressive disclosure**. One scannable line per field by default.
|
|
28
|
+
|
|
29
|
+
Everything else (calmer value chips, less chrome, smarter ordering, a "what's
|
|
30
|
+
next" path) compounds these.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. North star
|
|
35
|
+
|
|
36
|
+
> A configuration **summary you can read top-to-bottom in seconds**, that quietly
|
|
37
|
+
> tells you *what's done, what's left, and what to do next* — and gets out of the
|
|
38
|
+
> way when you're just scanning.
|
|
39
|
+
|
|
40
|
+
The audience is technical (developers configuring Qorus interfaces). They *want*
|
|
41
|
+
density — the model is a **good data table / code editor**, not a consumer
|
|
42
|
+
wizard. So the goal is **"calm density,"** not fewer fields. Think: the
|
|
43
|
+
difference between a well-set spreadsheet and a ransom note.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. What's wrong today (diagnosis)
|
|
48
|
+
|
|
49
|
+
Grounded in the screenshots + the implementation
|
|
50
|
+
([`CompactToolbar.tsx`](../src/components/form/engine/CompactToolbar.tsx),
|
|
51
|
+
[`CompactRow.tsx`](../src/components/form/engine/CompactRow.tsx),
|
|
52
|
+
[`FormEngine.tsx`](../src/components/form/engine/FormEngine.tsx),
|
|
53
|
+
[`compactRowStyles.ts`](../src/components/form/engine/compactRowStyles.ts)).
|
|
54
|
+
|
|
55
|
+
### 2.1 Five competing status systems
|
|
56
|
+
At one glance the user is shown completeness/validity **five different ways**:
|
|
57
|
+
|
|
58
|
+
| # | System | Where | Code |
|
|
59
|
+
|---|--------|-------|------|
|
|
60
|
+
| 1 | Draft badge + `12/21 fields set` + progress bar + `57%` | header | `CompactToolbar` `StyledCompletion` |
|
|
61
|
+
| 2 | Red banner: "6 fields are not valid… click to only show invalid" | header | `CompactToolbar` invalid banner |
|
|
62
|
+
| 3 | Per-group pills: `4 OPTIONAL`, `5 TO RESOLVE` | group headers | `FormEngine.tsx:1973` |
|
|
63
|
+
| 4 | Per-row red stripe + red ⓘ button + inline red message box | each invalid row | `CompactRow` tier-1 `StyledInfoPanel` |
|
|
64
|
+
| 5 | Per-row Draft chip / required asterisk / ⓘ toggle | each row | `CompactRow` `draftChip`, asterisk |
|
|
65
|
+
|
|
66
|
+
These don't reinforce — they **compete**. The user can't tell which is the
|
|
67
|
+
"real" status, so the whole UI reads as urgent. (NN/g *Aesthetic & minimalist
|
|
68
|
+
design*: every extra unit of status dilutes the rest.)
|
|
69
|
+
|
|
70
|
+
### 2.2 Premature, alarmist validation
|
|
71
|
+
The form is in **DRAFT**, yet untouched fields show **hard red errors** —
|
|
72
|
+
"Text value is empty," "Hash arguments are invalid," "Required — not set." Two
|
|
73
|
+
problems:
|
|
74
|
+
|
|
75
|
+
- **Unset is not an error.** Showing red on fields the user hasn't even reached
|
|
76
|
+
punishes them for not-yet-doing-something. This is the single biggest driver
|
|
77
|
+
of the "overwhelming" feeling. (Baymard / NN/g on inline validation: validate
|
|
78
|
+
**on blur or on submit**, not aggressively on load; reserve red for *wrong*,
|
|
79
|
+
not *empty*.)
|
|
80
|
+
- **The error chrome is layout-disruptive.** Full-width dark-red `ReqoreMessage`
|
|
81
|
+
boxes render *between rows*, shoving content down and shattering the table
|
|
82
|
+
rhythm that makes the form scannable. Six of them = a wall of red.
|
|
83
|
+
|
|
84
|
+
### 2.3 No focal point / flat hierarchy
|
|
85
|
+
Every row carries roughly equal visual weight; labels, values, blue chips,
|
|
86
|
+
swatches, nested trees and error boxes all shout at the same volume. There's no
|
|
87
|
+
"look here first," no obvious **next action**. The eye bounces. (Visual
|
|
88
|
+
hierarchy via size/weight/colour/space is absent within the row grid.)
|
|
89
|
+
|
|
90
|
+
### 2.4 Inconsistent row anatomy breaks scannability
|
|
91
|
+
Rows are one line (`Option with value · 123`), or two (label + sub-description),
|
|
92
|
+
or three+ (label + value + inline error box), or have an embedded object tree
|
|
93
|
+
with "Show more." The **grid rhythm** — the thing that makes a dense list
|
|
94
|
+
scannable — is constantly broken by inline variants rendered **by default**.
|
|
95
|
+
|
|
96
|
+
### 2.5 The value column has no consistent visual language
|
|
97
|
+
Plain text, **saturated-blue template chips** (`Test (local)`, `Richtext
|
|
98
|
+
Template`), colour swatch + hex, file pill, nested data tree — each a different
|
|
99
|
+
weight and colour. The blue chips in particular are *louder than the field
|
|
100
|
+
labels*, so decoration outshouts meaning. (Tufte: colour should encode data, not
|
|
101
|
+
ornament.)
|
|
102
|
+
|
|
103
|
+
### 2.6 Heavy non-data "chrome"
|
|
104
|
+
Recessed value surfaces, intent stripes, group spines, required-group rails,
|
|
105
|
+
dividers, hover fills — a lot of subtle lines/fills create a **busy texture**
|
|
106
|
+
before any content is read. (Tufte *data-ink ratio*: most separators could be
|
|
107
|
+
whitespace.)
|
|
108
|
+
|
|
109
|
+
### 2.7 Weak grouping semantics
|
|
110
|
+
"General" is a vague catch-all; "Optional/General" isn't a meaning the user
|
|
111
|
+
reasons with. 21 fields in 2 buckets, ordered by schema, with no
|
|
112
|
+
prioritisation. Findability relies on the filter box.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 3. Principles we'll design to
|
|
117
|
+
|
|
118
|
+
| Principle | Source | Applied here |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| **One primary status channel** | NN/g *Visibility of system status* + *Minimalist design* | Collapse 5 status systems → 1 meter |
|
|
121
|
+
| **Validate at the right time** | Baymard; NN/g *Error prevention* | Unset ≠ error; validate on blur/submit |
|
|
122
|
+
| **Severity = colour, not area** | Tufte; Material/Carbon error patterns | Replace red boxes with a dot/accent |
|
|
123
|
+
| **Progressive disclosure** | NN/g | One line per row; reveal the rest on demand |
|
|
124
|
+
| **Data-ink ratio** | Tufte | Whitespace > borders for separation |
|
|
125
|
+
| **Recognition over recall** | NN/g | Read-first rows (already good) — keep |
|
|
126
|
+
| **Chunking ~5–7** | Miller's law | Smaller, purpose-named groups |
|
|
127
|
+
| **Ask only what's needed** | GOV.UK *question protocol* | Defer/hide rarely-used optional fields |
|
|
128
|
+
| **Error summary + adjacent message** | GOV.UK Design System | A navigable "needs attention" section, not a red bar |
|
|
129
|
+
|
|
130
|
+
The throughline: **the IDE audience tolerates density but not noise.** Cut
|
|
131
|
+
*ink*, not *information*.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 4. The redesign — concrete moves
|
|
136
|
+
|
|
137
|
+
### 4.1 Collapse 5 status systems into 1
|
|
138
|
+
|
|
139
|
+
**Keep** the top completion strip as the *single* global status. **Evolve it** so
|
|
140
|
+
it carries everything the other four systems were saying:
|
|
141
|
+
|
|
142
|
+
- Segment the bar into **set-&-valid / needs-attention / unset** (e.g. green /
|
|
143
|
+
amber / track), so "57% done, 6 need attention" is legible in one glance —
|
|
144
|
+
no separate red banner needed.
|
|
145
|
+
- The **"6 need attention"** count becomes a quiet, clickable affordance *on the
|
|
146
|
+
meter* ("→ 6 to resolve") that filters to those fields. This replaces the
|
|
147
|
+
red banner (system #2) with a calm, constructive control.
|
|
148
|
+
- **Drop the per-group `4 OPTIONAL` pill entirely** (the group is already
|
|
149
|
+
labelled "Optional"). Keep a per-group "N to resolve" **only when > 0**, muted
|
|
150
|
+
and right-aligned — or better, a thin per-group progress underline.
|
|
151
|
+
- **Demote the per-row Draft chip**: the *global* Draft state is enough; per-row,
|
|
152
|
+
"changed" is better shown by the revert ↺ affordance alone.
|
|
153
|
+
|
|
154
|
+
Net: the user has **one** place to read status, and **one** click to act on it.
|
|
155
|
+
|
|
156
|
+
### 4.2 Errors → To-dos (validation timing & severity)
|
|
157
|
+
|
|
158
|
+
Introduce a 3-state model for a field's status, and **stop conflating them**:
|
|
159
|
+
|
|
160
|
+
| State | Meaning | Default treatment |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| **Unset** | No value, not required | Muted `—` / "Not set". **No alarm.** |
|
|
163
|
+
| **To-do** | Required (or required-group) but unset, untouched | **Quiet amber** dot + "Needs a value". Not red, not a box. |
|
|
164
|
+
| **Invalid** | User entered something that fails validation | **Red**, but only **after the field is touched** or **after a submit attempt**. |
|
|
165
|
+
|
|
166
|
+
Rules:
|
|
167
|
+
- **Untouched required fields are to-dos, not errors.** In a DRAFT, "Required —
|
|
168
|
+
not set" should be a calm amber hint, not a red error with a full-width box.
|
|
169
|
+
- **Validation fires on blur / on submit**, not on load. Track a per-field
|
|
170
|
+
`touched` flag (or a form-level `submitAttempted`).
|
|
171
|
+
- **The error reason moves off the default row.** Show at most a 1-word/short
|
|
172
|
+
inline hint after the value ("empty", "invalid"); the full message appears
|
|
173
|
+
**only when the row is focused/expanded** (where the editor's own message strip
|
|
174
|
+
already lives) or in the "needs attention" summary.
|
|
175
|
+
|
|
176
|
+
This one change removes the wall of red in the screenshot: those six boxes become
|
|
177
|
+
calm amber to-dos until the user engages or hits Submit.
|
|
178
|
+
|
|
179
|
+
### 4.3 The row: one strict template + progressive disclosure
|
|
180
|
+
|
|
181
|
+
Lock a **constant 3-zone row** and keep it one line by default:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
[ icon? · Label · req-dot ] [ value summary ……………… ] [ status · actions ]
|
|
185
|
+
label col value col (ellipsis) fixed right
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- **Constant height, constant alignment, constant zones.** The current grid is
|
|
189
|
+
already close (`StyledGroupBody .readfirst-row`); the fix is *what we allow to
|
|
190
|
+
break it.*
|
|
191
|
+
- **Move out of the default row, behind disclosure (ⓘ / focus / expand):**
|
|
192
|
+
- field **descriptions / subtitles** (currently inline under the label),
|
|
193
|
+
- **error/validation messages** (currently full-width boxes),
|
|
194
|
+
- **default-value hints**,
|
|
195
|
+
- **nested object/hash previews** (the `schemaOption2 → Show more` tree) →
|
|
196
|
+
collapse to the "Object · 1 field" summary; reveal the tree only on expand.
|
|
197
|
+
- Result: a 21-field form is **21 scannable lines**, each of which *can* expand —
|
|
198
|
+
rather than a variable-height stack you have to parse.
|
|
199
|
+
|
|
200
|
+
### 4.4 Value language: calm & consistent
|
|
201
|
+
|
|
202
|
+
- **Desaturate the template chips.** `Test (local)` / `Richtext Template`
|
|
203
|
+
currently use saturated blue and outshout the labels. Use a quiet, low-contrast
|
|
204
|
+
chip (subtle surface + the `$` template glyph) so the **label leads** and the
|
|
205
|
+
value supports. Reserve saturated colour for *state* (to-do/invalid), not for
|
|
206
|
+
*value type*.
|
|
207
|
+
- **One muted weight for all "set" values** (text, hex, filename, count). The
|
|
208
|
+
swatch/file-icon/`$`-glyph carry the type; the text stays uniform.
|
|
209
|
+
- **"Not set" gets the lightest possible treatment** (muted, no italics arms-race
|
|
210
|
+
with errors) so empty rows visually *recede* rather than read as problems.
|
|
211
|
+
|
|
212
|
+
### 4.5 Reduce chrome (data-ink)
|
|
213
|
+
|
|
214
|
+
- Replace the **red left stripe + red box + red ⓘ** trio with a **single status
|
|
215
|
+
dot** in the right-hand status slot (colour = severity). One mark, not three.
|
|
216
|
+
- **Whitespace over borders.** Drop the recessed value-surface fill and the
|
|
217
|
+
group spine where they don't encode meaning; let alignment + a little more
|
|
218
|
+
vertical rhythm do the grouping. (Keep the **required-group rail** — that one
|
|
219
|
+
*does* encode a real relationship.)
|
|
220
|
+
- Audit every line/fill in `compactRowStyles.ts` against "does this encode data?"
|
|
221
|
+
If not, it's a candidate for removal.
|
|
222
|
+
|
|
223
|
+
### 4.6 Grouping, ordering, findability
|
|
224
|
+
|
|
225
|
+
- **Default sort within a group = needs-attention → set → optional-unset.** Put
|
|
226
|
+
what the user must act on at the top of each group automatically (the existing
|
|
227
|
+
`invalid`/`unset` sort modes, but as the smart default for a draft).
|
|
228
|
+
- **Name groups by purpose.** "General" is a non-answer; if the schema gives no
|
|
229
|
+
meaningful group, prefer a single **Required / Optional** split over a vague
|
|
230
|
+
"General" bucket.
|
|
231
|
+
- **A pinned "Needs attention (6)" section** at the very top when there are
|
|
232
|
+
unresolved fields — the form's built-in answer to "what do I do next," and the
|
|
233
|
+
constructive replacement for the red banner (GOV.UK error-summary pattern, but
|
|
234
|
+
calm and always-navigable).
|
|
235
|
+
- Keep optional-but-unset fields **collapsed by default** behind the existing
|
|
236
|
+
"Optional (N)" disclosure, so the default view is "what's set + what's needed."
|
|
237
|
+
|
|
238
|
+
### 4.7 The "what's next" path
|
|
239
|
+
|
|
240
|
+
The form should always answer *what's left?* and make it one click to get there:
|
|
241
|
+
|
|
242
|
+
- Meter → click "6 to resolve" → filter to unresolved (reuse `showInvalidOnly`,
|
|
243
|
+
but renamed to *"needs attention"* and including required-unset to-dos).
|
|
244
|
+
- On **Submit** with unresolved fields: scroll-to + focus the first, and *now*
|
|
245
|
+
promote to-dos to errors (the flash machinery already exists).
|
|
246
|
+
- Optional niceties: keyboard `n`/`p` to jump between unresolved fields; a sticky
|
|
247
|
+
"Next: <field> →" hint while editing.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 5. Phased roadmap (impact × effort)
|
|
252
|
+
|
|
253
|
+
### P0 — quick wins (high impact, low effort): "stop shouting"
|
|
254
|
+
1. **Unset ≠ error.** Don't render red boxes/stripes for untouched required/empty
|
|
255
|
+
fields; downgrade to quiet amber to-dos. *(biggest single win)*
|
|
256
|
+
2. **Remove the red invalid banner**; fold the count into the meter as a quiet
|
|
257
|
+
"→ N to resolve" control.
|
|
258
|
+
3. **Drop the `N OPTIONAL` group pill**; show "N to resolve" only when > 0, muted.
|
|
259
|
+
4. **Move field descriptions + error messages off the default row** (behind ⓘ /
|
|
260
|
+
focus). One line per row.
|
|
261
|
+
5. **Desaturate template chips.**
|
|
262
|
+
|
|
263
|
+
> P0 alone should resolve "crowded and overwhelming" for the screenshot case.
|
|
264
|
+
|
|
265
|
+
### P1 — structural (medium effort): "calm density"
|
|
266
|
+
6. Segmented completion meter (set / to-do / unset).
|
|
267
|
+
7. Strict row template + collapse nested previews to summaries by default.
|
|
268
|
+
8. Single status-dot system; remove redundant stripes/boxes/ⓘ-buttons.
|
|
269
|
+
9. Validation timing: per-field `touched` + `submitAttempted`.
|
|
270
|
+
10. Smart default ordering (needs-attention first) + purpose-named groups.
|
|
271
|
+
|
|
272
|
+
### P2 — ambition (higher effort): "truly great"
|
|
273
|
+
11. Pinned **"Needs attention"** navigator section + keyboard jump.
|
|
274
|
+
12. Reduce the "edit card" modality — inline-first editing everywhere it fits.
|
|
275
|
+
13. **Density toggle** (comfortable / compact) for newcomers vs power users.
|
|
276
|
+
14. Per-group progress underline; section-level "all set" collapse.
|
|
277
|
+
15. Telemetry-driven defaults (collapse rarely-touched optional fields).
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## 6. Component-level change list (where the work lands)
|
|
282
|
+
|
|
283
|
+
| Area | File | Change |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| Status consolidation | `CompactToolbar.tsx` | Segmented meter; remove invalid banner; "N to resolve" control |
|
|
286
|
+
| Group header | `FormEngine.tsx:1968` `StyledGroupHeader` | Drop `N OPTIONAL`; muted "to resolve"; optional progress underline |
|
|
287
|
+
| Row template & disclosure | `CompactRow.tsx` | Move desc/error/default-hint behind disclosure; single status dot; calm chips |
|
|
288
|
+
| Validation model | `FormEngine.tsx` validity data + `CompactRow` | `touched`/`submitAttempted`; unset→to-do vs invalid |
|
|
289
|
+
| Visual chrome | `compactRowStyles.ts` | Remove non-data stripes/fills; whitespace rhythm; keep required rail |
|
|
290
|
+
| Ordering / sections | `FormEngine.tsx` group + sort | Needs-attention-first default; "Needs attention" section |
|
|
291
|
+
| Value language | `readFirst.ts` + `CompactRow` `renderReadFirstValue` | Uniform muted weight; desaturated template/richtext chips |
|
|
292
|
+
|
|
293
|
+
None of this changes the schema/value/`onChange` contract — it's all
|
|
294
|
+
presentation, consistent with the compact mode's existing "purely presentational"
|
|
295
|
+
design.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 7. Open questions (to decide / validate with users)
|
|
300
|
+
|
|
301
|
+
1. **Validation timing**: blur-only, or also a gentle "you skipped a required
|
|
302
|
+
field" on navigate-away? (Lean: blur + submit.)
|
|
303
|
+
2. Is **"General"** a real schema group or a fallback bucket? If fallback, can we
|
|
304
|
+
suppress the group chrome entirely for single-group schemas?
|
|
305
|
+
3. How often are the **nested object/hash previews** actually read at a glance vs
|
|
306
|
+
noise? (Telemetry / quick user check before investing in them.)
|
|
307
|
+
4. Do power users want the **density toggle**, or should we just ship one
|
|
308
|
+
well-tuned density?
|
|
309
|
+
5. Should **optional-unset** fields be collapsed by default (cleaner) or visible
|
|
310
|
+
(discoverable)? Likely collapsed with a strong "+ N optional" affordance.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 8. How we'll know it worked
|
|
315
|
+
|
|
316
|
+
- **Time-to-first-meaningful-scan** (qual): can a new user say "what's left?" in
|
|
317
|
+
< 5s?
|
|
318
|
+
- **Perceived calm** (5-pt survey before/after on the same schema).
|
|
319
|
+
- **Error-recovery**: time from Submit-blocked to all-resolved.
|
|
320
|
+
- **Eye-bounce** (optional): fewer fixations to locate the next action.
|
|
321
|
+
- Objective: **default view height** for the screenshot schema should drop
|
|
322
|
+
substantially once errors/descriptions move behind disclosure.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 9. References
|
|
327
|
+
|
|
328
|
+
- Nielsen Norman Group — *10 Usability Heuristics* (esp. Aesthetic & minimalist
|
|
329
|
+
design, Visibility of system status, Error prevention); *Progressive
|
|
330
|
+
Disclosure*; *Inline Validation in Web Forms*.
|
|
331
|
+
- Baymard Institute — form usability research (inline validation timing;
|
|
332
|
+
required vs optional).
|
|
333
|
+
- GOV.UK Design System — *Error summary* + *error message* patterns; *question
|
|
334
|
+
protocol* ("only ask what you need").
|
|
335
|
+
- Edward Tufte — *The Visual Display of Quantitative Information* (data-ink
|
|
336
|
+
ratio).
|
|
337
|
+
- Miller (1956) — chunking (~7±2).
|
|
338
|
+
- Design-system form guidance: Material 3, IBM Carbon, Shopify Polaris
|
|
339
|
+
(error/state colour conventions, one-column dense forms).
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
### Appendix — mapping the screenshot to the fixes
|
|
344
|
+
|
|
345
|
+
| In the screenshot | Why it feels heavy | Fix (section) |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| Red banner "6 fields are not valid…" | System #2, alarmist | 4.1 / P0-2 |
|
|
348
|
+
| `4 OPTIONAL` / `5 TO RESOLVE` pills | System #3, competes with titles | 4.1 / P0-3 |
|
|
349
|
+
| Red box "Text value is empty" (untouched) | Premature error | 4.2 / P0-1 |
|
|
350
|
+
| Red box "Hash arguments are invalid" inline | Layout-disruptive error | 4.2 / 4.3 |
|
|
351
|
+
| Saturated blue `Test (local)` chip | Decoration outshouts label | 4.4 / P0-5 |
|
|
352
|
+
| `schemaOption2 → Show more` tree inline | Breaks row rhythm | 4.3 / P1-7 |
|
|
353
|
+
| Per-row red stripe + red ⓘ + red box | Three marks for one state | 4.5 / P1-8 |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompactRow.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/CompactRow.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,eAAe,EAGhB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAe,MAAM,OAAO,CAAC;AA6FpC,eAAO,MAAM,UAAU,yGAQlB;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,eAAe,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IAIjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,
|
|
1
|
+
{"version":3,"file":"CompactRow.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/CompactRow.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,eAAe,EAGhB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,KAAe,MAAM,OAAO,CAAC;AA6FpC,eAAO,MAAM,UAAU,yGAQlB;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,eAAe,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IAIjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,6CAolCF,CAAC"}
|