Skip to content

[select][combobox] Ignore hidden-input changes while readonly or disabled#4810

Open
lunaxislu wants to merge 3 commits into
mui:masterfrom
lunaxislu:fix/select-combobox-hidden-input-autofill-lock-state
Open

[select][combobox] Ignore hidden-input changes while readonly or disabled#4810
lunaxislu wants to merge 3 commits into
mui:masterfrom
lunaxislu:fix/select-combobox-hidden-input-autofill-lock-state

Conversation

@lunaxislu
Copy link
Copy Markdown
Contributor

Fixes #4809.

Follow-up to #4806.

SelectRoot and AriaCombobox use the same hidden-input autofill pattern that
OTPFieldRoot used before #4806, but their hidden-input onChange handlers
still only guard defaultPrevented, not disabled || readOnly.

This PR applies the same locked-state guard to those remaining handlers.

AriaCombobox is the shared root used by:

  • Combobox.Root (selectionMode='single' | 'multiple')
  • Autocomplete.Root (selectionMode='none')

What changed

  • SelectRoot.tsx: hidden-input onChange guard now matches OTP's
  • AriaCombobox.tsx: same one-line guard update
  • Regression tests added for:
    • Select
    • Combobox (selectionMode='single')
    • Autocomplete (selectionMode='none')

Each regression test fires change on the hidden input and verifies that no public state change happens while readOnly or disabled.

No public API change.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 11, 2026

commit: c358f7d

@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 11, 2026

Bundle size

Bundle Parsed size Gzip size
@base-ui/react 🔺+70B(+0.02%) 🔺+35B(+0.02%)

Details of bundle changes

Performance

Total duration: 1,100.96 ms -226.53 ms(-17.1%) | Renders: 50 (+0) | Paint: 1,669.77 ms -337.90 ms(-16.8%)

Test Duration Renders
Menu mount (300 instances) 119.58 ms ▼-38.37 ms(-24.3%) 2 (+0)
Menu open (500 items) 65.76 ms ▼-23.08 ms(-26.0%) 12 (+0)
Scroll Area mount (300 instances) 80.40 ms ▼-20.33 ms(-20.2%) 3 (+0)
Dialog mount (300 instances) 48.19 ms ▼-13.46 ms(-21.8%) 1 (+0)

8 tests within noise — details


Check out the code infra dashboard for more information about this PR.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for base-ui ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit c358f7d
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/6a05d3d07ceb03000894abc8
😎 Deploy Preview https://deploy-preview-4810--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@zannager zannager added component: select Changes related to the select component. component: combobox Changes related to the combobox component. labels May 11, 2026
Comment thread packages/react/src/combobox/root/AriaCombobox.tsx
@mj12albert mj12albert added the type: bug It doesn't behave as expected. label May 11, 2026
@lunaxislu lunaxislu requested a review from mj12albert May 12, 2026 07:26
Comment thread packages/react/src/combobox/root/AriaCombobox.tsx Outdated
@lunaxislu lunaxislu requested a review from mj12albert May 14, 2026 13:57
@atomiks
Copy link
Copy Markdown
Contributor

atomiks commented May 15, 2026

Code Review (GPT-5.5)

Request changes 🔴 because the same readOnly hidden-input autofill path still exists in NumberField, and the new regression coverage can be much smaller.

1. Bugs / Issues

1. 🔴 NumberField still accepts hidden-input autofill while readOnly (blocking)

The PR fixes Select and AriaCombobox, but NumberField has the same supported hidden-input autofill path and still only guards defaultPrevented. By code reading, a readOnly NumberField can still update through this hidden input because the input does not receive readOnly, and the handler does not check the root locked state.

packages/react/src/number-field/root/NumberFieldRoot.tsx

onChange(event) {
  // Workaround for https://github.com/facebook/react/issues/9023
  if (event.nativeEvent.defaultPrevented) {
    return;
  }

  // Handle browser autofill.
  const nextValue = event.currentTarget.valueAsNumber;
  const parsedValue = Number.isNaN(nextValue) ? null : nextValue;

  setDirty(parsedValue !== validityData.initialValue);
  setValue(parsedValue, details);
}

This leaves the same class of bug open in another public component that already has a hidden-input browser autofill test.

Fix: Apply the same locked-state guard to NumberFieldRoot and add a regression for at least readOnly hidden-input autofill; include event.preventBaseUIHandler?.() there too when the handler is wrapped by Field.Root.

2. 🟡 The locked-state regression tests can be table-driven (non-blocking)

The new tests repeat the same matrix several times: readOnly vs disabled, inside Field.Root vs outside Field.Root, then the same hidden-input change and “no public state changed” assertions. That makes this bug fix look larger than it is.

packages/react/src/select/root/SelectRoot.test.tsx

it('ignores hidden-input autofill when readOnly', async () => { ... });
it('ignores hidden-input autofill when disabled', async () => { ... });
it('ignores hidden-input autofill when readOnly outside Field', async () => { ... });
it('ignores hidden-input autofill when disabled outside Field', async () => { ... });

Recommendation: Extract small render/assert helpers per component and drive the lock state with it.each(['readOnly', 'disabled'] as const). For Select and Combobox, a second table dimension for withField would keep the Field error assertion where it matters while removing most of the duplicated JSX.

2. Simplification Opportunities

The production change is already close to minimal for Select and AriaCombobox. If NumberField is included too, a tiny shared predicate may be worth it, for example “ignore this hidden input change when the native event was already prevented or the root is locked, and prevent the Field validation handler when possible.” I would only add that helper if it removes duplication across Select, AriaCombobox, and NumberField; otherwise the source-level inline guard is fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: combobox Changes related to the combobox component. component: select Changes related to the select component. type: bug It doesn't behave as expected.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[select][combobox] Hidden input ignores readonly/disabled during browser autofill

4 participants