Repository navigation
fix(pds-select): keep the inner select in step with changes to existing options - #917
Merged
Merged
Conversation
…ge below the top level
… value unselected
✅ Deploy Preview for pine-design-system ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
11 of 14 tasks
pixelflips
reviewed
Oct 28, 2026
pixelflips
left a comment
Member
There was a problem hiding this comment.
The in-place patching and reconnect handling look solid. Notes inline: one behavior change on first render, two small gaps in the observer, a docs clause, and two spec nits.
pixelflips
approved these changes
Oct 28, 2026
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4450d49. Configure here.
…empties the select
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.

Description
pds-selectcopies its slotted<option>/<optgroup>elements into its inner native<select>, and only re-copied onslotchange. That event fires only when top-level options are added or removed. So an existing option's text,valueordisabledchanging (a framework re-render, async label text), or an option added inside an<optgroup>, never reached the select. The dropdown could disagree with the markup the consumer rendered.Linear: DSS-422. Sibling of DSS-420 / #915 (
pds-tooltipshowing a frozen copy of content that changes below the top level).Fix
MutationObserveron the host's light DOM (childList, subtree, attributes, characterData) schedules one copy per animation frame. It reacts only to mutations inside an option or optgroup: the host's own attributes and children (top-level adds and removes still go throughslotchange) and theactionslot's content are ignored.handleSlotChangestays the single copy path:updateSelectedOptionreselects the current value. If no option has that value any more (say it was renamed), nothing is shown selected (selectedIndex = -1, as a native select does) andvalueis kept: no silent change, nopdsSelectChange. The form value and validity are recomputed after any copy that changed something.componentDidLoadand only for a connected element. On reconnect it restarts and schedules a catch-up copy. On disconnect it stops and cancels a pending frame.updateSelectedOptionowns selection.Review: a 3-reviewer Pine gauntlet (code, security, design) ran on this branch: 0 blockers. All SHOULD FIX and most CONSIDER findings applied (in-place patching, unmatched values, reconnect catch-up, a narrower observer, the
isConnectedguard, tighter tests, docs). A human pass from pixelflips found and fixed a first-render regression (the no-match blank state is now scoped to options changing under an already-matched value), acharacterData-on-a-comment-node crash in the observer filter, acomponentDidLoad/first-slotchangerace (closed the same way the reconnect path already does), a docs overclaim, and tightened two tests.Trade-offs
mainmeasures 359.74 kB, so 0.26 kB of headroom. This change adds about 1.3 kB to that measurement: the check globsp-*.entry.js, which counts the select twice (the ESM chunk and the SystemJS*.system.entry.jschunk). After trimming, the code is already compact. The budget is raised to 362 KB here and in fix(pds-tooltip): keep an open tooltip in step with its slotted content #915 (signed off), sized for both PRs, so each passes on its own. Whichever merges second will conflict on that one line in.size-limit.json; re-check the measured size then and readjust the budget if needed.slotchange; the observer ignores the host's own children so they aren't copied twice.valuedirectly to something with no option behaves as before.Fixes DSS-422
Type of change
How Has This Been Tested?
pds-select.spec.tsx51/51 (13 new: watches at any depth; text, value and disabled patched in place with the same node; optgroup add rebuilds; value kept selected; renamed selected value →selectedIndex-1, value kept, no event; the first copy still falls back to the first option when the value matches nothing, like a native select; callsupdateFormValueonce per changing copy, not on every mutation; one copy per frame; unchanged copies leave nodes alone; host attributes/children and the action slot ignored (including acharacterDatamutation on a comment node, which crashed before); catch-up after reconnect; observer disconnected and pending frame cancelled on removal).pds-select.e2e.ts14/14. The existing-option-text-change case times out againstmain's component and passes with this fix. New: a required select with its selected option renamed away matches:invalid— the one case the unit test can't cover, sinceupdateFormValueis a no-op underisSpecTest().nx run @pine-ds/core:buildregenerates nothing (no API change); ESLint 0 errors (the 5 warnings are pre-existing lines);size-limitas above.Manual a11y check to do before merge (not done yet):
multipleselect, Shift+click a range, change an option's text, then Shift+click again: the range should extend from the same anchor.Test Configuration:
Checklist:
Note
Medium Risk
Touches form-associated selection, validity, and DOM sync on every option mutation; behavior is well-tested but affects core select UX and validation edge cases.
Overview
Fixes
pds-selectshowing a stale dropdown when consumers update slotted<option>/<optgroup>markup without adding or removing top-level options (framework re-renders, async labels, nested optgroup changes)—casesslotchangealone never handled.A
MutationObserveron the host light DOM watches option/optgroup mutations (batched per animation frame) and reuseshandleSlotChangeas the single sync path: skip when markup is unchanged, patch attributes/text in place when structure matches (preserving open-picker highlight and screen-reader position), or rebuild when shape changes. After updates, selection follows the currentvalue; if no option matches anymore, the UI shows nothing selected (selectedIndex = -1) while keepingvalueand not firingpdsSelectChange, with form validity refreshed. Observer lifecycle covers load, reconnect catch-up, and disconnect cleanup.Docs add an “Options stay in sync” usage note; new unit and e2e tests cover in-place updates, unmatched values, async reload, and required
:invalidwhen the selected option’s value is renamed away.Reviewed by Cursor Bugbot for commit 3b7670d. Bugbot is set up for automated code reviews on this repo. Configure here.