feat(ui): add form controls for webview parameter forms - #1101
Open
EhabY wants to merge 4 commits into
Open
Conversation
EhabY
force-pushed
the
feat/ui-form-controls
branch
from
September 7, 2026 20:05
6e1807c to
a7149a2
Compare
Adds Input (text / number / password with reveal toggle), Textarea, Checkbox, Select (Radix), and Field/Label to packages/ui for the #972 dynamic-parameter webview form. Controls render like VS Code's settings editor; richer dashboard form_types map onto this vocabulary. - Input/Textarea are controlled with value + onChange(next); Checkbox uses checked + onChange(next). Select preserves Radix controlled and uncontrolled modes with flat compound exports. - Shared text-control.css paints Input, SearchInput, and Textarea from one set of ui-text-control rules. - New --ui-checkbox-*, --ui-dropdown-*, and --ui-list-focus-* tokens with high-contrast fallbacks. - Stories drive state for Pixel snapshots only; behavior is covered by per-component unit tests sharing test/webview/ui/helpers.tsx.
EhabY
force-pushed
the
feat/ui-form-controls
branch
from
September 8, 2026 12:22
a7149a2 to
b17a086
Compare
The select trigger inherits line-height 1 from .ui-control, so the ellipsized value box (13px) is shorter than the rendered text (17px) and clips it; the native dropdown draws an 18px line. Set line-height: normal on the trigger only. --ui-list-focus-outline was declared twice in :root; the tree-oriented declaration won, so highlighted select rows got a focus-colored outline in regular themes. Use the existing --ui-list-selection-outline, which is transparent outside high contrast, and drop the duplicate. Every form-control States story now renders unfocused, with a separate Focused story that focuses the first control for the focus-border snapshot.
Move the highlightable row chrome that Menu and Select both declared into overlay.css, keyed by --ui-overlay-highlight-* the same way .ui-overlay already maps its border. Split the password reveal toggle out of Input into PasswordInput, and give Input a children slot for trailing in-field actions. Cut CSS that only restated shared classes or browser defaults: the disabled modifier classes become :has(> :disabled), the Checkbox box reuses .ui-control and renders its check icon only when checked, and the no-op flex, user-select, and min-width declarations are gone. Rename the select highlight tokens to --ui-dropdown-list-focus-* so they don't read as list.focusBackground. Fold the five form control test files and their helpers into one forms.test.tsx with it.each tables, move the SearchInput tests there, drop the Radix keyboard navigation test that chained waitFor on internal timers, and move the jsdom pointer-capture shims into the shared setup file.
Name each it.each row with a case interface and a $name title instead of positional tuples, so a row reads as a record and the callback destructures only what it uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the form-control vocabulary
packages/uineeds for the #972 dynamic-parameter webview form:Input(text / number / password with reveal toggle),Textarea,Checkbox,Select(Radix), andField/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_typeinput(+mask)Input(text / number / password + reveal)textareaTextareadropdown,radioSelectcheckbox,switchCheckboxsliderInputtype="number"with min/maxmulti-selectCheckboxes in aField(composition)tag-selectInput, comma-separated (form-layer split)No Switch, Slider, RadioGroup, or MultiSelect widgets: VS Code has none, and every shipped control has a direct
@vscode-elementsparity reference, so the "Known gaps" list doesn't grow.Implementation notes
Input/Textareause controlledvalue+onChange(next), andCheckboxuseschecked+onChange(next).Selectpreserves Radix controlled/uncontrolled modes and flat compound exports likeDropdownMenu.text-control.csspaintsInput,SearchInput, andTextareafrom one set ofui-text-controlrules; component CSS files hold only component-specific rules. The inner native input restoresuser-select: text, which the.ui-controlwrapper turns off.--ui-checkbox-*,--ui-dropdown-*, and--ui-list-focus-{background,foreground}(quick-input list colors, matching nativeselectBoxStyles); all with fallback chains incl. high contrast. Highlighted select rows reuse the existing--ui-list-selection-outline(contrastActiveBorder, transparent outside high contrast).line-height: normal: the.ui-controlline-height of 1 gives the ellipsized value a 13px box that clips 17px text, while the native dropdown draws an 18px line.@radix-ui/react-selectadded via the workspace catalog (catalogMode: strict).play). Each form control has aStatesstory (unfocused) and aFocusedstory that focuses its first control for the focus-border snapshot. Behavior is covered by one unit test file per component, sharingtest/webview/ui/helpers.tsxfor the controlled-value andclassName/stylepass-through checks.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 frompackages/uiis required. The package has the full quality pipeline (tokens, four-theme Pixel snapshots, parity stories, webview tests, isolation lint) but had no form controls — onlySearchInputand menu items.Scope — five components
Input<input>;SearchInputprecedent;typepassthrough incl.number(min/max) andpasswordwith reveal toggleTextarea<textarea>, same tokens, vertical resizeCheckbox--vscode-checkbox-*geometry, codicon check; children as labelSelectreact-select; flat compound exports matching theDropdownMenu*convention; portal on.ui-overlay; option rows support adescriptionslot@radix-ui/react-selectField+LabelLabel(htmlFor+ children), thinField(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
Selectengine — Radixreact-select(established practice; native<select>+appearance: base-selectconsidered and rejected for Storybook portability).test/webview/ui/per AGENTS.md; per-file layout keeps a future move into the package mechanical.Review decisions and simplification log
showLabel/hideLabel), and Radix Select modes.helpers.tsxremoves the copy-pasted controlled-value andclassName/styletests. Overlapping Field, Input, and Select cases merged; the long-list keyboard test (disabled-option skipping,End,Enter, restored trigger focus) moved from the story intoSelect.test.tsx.expectremoved fromplay;playremains only where it drives the state to be screenshotted (Select open/scrolled, Focused variants). Layout-only checks (trigger ellipsis, last option within the scrolled listbox) are covered by theLongListPixel snapshot.text-control.cssrewritten against sharedui-text-control*classes so each rule is declared once; deadui-checkbox__labelclass removed.useIdexample replaced by a pointer toField.stories.tsx.@vscode-elementsreference (13px line box vs 17px text) — fixed with a trigger-onlyline-height: normal.--ui-list-focus-outlinewas declared twice in:rootand the tree declaration won, painting a focus-colored outline on highlighted select rows in regular themes — the duplicate is removed and Select uses--ui-list-selection-outline. Focus in stories was inconsistent (onlyFieldfocused a control); every form control now has an unfocusedStatesstory plus aFocusedstory.Note
This PR was generated by Coder Agents on behalf of @EhabY.