Skip to content

[Bug]: Fluent UI web-components Dropdown does not tolerate value being set before its slotted listbox/control is initialized #36189

@brianchristopherbrady

Description

@brianchristopherbrady

Component

Other...

Package version

3.0.0-rc.15

@microsoft/fast-element version

^2.10.4

Environment

OS: Windows
Workspace: fluentui
Current directory: fluentui
Node: v22.22.2
fnm current: v22.22.2
Repo-pinned Yarn: 1.23.34 via node .yarn/releases/cli.js
Git state after your commit: only .yarnrc.yml is untracked

Current Behavior

Fabric’s Angular dropdown wrapper currently needs a lifecycle workaround because Fluent UI web-components Dropdown does not tolerate value being set before its slotted listbox/control is initialized.

Angular forms can call ControlValueAccessor.writeValue() before the custom element has finished wiring its internal control and slotted listbox. In that case, setting dropdown.value early can throw instead of behaving like an initial value that is applied once the component finishes initialization.

This appears to come from the Fluent dropdown value setter calling selection logic that assumes this.listbox already exists.

Observed Behavior

When dropdown.value is set before the dropdown has assigned its slotted listbox:

dropdown.value = "apple";

the setter can call:

this.selectOption(this.enabledOptions.findIndex((x) => x.value === next));

and selectOption() can then call:

this.listbox.selectOption(index);

Before this.listbox is available.

That can throw during framework wrapper initialization, especially in Angular forms where writeValue() may run before projected content/custom element setup is complete.

Expected Behavior

Setting value before listbox initialization should be safe.

The dropdown should store the requested value as pending initial state and apply it after the slotted listbox and options are ready.

Expected outcomes:

dropdown.value = "apple" before listbox assignment should not throw.
Once the listbox/options are initialized, the matching option should be selected.
dropdown.value should then return "apple".
dropdown.value = null before initialization should also not throw and should clear/no-op safely.
Existing multiple behavior should remain unchanged.
Why This Matters

Framework wrappers should be able to treat value like a normal initial property.

Angular forms in particular can call writeValue() before the custom element’s internal listbox/control is ready. The wrapper should not need a MutationObserver to wait for internal web component readiness before assigning value.

Reproduction

https://stackblitz.com/edit/vitejs-vite-xog9yixu?file=package.json,index.html,src%2Fmain.ts

Steps to reproduce

  1. Install @fluentui/web-components@3.0.0-rc.15.

  2. Create a Fluent dropdown with a slotted listbox and options.

  3. Set the dropdown [value](vscode-file://vscode-app/c:/Users/brianbrady/AppData/Local/Programs/Microsoft%20VS%20Code/034f571df5/resources/app/out/vs/code/electron-browser/workbench/workbench.html) before the listbox has been assigned/initialized.

Example:

import {
  Dropdown,
  DropdownDefinition,
  ListboxDefinition,
  OptionDefinition
} from "@fluentui/web-components";

DropdownDefinition.define();
ListboxDefinition.define();
OptionDefinition.define();

const dropdown = document.createElement("fluent-dropdown") as Dropdown;

dropdown.value = "apple";

const listbox = document.createElement("fluent-listbox");
const apple = document.createElement("fluent-option");
apple.value = "apple";
apple.textContent = "Apple";

listbox.appendChild(apple);
dropdown.appendChild(listbox);

document.body.appendChild(dropdown);
  1. Observe that setting dropdown.value = "apple" exists.

More direct reproduction:

const dropdown = document.createElement("fluent-dropdown") as Dropdown;

// No listbox assigned yet.
dropdown.value = "apple";

Expected

Setting value before listbox initialization should not throw. The value should be treated like pending initial state and applied once the listbox/options are ready.

Actual

The dropdown can throw because value calls selectOption(...), and selectOption() assumes this.listbox exists.

Are you reporting an Accessibility issue?

None

Suggested severity

High - No workaround

Products/sites affected

Fabric

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

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