Skip to content

fix: make ChoicePicker options bindable and DOM ids instance-unique - #27

Merged
brianegan merged 2 commits into
mainfrom
18-fix-choicepicker-s-known-limitations
Sep 23, 2026
Merged

brianegan merged 2 commits into
mainfrom
18-fix-choicepicker-s-known-limitations

Conversation

@brianegan

Copy link
Copy Markdown
Collaborator

Summary

ChoicePicker's options can now be bound to the data model or computed by a function call, and literal options still resolve each option's own label binding. Every component that derives a DOM id or radio group name from its component id now uses a key unique to the rendered instance, so two surfaces, or two rows of a template, no longer share a radio group or produce duplicate ids.

Changes

  • A2uiComponent narrows a list-or-binding property's schema to the alternative its current value takes before building the binder. A literal list resolves element by element, and a binding or call is watched as a whole. It rebinds when an update flips the value between the two. a2ui_core classifies each property once from its schema, so without this a literal list would arrive with its inner bindings unresolved.
  • ChoicePicker.options is now the reference listOrReference shape: a literal list, a data binding, or a function call. Bound entries that aren't maps are skipped instead of crashing the build.
  • ComponentScope.instanceId is built from the surface id, component id and data-model base path, with each part encoded so separators can't collide. ChoicePicker's radio name, the Tabs tab and panel ids, and Modal's dialog id all derive from it.

Testing

The VM suite covers the binder contract directly. test/rendering/list_or_reference_test.dart exercises literal, bound and flipping values, and test/rendering/scope_test.dart covers instanceId across surfaces, templated rows and separator collisions. The Chrome test in test/browser/choice_picker_browser_test.dart is the one to read if you want to see the original bug: it renders two surfaces with the same picker id, clicks one, and checks the other's radio stays checked. It fails if you put scope.id back as the group name.

Gates

Gated de61343 against origin/main@2d651c4.

  • review: skill code-review on fable, passed, agent-reported
  • test: command, passed in 73.8s
  • example-test: command, passed in 15.4s
  • browser-test: command, passed in 6.1s

Reviewed against the plan recorded on the issue.

Closes #18

Narrows list-or-binding schemas per value so a literal list keeps resolving
each element's own bindings while a bound list is watched whole, switches
ChoicePicker's options to listOrReference, and adds ComponentScope.instanceId
so ChoicePicker, Tabs and Modal no longer collide across surfaces or
templated rows.

Closes #18
CI's stable SDK (3.13.4) flags the conditional expression inside the list
literal in narrowSchema, which the pinned 3.12.2 does not.
@brianegan
brianegan merged commit debac66 into main Sep 23, 2026
8 checks passed
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.

Fix ChoicePicker's known limitations

1 participant