Skip to content

feat(ui): add form controls for webview parameter forms - #1101

Open
EhabY wants to merge 8 commits into
mainfrom
feat/ui-form-controls
Open

feat(ui): add form controls for webview parameter forms#1101
EhabY wants to merge 8 commits into
mainfrom
feat/ui-form-controls

Conversation

@EhabY

@EhabY EhabY commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Adds the form-control vocabulary packages/ui needs for the #972 dynamic-parameter webview form: Input (text / number / password with reveal toggle), Textarea, Checkbox, Select (Radix), and Field/Label.

Design principle

Render like VS Code, not like the dashboard. VS Code's settings editor covers every configuration shape with text fields, number fields, checkboxes, and dropdowns, so the library ships exactly that vocabulary and the future form layer maps the richer dashboard form_types onto it:

form_type Rendered with
input (+ mask) Input (text / number / password + reveal)
textarea Textarea
dropdown, radio Select
checkbox, switch Checkbox
slider Input type="number" with min/max
multi-select Stacked Checkboxes in a Field (composition)
tag-select Input, comma-separated (form-layer split)

No Switch, Slider, RadioGroup, or MultiSelect widgets: VS Code has none, and every shipped control has a direct @vscode-elements parity reference, so the "Known gaps" list doesn't grow.

Implementation notes

  • Input/Textarea use controlled value + onChange(next), and Checkbox uses checked + onChange(next). Select preserves Radix controlled/uncontrolled modes and flat compound exports like DropdownMenu.
  • New tokens: --ui-checkbox-*, --ui-dropdown-*, and --ui-list-focus-* (quick-input list colors, matching native selectBoxStyles); all with fallback chains incl. high contrast.
  • The select list renders without animation, like the native dropdown, which also keeps Pixel snapshots stable.
  • @radix-ui/react-select added via the workspace catalog (catalogMode: strict).
  • One parity row and one test file per component; one commit per component.

Verification

  • pnpm typecheck, pnpm lint, pnpm format:check
  • pnpm test:webview (40 files, 344 tests)
  • pnpm build, Storybook build
  • Pixel snapshots in all four themes (CI)
  • Side-by-side eyeball against native VS Code
Implementation plan

Why

Issue #972's dynamic-parameter flow returns server-evaluated parameters with form_types the QuickPick prompter cannot render reactively; a webview form built from packages/ui is required. The package has the full quality pipeline (tokens, four-theme Pixel snapshots, parity stories, webview tests, isolation lint) but had no form controls — only SearchInput and menu items.

Scope — five components

Component Approach New dep
Input Native <input>; SearchInput precedent; type passthrough incl. number (min/max) and password with reveal toggle
Textarea Native <textarea>, same tokens, vertical resize
Checkbox Native input painted with --vscode-checkbox-* geometry, codicon check; children as label
Select Radix react-select; flat compound exports matching the DropdownMenu* convention; portal on .ui-overlay; option rows support a description slot @radix-ui/react-select
Field + Label One directory, two exports: Label (htmlFor + children), thin Field (label + control + description/error slot)

Deferred to the #972 form layer: form_type mapping, diagnostics, immutable/ephemeral badges, JSON list(string) encoding, debounce, WebSocket session, IPC, panel provider.

Extraction-readiness (future standalone repo)

No @repo/* imports (lint-enforced), react peer dep, colocated stories/parity, --ui-*-only component CSS, public root exports as the boundary, per-component test files so tests move wholesale at extraction time.

Decisions

  1. Select engine — Radix react-select (established practice; native <select> + appearance: base-select considered and rejected for Storybook portability).
  2. VS Code-like rendering — no Switch/Slider/RadioGroup/MultiSelect/TagInput components; form_types map per the table above.
  3. Test placement — repo-root test/webview/ui/ per AGENTS.md; per-file layout keeps a future move into the package mechanical.

Note

This PR was generated by Coder Agents on behalf of @EhabY.

Review decisions and follow-up validation
  • Keep Field as explicit composition: optional description/error IDs, consumer-owned ARIA and validation. No form context, child cloning, new dependency, or monorepo runtime coupling. Design references: MUI label/helper-text associations and shadcn field composition.
  • Preserve all controls, exports and Radix Select modes. Avoid generic control abstractions and unrelated menu changes.
  • Fix password reveal overriding subsequent input types; associate Select option descriptions without replacing consumer descriptions.
  • Constrain long selected labels, restore native scrollbars rather than adding scroll-button parts, and draw forced-color outlines only on highlighted options.
  • Add regression tests and a long-list story covering disabled-option skipping, End navigation, scrolling, selection and restored trigger focus.
  • Verified: 344 webview tests, typecheck, lint (8 GiB Node heap), formatting, extension/webview build and Storybook build. Chromium long-list interactions and scrollbar checks passed across four themes; forced-color outlines checked separately. Pixel snapshot approval and native VS Code side-by-side comparison remain separate checks.

Generated by Coder Agents on behalf of @EhabY.

Add description/error IDs without context or child cloning, document composition and preserve the Radix Select contract.

Generated by Coder Agents on behalf of @EhabY.
Associate option descriptions, constrain selected labels, expose native scrollbars, and correct forced-color outlines. Cover long-list keyboard navigation without adding component abstractions.

Generated by Coder Agents on behalf of @EhabY.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant