Skip to content

forms: include optgroup children in a select's list of options#3058

Open
navidemad wants to merge 1 commit into
lightpanda-io:mainfrom
navidemad:fix-select-optgroup-options
Open

forms: include optgroup children in a select's list of options#3058
navidemad wants to merge 1 commit into
lightpanda-io:mainfrom
navidemad:fix-select-optgroup-options

Conversation

@navidemad

Copy link
Copy Markdown
Contributor

Closes #3057

HTMLSelectElement collected its options from its direct children only, so an <option> inside an <optgroup> was invisible to options, length, value, selectedIndex, selectedOptions and to form submission — while querySelectorAll("option") still found every option. A grouped select could not be read, set, or submitted.

Per HTML §the-select-element, a select's list of options is its option element descendants: its option children plus the option children of its optgroup children.

The path that changed

flowchart TD
    A["sel.value = 'b'<br/>(and options / length / selectedIndex / FormData)"] --> B{"how the options are enumerated"}

    B -->|"before — childrenIterator() / TreeWalker.Children"| C["direct children only<br/>options inside an optgroup never seen"]
    C --> D["no match<br/>selectedIndex = -1, value stays ''<br/>FormData entry dropped"]

    B -->|"after — OptionIterator / select_options walk"| E["children + optgroup children,<br/>in tree order"]
    E --> F["match<br/>value 'b', selectedIndex 2<br/>FormData entry 's=b'"]
Loading

Fix

  • src/browser/webapi/collections/node_live.zig — new select_options mode; selected_options moves onto the same walk. Both now use TreeWalker.FullExcludeSelf with a filter that accepts an option whose parent is the select or an optgroup child of the select, so an option buried any deeper is still excluded.
  • src/browser/webapi/collections/HTMLCollection.zig — plumbing for the new mode (union arm, iterator walker type, nextTw arm).
  • src/browser/webapi/element/html/Select.zig — one OptionIterator replaces five hand-rolled child walks: effectiveOption, setValue, getSelectedIndex, setSelectedIndex, getLength, plus the index lookup in add. getOptions builds the new collection mode.

Two behavioral details that come with making grouped options visible:

  • effectiveOption now asks Element.isDisabled instead of Option.getDisabled, so an option inside <optgroup disabled> is skipped when falling back to the first non-disabled option (concept-option-disabled). Without this, an option in a disabled group could become the implicit selection — only reachable now that those options are in the list at all.
  • select.add(option, index) inserts into the optgroup when the option at that index lives in one, per §dom-select-add (the insertion parent is before's parent).

Tests

New fixture src/browser/tests/element/html/select-optgroup.html, registered in WebApi: HTML.Select. Covers the options collection and its iterator, the value setter/getter, option.selected, selectedIndex (including -1), selectedOptions, FormData, an explicit selected attribute inside a group, the disabled-optgroup fallback, add with an index inside a group, and that an option buried below an optgroup is not in the list.

Verification:

  • TEST_FILTER='WebApi: HTML.Select#select-optgroup.html' zig build test — passes.
  • Toggle-off: reverting the three production files (keeping the fixture and its registration) fails the fixture at options.length (1 vs 3), value ("" vs "b") and selectedIndex — so the fixture exercises the fix.
  • zig build test — 1073/1073.
  • End-to-end with the issue's reproducer: exit 1 on 1.0.0-nightly.8285 (8 checks disagree with the spec), exit 0 on this branch (both selects identical, 9/9 each).

HTMLSelectElement collected its options from its direct children only, so an
<option> inside an <optgroup> was invisible to options, length, value,
selectedIndex, selectedOptions and to form submission. A grouped select could
not be read, set or submitted, while querySelectorAll("option") still found
every option.

Per HTML §the-select-element a select's list of options is its option element
descendants: its option children plus the option children of its optgroup
children.

- add a select_options collection mode, and move selected_options onto the
  same walk, matching an option whose parent is the select or an optgroup
  child of the select
- add one OptionIterator in Select.zig, used by effectiveOption, setValue,
  getSelectedIndex, setSelectedIndex, getLength and add
- effectiveOption skips options disabled through <optgroup disabled>
  (concept-option-disabled); those options only become reachable now that
  grouped options are part of the list
- select.add(option, index) inserts into the optgroup when the option at that
  index lives in one, per §dom-select-add

Closes lightpanda-io#3057
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.

forms: <option>s inside <optgroup> are invisible to HTMLSelectElement (options, value, selectedIndex, selectedOptions, form submission)

1 participant