@atomic-testing/component-driver-fluent-v9
Component drivers for Fluent UI v9 ("Fluent 2", @fluentui/react-components). Component drivers expose simple APIs for unit tests or end-to-end tests to interact with Fluent-based componentsβreading state and driving actionsβso test engineers focus on test flows instead of the component internals.
The problemβ
Fluent v9 styles every component with Griffel, an atomic CSS-in-JS engine β the classes it emits are hashed and change across builds, so they are not stable test anchors. Fluent also ships a strong accessibility program (it is the Microsoft 365/Office design system), so the stable anchors, in priority order, are:
role+ ARIA state β Fluent renders correct roles/aria-*per component (e.g.aria-pressedonToggleButton,aria-disabledonLink).- Fluent's own un-hashed structural classes β every component stamps a plain
fui-<ComponentName>class (andfui-<ComponentName>__<part>for sub-parts, e.g.fui-Field__hint) alongside the hashed Griffel utility classes. These are Fluent-owned and stable across releases; the drivers in this package use them where role/ARIA isn't enough (e.g.FieldDriver's hint/validation-message reads). - Never the hashed Griffel utility classes.
Several core controls (Input, Textarea, Checkbox, Switch, Radio, Select) render as real native form elements at their root β data-testid (or any locator) placed on the component lands directly on the native <input>/<textarea>/<select>, not a styled wrapper β so this package reuses @atomic-testing/component-driver-html's drivers wholesale wherever that holds.
The solutionβ
The drivers in this package locate Fluent parts by those stable anchors and expose high-level interactions. Combined with the React adapter (@atomic-testing/react-19 or another React major), the same scene definitions run across DOM (jsdom) and end-to-end (Playwright) tests.
Target package & version pinβ
This driver targets Fluent UI v9 and is declared as a peer dependency pinned to ^9.0.0: consumers bring their own @fluentui/react-components. Fluent v8 (@fluentui/react) is a materially different DOM/styling contract (mergeStyles, no Griffel) and is out of scope for this package.
Installationβ
npm install @atomic-testing/core @atomic-testing/react-19 \
@atomic-testing/component-driver-html @atomic-testing/component-driver-fluent-v9 \
@fluentui/react-components --save-dev
Refer to the documentation for usage patterns and examples.
Portal & overlay recipe (Wave 2)β
Dialog, Popover, Drawer (OverlayDrawer), Menu, Toast/Toaster, and TeachingPopover all portal by default β verified against rendered DOM (@fluentui/react-components@9.74.3): each mounts into a cloned FluentProvider on document.body (Fluent's own mountNode default, confirmed against @fluentui/react-portal's types), a sibling of the render root rather than a descendant of the trigger. InlineDrawer is the one exception β it renders in-tree, no portal at all. Two distinct re-root techniques cover this wave, chosen per component by whether the scene's own locator can land directly on the portalled surface:
- Static re-root + class compounding (
Dialog,Popover,OverlayDrawer,TeachingPopover,Toaster) β the driver overrides theoverriddenParentLocator()/overrideLocatorRelativePosition()static hooks (seepackages/core/src/drivers/ComponentDriver.ts) to re-root at the un-hashed Fluent structural class of that surface (e.g..fui-DialogSurface,.fui-PopoverSurface), and the scene's own locator (forwarded onto the surface component, e.g.<DialogSurface data-testid="...">) compounds onto that SAME element. Two simultaneously open instances disambiguate correctly because each surface only matches its own forwarded test id β verified with two open dialogs/popovers side by side. Anchored on the class rather thanrolewhere the role is shared:role="dialog"alone is worn byDialog,OverlayDrawer, andTeachingPopover;role="group"is far too generic forPopoveralone. - Trigger-anchored +
byLinkedElement(Menu) β the driver is constructed from the TRIGGER locator and resolves the portalledMenuListby following the trigger'sidto the list'saria-labelledby, re-read fresh on every call (byLinkedElement, the same techniquecomponent-driver-radix-v1uses for its ownaria-controls/aria-describedbylinks). Necessary becauserole="menu"/role="presentation"are identical across every simultaneously open menu β a static class/role re-root cannot tell "this menu" from a sibling one, but the triggerβlist id link can. Verified with two open menus side by side. - Trigger-anchored +
byLinkedElementonaria-controls(Combobox,Dropdown,TagPicker, Wave 3) β the samebyLinkedElementidiom asMenu, but following the trigger/input'saria-controls(notaria-labelledby) to the portalled listbox, and the attribute is present ONLY while open (absent entirely, not merely empty, once closed) β every listbox-reading method guards the resolution in try/catch, andisOpen()reads the trigger's ownaria-expandedrather than listbox existence, since Fluent keeps the listbox mounted briefly after a legitimate close.
Tooltip is trigger-anchored too, but for a different reason: its content carries no reliable per-instance link in the default relationship="label" mode (only relationship="description" sets aria-describedby), so isOpen() falls back to a best-effort shared-portal check in that mode β see the Known gaps section below and TooltipDriver's TSDoc.
Escape dismisses the topmost stacked overlay, not a specific targeted one β verified against real Chromium: with two overlays open, Escape always closes the most-recently-opened one, regardless of which overlay's locator the key event is dispatched on (Fluent's dismiss handling is a global, stack-ordered listener). Drive closeByEscape() on the LAST-opened instance in a stacked scenario.
Driversβ
Drivers land in waves (see the umbrella issue #1098); all 6 waves (core form primitives; overlays & portals; selection & specialized inputs; navigation & disclosure; data display & feedback; complex/composite) ship in full below, plus a Wave 6 follow-up (issue #1138) adding FlatTree/FlatTreeItem β this is the complete driver catalog.
| Driver | Fluent component | Notes |
|---|---|---|
ButtonDriver | Button | Native <button>; delegates wholesale to HTMLButtonDriver. |
CompoundButtonDriver | CompoundButton | Same native <button> root as Button; getSecondaryContent() reads the fui-CompoundButton__secondaryContent part (see JSDoc for the known primary/secondary text-splitting limitation). |
ToggleButtonDriver | ToggleButton | Native <button>; pressed state read/written via aria-pressed (no native "pressed" concept exists for <button>). |
InputDriver | Input | The root IS a native <input> β full HTMLTextInputDriver surface, incl. isError via aria-invalid. |
TextareaDriver | Textarea | The root IS a native <textarea> β full HTMLTextAreaDriver surface. |
CheckboxDriver | Checkbox | Extends HTMLCheckboxDriver (the root IS a real native <input type="checkbox">); label prop renders a sibling <label for>, resolved via the forβid link. isIndeterminate() reads the live .indeterminate property via the :indeterminate CSS pseudo-class. |
SwitchDriver | Switch | Same shape as Checkbox, but no value concept (pure on/off) β does not implement IFormFieldDriver. |
RadioDriver | Radio | The root IS a real native <input type="radio">; setSelected(false) is rejected (native radio semantics). |
RadioGroupDriver | RadioGroup | Delegates to HTMLRadioButtonGroupDriver β point its ScenePart locator at the radio inputs (e.g. the group container appended with an input[type="radio"] descendant selector), not at the [role="radiogroup"] wrapper. |
SelectDriver | Select | The root IS a native <select> β full HTMLSelectDriver surface. |
LabelDriver | Label | Plain native <label>; getFor() reads the linked control's id. |
FieldDriver | Field | Container wrapper; getLabel/getHint/getValidationMessage read descendant parts anchored on Fluent's fui-Field__* structural classes. |
LinkDriver | Link | Native <a>; overrides isDisabled to read aria-disabled (an anchor has no native disabled property). |
DividerDriver | Divider | [role="separator"]; getOrientation() reads aria-orientation. |
ImageDriver | Image | Native <img>; getSrc/getAlt read attributes directly. |
TextDriver | Text | Plain content wrapper; all behavior is inherited (getText). |
Wave 2 β overlays & portals (see the portal & overlay recipe above):
| Driver | Fluent component | Notes |
|---|---|---|
DialogDriver | Dialog (+ DialogSurface/Body/Title/Content/Actions) | Portalled; re-roots on .fui-DialogSurface. isModal() reads aria-modal; non-modal dialogs auto-render a close X. No portable closeByBackdropClick (the backdrop is an un-linkable document.body sibling, not a descendant) β only closeByEscape. |
PopoverDriver | Popover (+ PopoverTrigger/Surface) | Portalled; re-roots on .fui-PopoverSurface (role is the too-generic "group"). Shares that class with TeachingPopoverSurface β use TeachingPopoverDriver for that component specifically. |
OverlayDrawerDriver | OverlayDrawer | Portalled; re-roots on .fui-OverlayDrawer (role="dialog", shared with Dialog/TeachingPopover). defaultOpen is deprecated/non-functional β drive it via the controlled open prop. |
InlineDrawerDriver | InlineDrawer | Renders in-tree β no portal, no re-root, unlike every other driver in this wave. |
DrawerDriverBase | shared base | getHeaderTitle/getBodyText + open/close lifecycle common to both drawer variants. |
MenuDriver | Menu (+ MenuTrigger/Popover/List) | Constructed from the TRIGGER locator; resolves the portalled MenuList via the trigger id β list aria-labelledby link (byLinkedElement) β correctly disambiguates two simultaneously open menus. |
MenuItemDriver | MenuItem | role="menuitem"; getLabel/isDisabled. |
MenuItemCheckboxDriver | MenuItemCheckbox | role="menuitemcheckbox"; adds isChecked() via aria-checked. Selecting one persists the open menu. |
MenuItemRadioDriver | MenuItemRadio | role="menuitemradio"; adds isChecked() via aria-checked. Selecting one closes the menu (unlike checkbox items) β re-open to observe the persisted choice. |
MenuButtonDriver | MenuButton | Native <button> (delegates to HTMLButtonDriver); getMenu() returns the MenuDriver it opens. |
SplitButtonDriver | SplitButton | Wrapper <div> around a primary action button and a menu-invoking button; clickPrimary()/getMenu() expose each half separately (the base click() on the wrapper is not meaningful). |
TooltipDriver | Tooltip | Trigger-anchored. getContent() reads aria-label (default relationship="label") or follows aria-describedby (relationship="description") β available regardless of open state, since Fluent mounts the content unconditionally. isOpen()'s fallback for "label"-relationship tooltips is best-effort when multiple tooltips are mounted (see Known gaps). Opens via .focus(), not hover (hover does not reveal Fluent's tooltip under jsdom). |
ToasterDriver | Toaster | Portalled; re-roots on .fui-Toaster. Deliberately has no caller-supplied interior β toasts arrive dynamically via dispatchToast, read positionally/by-title (getToastByIndex/getToastByTitle) rather than as a declared scene. |
ToastDriver | Toast (+ Title/Body) | getTitle/getBodyText; no built-in dismiss button (use a declared content part for consumer-supplied actions, like component-driver-mui-v9's SnackbarDriver). |
TeachingPopoverDriver | TeachingPopover (+ Header/Title/Body/Footer) | Portalled; re-roots on the more-specific .fui-TeachingPopoverSurface (not the shared .fui-PopoverSurface). Has a built-in dismiss button, unlike plain Dialog/Popover. |
Wave 3 β selection & specialized inputs:
| Driver | Fluent component | Notes |
|---|---|---|
ComboboxDriver | Combobox (+ Option/OptionGroup) | Root IS a native <input role="combobox"> β extends HTMLTextInputDriver wholesale. Listbox is trigger-anchored via aria-controls byLinkedElement (same technique as MenuDriver). No selectByValue β Fluent never reflects Option's value to the DOM, only the label. Single-select only. |
ComboboxOptionDriver | Option | role="option"; getLabel/isDisabled/isSelected β a standalone class, not sharing MenuItemDriver's inheritance. |
DropdownDriver | Dropdown (@fluentui/react-select) | NOT the native-<select>-backed Select despite the shared naming β a fully custom combobox widget. Trigger is a real <button role="combobox">; isOpen() reads its aria-expanded (the listbox stays mounted post-close). Listbox resolved via aria-controls byLinkedElement. Single-select only. |
DropdownOptionDriver | Option (single-select Dropdown) | role="option"; getLabel/isSelected/isDisabled. |
SliderDriver | Slider | Root IS a native <input type="range"> β extends HTMLRangeInputDriver wholesale. Adds getMin/getMax/getStep/isRequired/getLabel. Single-thumb only β Fluent v9 ships no multi-thumb variant. |
SpinButtonDriver | SpinButton | Root IS the native <input role="spinbutton">; stepper <button>s reached via the general-sibling combinator (~), same escape hatch as component-driver-astryx's NumberInputDriver. setValue types then blurs to commit; increment/decrement click the steppers; moveToMin/moveToMax/incrementByPage/decrementByPage drive Home/End/PageUp/PageDown. |
SwatchPickerDriver | SwatchPicker | Does not portal. Items matched on the un-hashed .fui-ColorSwatch class (role flips radio/gridcell with layout). No getValue/selectByValue β color-based equivalents instead (getSwatchColors/selectByColor/getSelectedColor). |
SwatchPickerItemDriver | ColorSwatch | Real native <button>; isSelected() reads aria-checked falling back to aria-selected. setSelected(false) rejected (no self-deselect). getColor() reads the --fui-SwatchPicker--color inline CSS var β no getValue(). |
RatingDriver | Rating | Root IS role="radiogroup" with visually-hidden native radio items; getValue/setValue drive the :checked radio via Interactor.activate. No native disabled/readOnly β isDisabled() reads a :disabled descendant radio (consumer <fieldset disabled> cascade). |
RatingDisplayDriver | RatingDisplay | Read-only sibling of Rating; root is role="img", never radiogroup. Does not implement IInputDriver. getValue/getCount read fui-RatingDisplay__valueText/__countText. |
TagDriver | Tag | Static, non-dismissible tag; root IS <span class="fui-Tag">. getLabel() reads the fui-Tag__primaryText part. Does not implement IDisableableDriver β disabled has zero DOM reflection. |
InteractionTagDriver | InteractionTag (+ Primary/Secondary) | Dismissible tag; wrapper <div> around two real native <button>s, delegated wholesale to HTMLButtonDriver. isDisabled() reads the Primary button's disabled. clickPrimary() selects; dismiss() removes. |
TagGroupDriver | TagGroup | List container; never portals. isDisabled() reads the group's own always-present aria-disabled. getTagCount/getTagLabels/getTagByIndex enumerate mixed Tag/InteractionTag children positionally. |
TagPickerDriver | TagPicker (+ Control/Group/Input/List/Option) | Constructed from TagPickerControl's locator (<TagPicker> itself renders no DOM). Portalled TagPickerList resolved via aria-controls byLinkedElement; isOpen reads the input's aria-expanded. getSelectedLabels/removeSelected read the in-tree TagPickerGroup; getOptionCount/selectByLabel auto-open the portalled list. |
TagPickerOptionDriver | TagPickerOption / a selected Tag | Shared getLabel/isDisabled surface for both an open-list option and an already-selected tag (both render role="option"). No portable isSelected for either. |
SearchBoxDriver | SearchBox | Same native <input type="search"> root as Input β full HTMLTextInputDriver surface, plus clear()/hasClearButton(). The dismiss button renders unconditionally by default and is a sibling with no per-instance link, resolved via the ancestor-:has() re-root (same idiom as component-driver-mui-v9's CheckboxDriver). |
ColorPickerDriver | ColorPicker (+ ColorArea/ColorSlider) | Composite; renders its own <div class="fui-ColorPicker"> root, unlike a bare context provider. area/hueSlider parts locate children by un-hashed structural classes. Strictly a controlled component (color/onColorChange only). |
ColorAreaDriver | ColorArea | The 2-D saturation/value picker; root wraps two real native <input type="range">s (inputX/inputY), driven via setRangeValue/getInputValue. No disabled/required (not valid attributes on a plain <div>). |
ColorSliderDriver | ColorSlider | The 1-D hue slider; locator lands straight on a real native <input type="range"> β same shape as the standalone SliderDriver, so this driver extends HTMLRangeInputDriver wholesale. |
Wave 4 β navigation & disclosure: none of this wave's components portal by default except NavDrawer (which reuses the Wave 2 OverlayDrawer recipe). A unifying design rule shapes the whole wave: a driver models each independently INTERACTIVE/addressable unit; purely structural wrapper elements (a decorative divider, a header/panel pairing that's always 1:1 with its item, a non-interactive group <div>) fold into their interactive sibling's driver rather than getting their own class β the same way AccordionHeader/AccordionPanel fold into AccordionItemDriver, and BreadcrumbDivider isn't modeled at all (see its row below).
| Driver | Fluent component | Notes |
|---|---|---|
TabListDriver | TabList | A ListComponentDriver over role="tab" children (templated off component-driver-mui-v9's TabsDriver), since TabList has no built-in TabPanel β panel wiring is left to the consumer. Tab's value prop genuinely reflects onto the native value attribute (unlike Option.value elsewhere in this package), so getSelectedValue/selectByValue are reliable, un-hashed reads. |
TabDriver | Tab | Real <button role="tab">; aria-selected is absent entirely (not "false") when disabled. Overrides getText(): when reserveSelectedTabSpace (the default) and the tab is unselected, Fluent renders an invisible SECOND copy of the label in a fui-Tab__content--reserved-space span purely to reserve layout width β the inherited whole-button text read would return the label doubled (verified: "ProfileProfile"), so this driver reads .fui-Tab__content specifically instead. |
BreadcrumbDriver | Breadcrumb | Items live inside a child <ol role="list">, interleaved with decorative <li> dividers β both share the <li> tag, so item enumeration uses childListHelper's :nth-child + class-selector filter (same mixed-sibling shape TagGroupDriver handles) rather than ListComponentDriver. BreadcrumbDivider has no driver β purely decorative (aria-hidden), see the wave-level rule above. |
BreadcrumbItemDriver | BreadcrumbItem | The <li> wrapper itself carries no interactive state; folds in BreadcrumbButton's behavior via getButton() and overrides click() to target the button, not the wrapper. |
BreadcrumbButtonDriver | BreadcrumbButton | Renders <a> (with href) or <button> β isDisabled() combines a native-disabled check with an aria-disabled read (mirrors LinkDriver's reasoning) since either element shape is possible. isCurrent() reads aria-current="page", present only when the current prop is set. |
AccordionDriver | Accordion | A ListComponentDriver over fui-AccordionItem children (homogeneous <div> siblings, safe for :nth-of-type). multiple/collapsible mode has zero DOM reflection (grepped the compiled package) β this driver exposes no mode getter; drive/observe expansion per item instead. |
AccordionItemDriver | AccordionItem (+ AccordionHeader/AccordionPanel) | Folds header/panel state in directly (templated off component-driver-radix-v1's single-item AccordionDriver). AccordionPanel fully UNMOUNTS while collapsed (Fluent's hard-coded unmountOnExit) β getPanelText() returns null whenever absent. collapse() no-ops on the parent's OWN collapsible prop (Fluent refuses to reach zero open items otherwise) β see Known Gaps. click() no-ops on a disabled header, same portability contract as RadioDriver.setSelected. |
NavDriver | Nav | Never portals. Item enumeration (NavItem/NavCategory/NavCategoryItem/NavSubItem, arbitrarily nested) walks via childListHelper's groupSelector: '*' recursion, flattening the tree. Shares its item-query surface with NavDrawerDriver via the internal NavDriverBase. |
NavDrawerDriver | NavDrawer | Portal-backed by default (wraps Drawer β OverlayDrawer, same as Wave 2) β re-roots on the un-hashed fui-NavDrawer class. Corrects the umbrella issue's hypothesis: "Nav's flyouts are overlay-backed" is true only of NavDrawer's own outer surface, NOT of NavCategory expansion inside it (see NavCategoryItemDriver). Targets the default (portal) variant only β type="inline" isn't covered. |
NavItemDriver | NavItem / NavSubItem | Renders <a href> or <button> β the component's OWN root IS the interactive element (no wrapper to look past, unlike BreadcrumbItemDriver). aria-current is always a literal "page"/"false" string, never merely absent. |
NavCategoryItemDriver | NavCategoryItem | Extends NavItemDriver, adding expand/collapse. Always a real <button>, never <a>. No portal β grepped the entire compiled @fluentui/react-nav package for Popover/Menu/role="menu": zero matches; the sub-item group is a same-tree animated accordion, reached via the general-sibling CSS combinator (same escape hatch SpinButtonDriver uses for its steppers). |
ToolbarDriver | Toolbar | aria-orientation is present only when vertical β getOrientation() defaults the horizontal case rather than passing through null. Button enumeration (getButtonByLabel) descends one level into .fui-ToolbarGroup wrappers via childListHelper's groupSelector (ToolbarRadioGroup shares this same class, no separate fui-ToolbarRadioGroup class is exported), since ToolbarButton/ToolbarToggleButton/ToolbarRadioButton all share the identical fui-Button class. |
ToolbarButtonDriver | ToolbarButton | Plain native <button class="fui-Button"> β no fui-ToolbarButton class of its own; delegates wholesale, like ButtonDriver. |
ToolbarDividerDriver | ToolbarDivider | Delegates to DividerDriver wholesale (identical DOM, no separate class). Orientation is INVERTED relative to the toolbar β verified against source (vertical: !toolbarContext.vertical): a horizontal toolbar's divider itself reports 'vertical'. |
ToolbarRadioGroupDriver | ToolbarRadioGroup | ToolbarRadioGroup IS ToolbarGroup with role="radiogroup" forced on, sharing its class β no native :checked/[value=] to delegate to HTMLRadioButtonGroupDriver, so this is a ListComponentDriver over role="radio" buttons instead (mirrors TabListDriver's shape). |
ToolbarRadioButtonDriver | ToolbarRadioButton | Real <button role="radio" aria-checked> β Fluent explicitly strips the aria-pressed its shared toggle-button primitive would otherwise carry. setSelected(false) is rejected, same contract as RadioDriver. |
OverflowDriver | Overflow | Overflow/OverflowItem render NO wrapper of their own β they clone ref/class/data-* attributes onto the consumer's OWN child, so the scene locator must target that element directly, assuming the idiomatic flat-row usage (every item a direct child of the wrapped container). getOverflowMenu() returns a plain Wave 2 MenuDriver resolved via [data-overflow-menu] β no new portal logic needed, since useOverflowMenu() wires a trigger but builds no Menu of its own. |
OverflowItemDriver | OverflowItem | isOverflowing() reads the data-overflowing attribute directly (portable/jsdom-safe) rather than relying on computed CSS visibility, since overflowed items stay mounted (display: none via a stylesheet rule) rather than being removed. |
Wave 5 β data display & feedback: presentational/read-state drivers β the fastest wave per-component (few new interactions, no portals except InfoButton's own inline-by-default popover). A unifying design rule shapes several of this wave's composite components: CardDriver folds CardHeader/CardFooter/CardPreview reads directly onto itself (mirroring DialogDriver/TeachingPopoverDriver's "one composite driver" shape from Wave 2), rather than minting a driver class per sub-component β none of the three are independently addressable/interactive the way a repeated list item (AvatarGroupItem, ListItem) is.
| Driver | Fluent component | Notes |
|---|---|---|
AvatarDriver | Avatar | role="img" aria-label="{name}"; getName() reads that aria-label (present regardless of whether a badge is also set). getInitials() reads the fui-Avatar__initials part, undefined when an image renders instead. getPresenceBadge() returns a nested PresenceBadgeDriver for the fui-Avatar__badge part. |
AvatarGroupDriver | AvatarGroup | A ListComponentDriver over AvatarGroupItem wrappers (NOT the nested .fui-Avatar elements directly β each is the sole avatar under its own wrapper, so :nth-of-type addressing on the avatar itself would miscount; see AvatarGroupItemDriver). Overflow (AvatarGroupPopover, the +N trigger) is out of scope β only inline items are enumerated. |
AvatarGroupItemDriver | AvatarGroupItem | Wrapper with no state of its own; getAvatar() returns the nested AvatarDriver. |
BadgeDriver | Badge | Plain <div class="fui-Badge">; all state is its own text content (inherited getText()) β appearance/color/shape/size have no un-hashed DOM reflection. |
CounterBadgeDriver | CounterBadge | Extends BadgeDriver wholesale (identical DOM shape plus a marker class). getDisplayedCount() reads the already-overflowCount-clamped text (e.g. count={150} with the default overflowCount={99} renders "99+"). |
PresenceBadgeDriver | PresenceBadge | role="img" aria-label="{status}" β for a plain status the label IS the status string; getStatusLabel() reads it raw (the outOfOffice composite label text is unverified β see Known gaps). |
CardDriver | Card (+ CardHeader/CardFooter/CardPreview) | Folds header/footer/preview reads directly (see the wave-level rule above). A selectable card (selected/onSelectionChange supplied) renders a REAL native <input type="checkbox" class="fui-Card__checkbox">, driven directly via Interactor.isChecked/click. isDisabled() reads the root's aria-disabled (present regardless of selectable). |
PersonaDriver | Persona | getPrimaryText/getSecondaryText/getTertiaryText read their own fui-Persona__* structural classes (the whole-root getText() would double-count the avatar's own initials). getAvatar()/getPresenceBadge() reach the avatar or (in presenceOnly mode) the standalone presence slot. |
ListDriver | List | A ListComponentDriver over .fui-ListItem children β homogeneous siblings regardless of root tag (<ul>/<ol>/<div>) or role (role="list" plain, role="listbox" when selectionMode is set). getSelectedValues() enumerates selected items' values. |
ListItemDriver | ListItem | Its value prop reflects directly onto a plain value attribute (a rare un-hashed prop reflection in this package). isSelected() reads aria-selected, present only when the parent's selectionMode is set. |
SkeletonDriver | Skeleton (+ SkeletonItem) | role="progressbar" aria-busy="true"; individual items carry zero distinguishable state, so this driver exposes only getItemCount() rather than a per-item class (same "no interactive unit" call Wave 4 makes for BreadcrumbDivider). |
SpinnerDriver | Spinner | role="progressbar"; getLabel() reads the fui-Spinner__label part, undefined when rendered without one. |
ProgressBarDriver | ProgressBar | role="progressbar" with aria-valuemin/aria-valuemax/aria-valuenow; aria-valuenow is entirely ABSENT (not "0") in the indeterminate (no value prop) state β isIndeterminate() reads that absence directly. |
InfoLabelDriver | InfoLabel | The scene locator lands on the inner <label> (InfoLabel's PRIMARY slot), not the wrapping <span> β extends LabelDriver wholesale. getInfoButton() reaches the sibling InfoButton via an ancestor :has() re-root (same idiom as SearchBoxDriver's wrapper). |
InfoButtonDriver | InfoButton | Unlike every other Popover-backed overlay in this package, InfoButton's inline prop defaults to true β its popover surface renders as a plain DOM SIBLING (adjacent-sibling-combinator addressed), no portal re-root needed. Targets only that default case; inline={false} (portal) is out of scope. |
MessageBarDriver | MessageBar (+ MessageBarBody/Title/Actions) | getTitle() reads fui-MessageBarTitle exactly; getBodyText() reads the WHOLE fui-MessageBarBody, which includes the title's text too since Title nests INSIDE Body (known gap, same class as CompoundButtonDriver's). No built-in dismiss β declare the consumer-supplied action as its own scene part, same as ToastDriver. |
AlertDriver | Alert | Deprecated by Fluent itself (AlertProps's own TSDoc: "use the Toast or MessageBar component") and importable ONLY from @fluentui/react-components/unstable β NOT the stable package surface. getText() includes an action button's text when present (no isolated message read; known gap). |
Wave 6 β complex/composite (the highest-engineering-cost wave, saved for last by design): Table/DataGrid β the single largest driver family in this catalog, split the same way component-driver-mui-v9's Table family is (a top-level driver over rows, rows over cells, plus a distinct header-row driver) β Tree, and Carousel.
| Driver | Fluent component | Notes |
|---|---|---|
TableDriver | Table | A ListComponentDriver over data (body) rows (.fui-TableBody .fui-TableRow), templated off component-driver-mui-v9's TableDriver. Header row is a separate TableHeaderRowDriver rather than folded in β a real DOM difference from MUI (Fluent's TableCell/TableHeaderCell are two structurally distinct components, <td>/<th>, each with its own structural class), not a stylistic choice. sortByColumn/getSortDirection are generic/best-effort (read aria-sort, click the header cell) since plain Table ships no built-in sort orchestration. |
TableRowDriver | TableRow (body) | A ListComponentDriver over .fui-TableCell children. |
TableHeaderRowDriver | TableRow (header) | A ListComponentDriver over .fui-TableHeaderCell children; adds best-effort getSortDirection/sortByColumn. |
TableRowDriverBase | shared base | ListComponentDriver-based cell iteration (getCellCount/getCell/getCellTexts) shared by both row kinds above. |
TableCellDriver | TableCell | Plain native <td>, no role; relies on inherited getText(). getActionButtons()/isActionsVisible() read a nested TableCellActions (shared logic with DataGridCellDriver) β see Wave 6 scope decisions. |
TableHeaderCellDriver | TableHeaderCell | Plain native <th>; isSortable()/getSortDirection() read aria-sort (absent entirely, not "none", when not sortable). |
DataGridDriver | DataGrid | A ListComponentDriver over data rows (.fui-DataGridBody [role="row"]) with built-in sort, row selection (single/multiselect), and column resize β the richer columns/items-driven sibling of Table. Columns are addressed by zero-based index, not field β columnId has zero DOM reflection (verified: getNativeElementProps's per-tag attribute allowlist strips it from every rendered slot). |
DataGridRowDriver | DataGridRow (data) | A childListHelper-based row over .fui-DataGridCell children (not ListComponentDriver β DataGridRow conditionally renders a leading DataGridSelectionCell only when selectionMode is set, which breaks :nth-of-type addressing). Implements IToggleDriver; setSelected clicks the row's real native checkbox/radio. |
DataGridHeaderRowDriver | DataGridRow (header) | Same childListHelper shape over [role="columnheader"] children; adds sort, "select all" (multiselect only), and per-column resize delegation. |
DataGridRowDriverBase | shared base | childListHelper-based cell iteration shared by both DataGrid row kinds β diverges from TableRowDriverBase for the reason above. |
DataGridCellDriver | DataGridCell | role="gridcell" div; relies on inherited getText(). getActionButtons()/isActionsVisible() read a nested TableCellActions (shared logic with TableCellDriver) β see Wave 6 scope decisions. |
DataGridHeaderCellDriver | DataGridHeaderCell | Extends TableHeaderCellDriver (identical aria-sort surface β renderDataGridHeaderCell_unstable delegates to renderTableHeaderCell_unstable); adds getWidthPx()/resize() for resizableColumns, plus pressResizeKey()/isInKeyboardResizeMode() for the keyboard-accessible resize path β see Wave 6 scope decisions. |
TreeDriver | Tree | Never portals. A ListComponentDriver over this level's TOP-LEVEL TreeItems only β .fui-TreeItem recurs at every nesting level, so the item locator is 'Child'-restricted (a plain descendant match would flatten the whole tree). Recursion into a branch's own children is TreeItemDriver.getChildItems()'s job, not this driver's. isMultiSelect() reads aria-multiselectable (present only for selectionMode="multiselect"; absent, not "false", for "single"/"none"). Covers nested Tree/TreeItem β see FlatTreeDriver below for the flattened, virtualization-friendly variant. |
TreeItemDriver | TreeItem (+ TreeItemLayout) | role="treeitem"; own root, no wrapper. getLabel() reads .fui-TreeItemLayout__main specifically β the inherited whole-row getText() would include an expanded branch's descendant labels too (same class of hazard as CompoundButtonDriver). isBranch()/isLeaf() read aria-expanded's PRESENCE (itemType is consumer-declared, not inferred from children). isSelected() reads aria-selected (single) or aria-checked (multiselect) β never both, neither under "none". select()/setSelected() click the item's own real-but-aria-hidden selector input directly, not the row β verified this does NOT also toggle expansion, unlike component-driver-mui-x-v9's SimpleTreeViewDriver. setSelected(false) rejected in single-select mode (native radio semantics), same contract as RadioDriver. getValue() reads the DOM-reflected data-fui-tree-item-value. Does not implement IDisableableDriver β TreeItem has no disabled prop at all. getChildItemCount()/getChildItemByIndex()/getChildItems() recurse one level down and deliberately do NOT auto-expand a collapsed branch β call expand() first. |
FlatTreeDriver | FlatTree | Never portals. FlatTreeItems never nest in the DOM β DOM audit (@fluentui/react-tree@9.16.3): every visible item, at any aria-level, is a direct 'Child' of this same root, so the inherited ListComponentDriver reads (getItemCount/getItemByIndex/getItems/getItemByLabel) already answer "every item, any level" with no override, unlike TreeDriver's deliberate top-level-only restriction. Adds getItemByValue() β a flat dataset's natural lookup key, since visible labels commonly repeat across branches. |
FlatTreeItemDriver | FlatTreeItem (+ TreeItemLayout) | A bare re-export of TreeItem upstream (export const FlatTreeItem = TreeItem;), so it shares TreeItemDriver's locators and most read/selection logic (getLabel/isBranch/isLeaf/isExpanded/isSelected/setSelected/select/getValue/click). Adds getLevel()/getPosInSet()/getSetSize() from the required aria-level/aria-posinset/aria-setsize triad (FlatTreeItem warns in dev mode if any is missing). No getChildItem* recursion β a FlatTreeItem's "children" are other flat-list entries with a higher level, not a DOM descendant relationship (parentValue is a React-only prop; source audit confirms it never reaches the DOM), matching component-driver-mui-x-v9's SimpleTreeViewDriver precedent. expand()/collapse() need no exit-motion wait, unlike TreeItemDriver β there is no per-item subtree to unmount; hiding descendants is the app's own synchronous re-render. |
CarouselDriver | Carousel (+ CarouselViewport/CarouselSlider) | A ListComponentDriver over .fui-CarouselCard children; root has no aria-roledescription (unlike the ARIA APG pattern). getNav() resolves an optional CarouselNavDriver. scrollNext/scrollPrev resolve the prev/next CarouselButtons by DOM position β Fluent reflects neither navType nor a default aria-label β and are E2E-only, like component-driver-astryx's own Carousel, since embla-carousel's snap-group computation needs real layout (see Known gaps). |
CarouselCardDriver | CarouselCard | A slide's content; mostly the inherited getText()/exists() (identity-only, like component-driver-mui-v9's TableCellDriver). isActive() reads the non-visible-card aria-hidden signal β E2E-only, never toggles under jsdom at all. |
CarouselNavDriver | CarouselNav | A ListComponentDriver over role="tab" CarouselNavButtons (same ARIA shape TabListDriver covers). Item count/getActiveIndex/selectByIndex are E2E-only β jsdom collapses every card count to exactly one snap group/nav button. |
CarouselNavButtonDriver | CarouselNavButton | A nav dot; isSelected/select mirror TabDriver's role="tab" contract (not an IToggleDriver β can't deselect). getLabel() reads the consumer-supplied aria-label (Fluent renders no visible text). |
Wave 6 scope decisions (per the umbrella issue's "no silent caps" instruction):
- Column resize IS in scope for
DataGridDriver, despite being implemented internally viauseTableColumnSizing_unstableβ verifiedresizableColumns/columnSizingOptions/onColumnResizeare plain, non-_unstable-suffixed fields on the stableDataGridPropstype (@fluentui/react-table@9.19.17). This differs from Wave 5'sAlertDriver, which is importable ONLY from the/unstablesubpath with no stable entry point at all β an internal_unstableimplementation detail is not the same as an unstable public surface.DataGridHeaderCellDriver.resize()drags the rendered.fui-TableResizeHandle(mouse-based, same techniquecomponent-driver-mui-x-v9'sDataGridPremiumDriver.resizeColumnuses) β E2E-only, since jsdom has no layout engine. The LAST column has no resize handle by default (autoFitColumns: trueabsorbs remaining width) βresize()/resizeColumn()returnfalsethere rather than throwing. - Keyboard-accessible resize (
useKeyboardResizing) is covered bypressResizeKey()/isInKeyboardResizeMode(), but deliberately as LOW-LEVEL primitives only β full source audit of@fluentui/react-table@9.19.17confirms entering keyboard-resize mode (columnSizing_unstable.enableKeyboardMode) has NO default UI trigger anywhere in the library (Fluent's own Storybook wires it via a consumer-added context menu); nothing calls it internally. A driver method cannot invoke an app-specific affordance that doesn't exist in Fluent's own DOM, sopressResizeKey()assumes the resize handle is already focused (via whatever entry point the consuming app wired) and dispatchesArrowLeft/ArrowRight/Enter/Space/Escapeon it directly β verified via a rendered probe that this reachesuseKeyboardResizing's internal handler correctly (confirmed throughonColumnResizefiring with the expected delta). Like the mouse-drag path, the resulting width change is E2E-only under jsdom. - Cell editing is NOT a Fluent
DataGridfeature at all β confirmed@fluentui/react-table@9.19.17ships no editing capability upstream (unlike MUI-X Premium'sstartCellEdit/commitCellEdit); not a driver gap. TableCellActions(getActionButtons()/isActionsVisible()onTableCellDriver/DataGridCellDriver) ships with NO default hover/focus CSS of its own either β DOM/source audit ofuseTableCellActionsStyles_unstableshows visibility is 100% driven by the component's ownvisibleboolean prop, which the consuming app must wire from whatever hover/focus state it tracks.isActionsVisible()reads the actual computedopacityrather than matching a Griffel-hashed class name, so it reflects real visibility however the app wired it.- No Fluent-native pagination component exists alongside
Table/DataGridβ pagination is entirely a consumer concern; out of scope, not a gap (noTablePaginationDriverequivalent). - No built-in filtering, row grouping, virtualization, or column pinning on
DataGridβ Fluent's ownDataGridships none of these (MUI-X Pro/Premium-only features); not driver gaps. Tree/TreeItemandFlatTree/FlatTreeItemare both covered β the latter landed as a follow-up (issue #1138) once its own DOM audit confirmeduseHeadlessFlatTree_unstable(still_unstable-suffixed as of@fluentui/react-tree@9.16.3) produces a structurally flat, non-nesting DOM needing its own driver pair (FlatTreeDriver/FlatTreeItemDriver) rather than reusingTreeDriver/TreeItemDriver's recursive shape.- Only
Carousel/CarouselCard/CarouselNav(+CarouselNavButton) are covered;CarouselAutoplayButtonis out of scope (not named in the umbrella issue).
Known gaps (Wave 1)β
CompoundButtonDriver.getSecondaryContent()returns the secondary line, but there is no way to read only the primary label β the two render as adjacent text nodes in one container, and CSS/textContentcannot exclude nested content from an ancestor read.
Known gaps (Wave 2)β
- No portable
closeByBackdropClickforDialog/OverlayDrawer: their backdrops are separatedocument.bodysiblings of the surface (not descendants), with no id/data-* link back to a specific instance β a backdrop click cannot be scoped to one dialog when more than one modal is open. OnlycloseByEscapeis offered. Escapedismisses the topmost stacked overlay, not a specific targeted one β see the portal & overlay recipe above. Applies toDialog,Popover,Menu, andOverlayDrawer.TooltipDriver's"label"-relationship fallback is best-effort with multiple tooltips mounted:isOpen/open/dismiss/waitForOpen/waitForClosefall back to matching anyrole="tooltip"on the page when the trigger carries noaria-describedbylink (the defaultrelationship="label"mode) β verified against real Chromium, with two tooltips mounted the portalled contents do not land in the DOM in trigger-declaration order, so this fallback can resolve to the wrong trigger's content.getContent()is unaffected (it reads the trigger's ownaria-labeldirectly).relationship="description"tooltips do not have this problem β theiraria-describedbylink always resolves the exact instance.OverlayDrawer'sdefaultOpenis deprecated and non-functional (a Fluent-side limitation, not a driver one) β drive it via the controlledopenprop.
Known gaps (Wave 3)β
- No
selectByValue/getOptionValuesonComboboxDriverβ Fluent never reflectsOption'svalueprop to the DOM (only the visible label renders);selectByLabel/getOptionByLabelonly. multiselectmode is out of scope forComboboxDriverandDropdownDriverβ both swap every option's role/state model entirely (menuitemcheckbox/aria-checkedinstead ofoption/aria-selected), which these drivers' selectors don't match.DropdownDriver.getSelectedLabel()cannot distinguish a placeholder from a genuinely-selected option sharing its label β both render through the identical trigger text node while closed, with no other DOM signal available.SpinButtonDriver.setValue()is not clamped tomin/maxβ Fluent commits an out-of-range typed value verbatim; the returned boolean reflects "did the exact value land," not "was it within bounds."RatingDriver/SwatchPickerItemDriverreject clearing a selection β native radio-group semantics (Rating's hidden radios; SwatchPicker'sradiogroup/grid) offer no click path back to "unselected," mirroringRadioDriver's existingsetSelected(false)rejection.RatingDisplayDriver.getMax()is exact only in the default display mode βcompactmode always renders a single icon regardless ofmax, so a compact instance's true max isn't observable in the DOM.- No
getValue/selectByValueonSwatchPickerDriver/SwatchPickerItemDriverβColorSwatch.value/SwatchPicker.selectedValuehave zero DOM reflection; color-based equivalents (getColor/selectByColor) are exposed instead. TagDriverdoes not implementIDisableableDriverβ a staticTag'sdisabledprop has no DOM reflection at all (its root is a plain<span>, not a button);InteractionTagDriver/TagGroupDriverremain reliable since their roots reflect it natively.TagPickerOptionDriverhas no portableisSelectedβ neither an open-list option nor a selected tag exposes a computed selection signal under Fluent's idiomatic (filter-selected-out) usage pattern.- No
TagPickerOptionGroupsupport onTagPickerDriverβ only a flat, ungrouped option list is enumerated. - No alpha-channel slider on
ColorPickerDriverβ@fluentui/react-color-pickerships no alphachannelin this version; an upstream library gap, not a driver omission. - No keyboard/drag write path on
ColorAreaDriver/ColorSliderDriver/SwatchPickerDriverβsetRangeValue/click already give an exact, cross-environment-verified write, so nudge/drag primitives were left out as redundant surface rather than a missing capability.
Known gaps (Wave 4)β
AccordionItemDriver.collapse()is a no-op unless the parentAccordionsetscollapsibleβ verified against Fluent's ownupdateOpenItemssource: with the defaultcollapsible={false}, clicking the header of the single currently-open item (in single-select mode, or the last remaining open item inmultiplemode) is silently ignored, since Fluent refuses to ever reach zero open items otherwise.expand()is unaffected.- No
BreadcrumbDividerDriverβBreadcrumbDivideris purely decorative (aria-hidden, no interactive state); out of scope per the wave-level "interactive units only" rule (see above the Wave 4 table). NavDrawerDriveronly covers the default portal-backed variant βtype="inline"switchesNavDrawer's underlying element to a non-portalInlineDrawer, which this driver's class-based re-root does not resolve (the same asymmetryOverlayDrawerDriver/InlineDrawerDriverare split into two classes over elsewhere in this package).- No portable
isDisabled()onNavItemDriver/NavCategoryItemDriverβ Fluent's@fluentui/react-navtypes expose no confirmeddisabledprop/DOM reflection for these components to verify against. OverflowDriverassumes the idiomatic flat-row usage β everyOverflowItem's underlying element must be a DIRECT child of the container the scene locator targets; a consumer nesting items inside extra wrapper elements needs a different container locator.- Real overflow computation (
isOverflowing(), which items land behind the "+N" trigger) needs actual layout measurement (@fluentui/priority-overflow'sResizeObserver-driven algorithm), unavailable under jsdom (confirmed: it degrades gracefully there β noResizeObserverglobal logs a console error and no-ops, it does not throw β but produces no meaningful overflow state). This package's shared dom+e2e suite pattern runs identical assertions in both environments, soOverflowDriver's own test coverage is limited to layout-independent behavior (item registration, labels, count, the menu's structural composition); genuine overflow-triggering behavior needs a dedicated real-browser scenario outside this package's shared-suite pattern to verify meaningfully. @fluentui/react-nav(Nav/NavDrawer) is explicitly pre-release upstream β per the package's own README ("not production-ready... APIs might change before final release");NavDriver/NavDrawerDriver/NavItemDriver/NavCategoryItemDrivertarget the current shipped shape and may need to track a future breaking change.
Known gaps (Wave 5)β
AvatarGroupDriverdoes not enumerate overflowed items β only the inlineAvatarGroupItems are covered; items pushed into theAvatarGroupPopover's+Ntrigger are out of scope for this wave.PresenceBadgeDriver.getStatusLabel()'soutOfOfficecomposite label text is unverified β confirmed against real DOM only for a plainstatus(where thearia-labelis exactly the status string); the exact label Fluent renders whenoutOfOfficeis also set was not probed, so this driver exposes the rawaria-labelrather than a parsed, typed result.- No
getValue/read path forCard'sselectedbeyond the booleanisSelected()β matches the component's own API surface (no intermediate/tri-state selection exists). MessageBarDriver.getBodyText()includes the title's text βMessageBarTitlenests INSIDEMessageBarBodyrather than beside it, so there is no CSS/textContentread that excludes it; usegetTitle()for the title alone. Same class of limitation asCompoundButtonDriver.getSecondaryContent().AlertDriver.getText()includes theactionbutton's text when one is present β the message renders as a bare text node alongside the icon/action as DOM siblings, with no wrapping element around the message alone to read in isolation.InfoButtonDriver/InfoLabelDriver.getInfoButton()only cover the defaultinline={true}popover βinline={false}switchesInfoButtonto the standard portal-to-document.bodybehavior used elsewhere in this package (Wave 2), which this driver does not resolve.ListItemDriver.isSelected()is only meaningful when the parentListhas aselectionModeβaria-selectedis entirely absent (not"false") otherwise, and this driver treats that absence the same as "not selected".SkeletonDriverexposes no per-item driver β individualSkeletonItems carry no distinguishable state (no reflectedshape/animationprop, no text) beyond their count.
Known gaps (Wave 6)β
DataGridRowDriver.setSelected(false)silently no-ops in single-select mode, unlikeRadioDriver/TreeItemDriver's throw-on-deselect contract β verified against@fluentui/react-utilities'suseSingleSelection.toggleItem, which always resolves a click to "select this row," never "deselect," so a no-op (not an exception) keepsDataGridDriver.deselectRow's bulk-friendly boolean-return contract intact for an otherwise-valid row index.Treeselection is fully controlled upstream β a consumer scene MUST wirecheckedItems/onCheckedChangeforTreeItemDriver.select()/setSelected()to have any visible effect. Source audit of@fluentui/react-tree@9.16.3: unlikeopenItems(which has a genuine uncontrolled fallback),checkedItemsis derived only from thecheckedItemsprop with no internal state and nodefaultCheckedItemson the base<Tree>β a bareselectionMode="single"/"multiselect"Treewith nothing wired never firesonCheckedChangeand stays silently unselectable no matter what's clicked or key-pressed. Not a driver limitation; seeTree.examples.tsxfor the required controlled-state pattern.TreeItemDriverdoes not implementIDisableableDriverβTreeItemhas zerodisabledsupport (confirmed absent from the compiledTreeItemPropstype declarations), unlikeAccordionItem.TreeDriver.getItemByLabel()(inherited fromListComponentDriver) compares againstgetText(), which for a currently-expanded branch includes its descendant items' labels too (same class of limitation asCompoundButtonDriver/MessageBarDriver) β prefergetItems()+TreeItemDriver.getLabel()for a reliable, scoped comparison.TreeItemDriver.getChildItemCount()/getChildItemByIndex()/getChildItems()never auto-expand a collapsed branch β a deliberate departure fromNavCategoryItemDriver.getSubItemCount()'s auto-expand, to keep these reads free of UI side effects; callexpand()first.TreeItemPersonaLayoutis unaudited βTreeItemDriver.getLabel()(andFlatTreeItemDriver.getLabel(), which shares the same locator) assumesTreeItemLayout's.fui-TreeItemLayout__mainstructural class; aTreeItemPersonaLayout-based item's label read is unverified.FlatTreeItemDriverexposes no descendant-navigation method (nogetChildItem*, unlikeTreeItemDriver) β a flat item's hierarchy is knowable only fromgetLevel()/getPosInSet()/getSetSize(); the DOM carries no parent/child relationship to walk (parentValueis consumed purely as a React prop upstream and never reaches the DOM β confirmed via source audit ofgetIntrinsicElementProps/getNativeElementProps's attribute allowlist). A caller wanting a specific descendant looks it up byvalueviaFlatTreeDriver.getItemByValue()instead.FlatTreeselection has the identical fully-controlled-upstream caveat asTree(see above) βuseHeadlessFlatTree_unstable's options DO support real uncontrolledcheckedItemsviadefaultCheckedItems(unlike the bare<Tree checkedItems>prop), so this only bites a scene that skips the headless hook and hand-assembles<FlatTree>props itself.Carouselcannot mount under jsdom without extra polyfills βembla-carousel@8.6.0(the engine@fluentui/react-carousel@9.9.10wraps) unconditionally callswindow.matchMediaand constructs anIntersectionObserverduring its first activation effect, which jsdom implements neither of βpackage-tests/component-driver-fluent-v9-test/jest.setup.tsinstalls guarded, inert stubs for both, alongside the package's existingResizeObserverstub.- Real navigation/active-slide state on
Carouselis E2E-only, not merely imprecise, under jsdom β even with the mount-blocking polyfills in place,CarouselNavstill renders exactly ONECarouselNavButtonregardless of the real card count, and both prev/nextCarouselButtons stay permanentlydisabled.CarouselNavDriver's item surface,CarouselDriver.scrollNext/scrollPrev, andCarouselCardDriver.isActive()are all E2E-only for this reason. - No default accessible name on
CarouselButton, and itsnavTypeprop is not reflected to the DOM at all βscrollPrev/scrollNextresolve the pair by DOM position (first = prev, last = next) among descendant.fui-CarouselButtonelements; a consumer rendering only one such button, or in reverse order, is not resolved correctly (same class of assumption asOverflowDriver's documented flat-row usage). CarouselNavImageButton(an alternate image-thumbnail nav button, alsorole="tab") has no dedicated example/test coverage this wave, thoughCarouselNavButtonDriver's generic surface should apply to it structurally.
Classesβ
- AccordionDriver
- AccordionItemDriver
- AlertDriver
- AvatarDriver
- AvatarGroupDriver
- AvatarGroupItemDriver
- BadgeDriver
- BreadcrumbButtonDriver
- BreadcrumbDriver
- BreadcrumbItemDriver
- BreadcrumbItemNotFoundError
- ButtonDriver
- CardDriver
- CarouselCardDriver
- CarouselDriver
- CarouselNavButtonDriver
- CarouselNavDriver
- CheckboxDriver
- ColorAreaDriver
- ColorPickerDriver
- ColorSliderDriver
- ComboboxDriver
- ComboboxOptionDriver
- CompoundButtonDriver
- CounterBadgeDriver
- DataGridCellDriver
- DataGridDriver
- DataGridHeaderCellDriver
- DataGridHeaderRowDriver
- DataGridRowDriver
- DataGridRowDriverBase
- DialogDriver
- DividerDriver
- DrawerDriverBase
- DropdownDriver
- DropdownOptionDriver
- DropdownOptionNotFoundError
- FieldDriver
- FlatTreeDriver
- FlatTreeItemDriver
- ImageDriver
- InfoButtonDriver
- InfoLabelDriver
- InlineDrawerDriver
- InputDriver
- InteractionTagDriver
- LabelDriver
- LinkDriver
- ListDriver
- ListItemDriver
- MenuButtonDriver
- MenuDriver
- MenuItemCheckboxDriver
- MenuItemDriver
- MenuItemNotFoundError
- MenuItemRadioDriver
- MessageBarDriver
- NavCategoryItemDriver
- NavDrawerDriver
- NavDriver
- NavDriverBase
- NavItemDriver
- NavItemNotFoundError
- OverflowDriver
- OverflowItemDriver
- OverlayDrawerDriver
- PersonaDriver
- PopoverDriver
- PresenceBadgeDriver
- ProgressBarDriver
- RadioDriver
- RadioGroupDriver
- RatingDisplayDriver
- RatingDriver
- SearchBoxDriver
- SelectDriver
- SkeletonDriver
- SliderDriver
- SpinButtonDriver
- SpinnerDriver
- SplitButtonDriver
- SwatchNotFoundError
- SwatchPickerDriver
- SwatchPickerItemDriver
- SwitchDriver
- TabDriver
- TableCellDriver
- TableDriver
- TableHeaderCellDriver
- TableHeaderRowDriver
- TableRowDriver
- TableRowDriverBase
- TabListDriver
- TagDriver
- TagGroupDriver
- TagPickerDriver
- TagPickerOptionDriver
- TagPickerOptionNotFoundError
- TeachingPopoverDriver
- TextareaDriver
- TextDriver
- ToastDriver
- ToasterDriver
- ToggleButtonDriver
- ToolbarButtonDriver
- ToolbarDividerDriver
- ToolbarDriver
- ToolbarRadioButtonDriver
- ToolbarRadioGroupDriver
- TooltipDriver
- TreeDriver
- TreeItemDriver