Skip to content

[Bug]: Radio Group Pre-Upgrade Property Shadowing Breaks checked State #36239

@Mich0608

Description

@Mich0608

Component

RadioGroup

Package version

3.0.0-rc.20

@microsoft/fast-element version

2.10.4

Environment

System:
    OS: macOS 26.4.1
    CPU: (8) arm64 Apple M3
    Memory: 298.63 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Browsers:
    Chrome: 148.0.7778.97
    Edge: 148.0.3967.83
    Firefox: 150.0.1
    Safari: 26.4

Current Behavior

When BaseRadioGroup.slottedRadiosChanged runs before slotted <fluent-radio> elements have been upgraded (e.g. RadioGroupDefinition.define() is called before RadioDefinition.define()), the radio group collects bare HTMLElement nodes into this.radios.

// radio-group.base.ts
slottedRadiosChanged(prev, next) {
  this.radios = [...this.querySelectorAll('*')].filter(x => isRadio(x));
}

isRadio is a tag-name suffix check (-radio) inherited from isCustomElement, so it matches elements regardless of upgrade state:

export function isCustomElement(tagSuffix) {
  return (element) =>
    element?.nodeType === Node.ELEMENT_NODE &&
    element.tagName.toLowerCase().endsWith(tagSuffix);
}

Downstream radiosChanged then synchronously writes .checked (and name, disabled, etc.) on each entry. Because these targets are still bare HTMLElements, the assignments become own data properties on the instance. When the element later upgrades to Radio, those own data properties permanently shadow the inherited BaseCheckbox.checked accessor — so every subsequent write through the setter is silently dropped:

  • :state(checked) is never added → indicator dot never paints
  • setFormValue is never called → no form participation
  • setAriaChecked is never called → broken aria-checked
  • Observable.notify('checked') is never called → no change propagation

Regression history

The previous implementation filtered with instanceof Radio (which is false for unupgraded elements) and deferred collection via Updates.enqueue. A refactor replaced both — switching to the isRadio tag check and removing the deferral — which is what introduced this race.

Expected Behavior

RadioGroup should never write to elements that haven't upgraded yet, because doing so corrupts their prototype chain once they do.

Reproduction

https://stackblitz.com/edit/vitejs-vite-jwyknbey?file=src%2Fmain.ts

Steps to reproduce

  1. Go to Stackblitz repro
  2. See that radio Option 2 should be checked according to the code
  3. Observe that Option 2 is not checked in the rendered control

Are you reporting an Accessibility issue?

None

Suggested severity

Urgent - No workaround and Products/sites are affected

Products/sites affected

Fabric UX and Power BI

Are you willing to submit a PR to fix?

yes

Validations

  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • The provided reproduction is a minimal reproducible example of the bug.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions