diff --git a/change/@fluentui-react-headless-components-preview-c00fc5f6-dee8-494a-a4e9-61e4e74ae664.json b/change/@fluentui-react-headless-components-preview-c00fc5f6-dee8-494a-a4e9-61e4e74ae664.json new file mode 100644 index 00000000000000..08da26aa9f7e62 --- /dev/null +++ b/change/@fluentui-react-headless-components-preview-c00fc5f6-dee8-494a-a4e9-61e4e74ae664.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: expose Dropdown invalid and clear button visibility data attributes", + "packageName": "@fluentui/react-headless-components-preview", + "email": "vgenaev@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-headless-components-preview-focusgroup-typings.json b/change/@fluentui-react-headless-components-preview-focusgroup-typings.json new file mode 100644 index 00000000000000..c83d0b4ca4bb80 --- /dev/null +++ b/change/@fluentui-react-headless-components-preview-focusgroup-typings.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "feat: add development-time types for focusgroup attributes", + "packageName": "@fluentui/react-headless-components-preview", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-headless-components-preview/library/etc/dropdown.api.md b/packages/react-components/react-headless-components-preview/library/etc/dropdown.api.md index 18c863047f9a36..dab6fb863f9189 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/dropdown.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/dropdown.api.md @@ -32,10 +32,14 @@ export type DropdownProps = Omit; -// @public (undocumented) -export type MenuListProps = MenuListProps_2; +export { MenuListProps } export { MenuListSlots } -// @public (undocumented) -export type MenuListState = MenuListState_2 & { - root: { - focusgroup?: string; - }; -}; +export { MenuListState } export { MenuOpenChangeData } @@ -234,7 +227,7 @@ export { renderMenuItemRadio } export { renderMenuItemSwitch } // @public (undocumented) -export const renderMenuList: (state: MenuListState_2, contextValues: MenuListContextValues) => JSXElement; +export const renderMenuList: (state: MenuListState, contextValues: MenuListContextValues) => JSXElement; // @public (undocumented) export const renderMenuPopover: (state: MenuPopoverState) => JSXElement; diff --git a/packages/react-components/react-headless-components-preview/library/etc/nav.api.md b/packages/react-components/react-headless-components-preview/library/etc/nav.api.md index c5b42faefa493a..382804a821c541 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/nav.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/nav.api.md @@ -9,7 +9,6 @@ import type { ComponentState } from '@fluentui/react-utilities'; import type { DividerBaseProps } from '@fluentui/react-divider'; import { DividerBaseState } from '@fluentui/react-divider'; import type { DividerSlots as DividerSlots_2 } from '@fluentui/react-divider'; -import { DrawerBodyState } from '@fluentui/react-drawer'; import { DrawerFooterState } from '@fluentui/react-drawer'; import { DrawerHeaderState } from '@fluentui/react-drawer'; import type { EventHandler } from '@fluentui/react-utilities'; @@ -17,7 +16,6 @@ import type { ForwardRefComponent } from '@fluentui/react-utilities'; import { InlineDrawerBaseProps } from '@fluentui/react-drawer'; import { InlineDrawerSlots } from '@fluentui/react-drawer'; import { JSXElement } from '@fluentui/react-utilities'; -import type { NavBaseState } from '@fluentui/react-nav'; import { NavCategoryContextValues } from '@fluentui/react-nav'; import { NavCategoryItemProvider } from '@fluentui/react-nav'; import { NavCategoryProps } from '@fluentui/react-nav'; @@ -27,6 +25,7 @@ import { NavContextValue } from '@fluentui/react-nav'; import { NavContextValues } from '@fluentui/react-nav'; import { DrawerBodyProps as NavDrawerBodyProps } from '@fluentui/react-drawer'; import { DrawerBodySlots as NavDrawerBodySlots } from '@fluentui/react-drawer'; +import { DrawerBodyState as NavDrawerBodyState } from '@fluentui/react-drawer'; import { DrawerFooterProps as NavDrawerFooterProps } from '@fluentui/react-drawer'; import { DrawerFooterSlots as NavDrawerFooterSlots } from '@fluentui/react-drawer'; import { DrawerHeaderProps as NavDrawerHeaderProps } from '@fluentui/react-drawer'; @@ -39,6 +38,7 @@ import { NavItemValue } from '@fluentui/react-nav'; import { NavBaseProps as NavProps } from '@fluentui/react-nav'; import { NavProvider } from '@fluentui/react-nav'; import { NavSlots } from '@fluentui/react-nav'; +import { NavBaseState as NavState } from '@fluentui/react-nav'; import type { NavSubItemBaseState } from '@fluentui/react-nav'; import { NavSubItemBaseProps as NavSubItemProps } from '@fluentui/react-nav'; import { NavSubItemSlots } from '@fluentui/react-nav'; @@ -135,12 +135,7 @@ export { NavDrawerBodyProps } export { NavDrawerBodySlots } -// @public (undocumented) -export type NavDrawerBodyState = DrawerBodyState & { - root: DrawerBodyState['root'] & { - focusgroup?: string; - }; -}; +export { NavDrawerBodyState } // @public export const NavDrawerFooter: ForwardRefComponent; @@ -215,12 +210,7 @@ export type NavSectionHeaderState = ComponentState; export { NavSlots } -// @public (undocumented) -export type NavState = NavBaseState & { - root: NavBaseState['root'] & { - focusgroup?: string; - }; -}; +export { NavState } // @public export const NavSubItem: ForwardRefComponent; @@ -271,7 +261,7 @@ export const renderNavDivider: (state: DividerBaseState) => JSXElement; export const renderNavDrawer: (state: NavDrawerState, contextValues: NavContextValues) => JSXElement; // @public -export const renderNavDrawerBody: (state: DrawerBodyState) => JSXElement; +export const renderNavDrawerBody: (state: NavDrawerBodyState) => JSXElement; // @public export const renderNavDrawerFooter: (state: DrawerFooterState) => JSXElement; diff --git a/packages/react-components/react-headless-components-preview/library/etc/swatch-picker.api.md b/packages/react-components/react-headless-components-preview/library/etc/swatch-picker.api.md index d4f2f18cc1f36a..81a3004c4c0b8e 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/swatch-picker.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/swatch-picker.api.md @@ -108,7 +108,6 @@ export { SwatchPickerSlots } export type SwatchPickerState = SwatchPickerBaseState & { root: { 'data-layout'?: SwatchPickerBaseState['layout']; - focusgroup?: string; }; }; diff --git a/packages/react-components/react-headless-components-preview/library/etc/tab-list.api.md b/packages/react-components/react-headless-components-preview/library/etc/tab-list.api.md index 2564195bcfb3a5..948c30ea5ee373 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/tab-list.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/tab-list.api.md @@ -36,7 +36,6 @@ export type TabListSlots = TabListSlots_2; // @public export type TabListState = TabListBaseState & { root: { - focusgroup?: string; 'data-orientation'?: 'vertical' | 'horizontal'; }; }; @@ -49,7 +48,6 @@ export { TabSlots } // @public (undocumented) export type TabState = TabBaseState & { root: { - focusgroupstart?: string; 'data-icon-only'?: string; 'data-selected'?: string; 'data-disabled'?: string; diff --git a/packages/react-components/react-headless-components-preview/library/etc/tag-group.api.md b/packages/react-components/react-headless-components-preview/library/etc/tag-group.api.md index 535cd8c50633a1..ce04d9710db54c 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/tag-group.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/tag-group.api.md @@ -29,7 +29,6 @@ export { TagGroupSlots } // @public (undocumented) export type TagGroupState = TagGroupBaseState & { root: { - focusgroup?: string; 'data-disabled'?: string; 'data-dismissible'?: string; }; diff --git a/packages/react-components/react-headless-components-preview/library/etc/tag-picker.api.md b/packages/react-components/react-headless-components-preview/library/etc/tag-picker.api.md index 7a4a44360b804e..b691a104dd2432 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/tag-picker.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/tag-picker.api.md @@ -37,7 +37,7 @@ import { TagPickerControlInternalSlots } from '@fluentui/react-tag-picker'; import { TagPickerControlProps } from '@fluentui/react-tag-picker'; import { TagPickerControlSlots } from '@fluentui/react-tag-picker'; import type { TagPickerGroupBaseState } from '@fluentui/react-tag-picker'; -import type { TagPickerGroupSlots } from '@fluentui/react-tag-picker'; +import type { TagPickerGroupSlots as TagPickerGroupSlots_2 } from '@fluentui/react-tag-picker'; import type { TagPickerInputBaseState } from '@fluentui/react-tag-picker'; import { TagPickerInputBaseProps as TagPickerInputProps } from '@fluentui/react-tag-picker'; import { TagPickerInputSlots } from '@fluentui/react-tag-picker'; @@ -113,12 +113,12 @@ export const TagPickerGroup: ForwardRefComponent; // @public export type TagPickerGroupProps = ComponentProps & Pick; -export { TagPickerGroupSlots } +// @public (undocumented) +export type TagPickerGroupSlots = TagPickerGroupSlots_2; // @public export type TagPickerGroupState = TagPickerGroupBaseState & { root: { - focusgroup?: string; 'data-disabled'?: string; }; }; diff --git a/packages/react-components/react-headless-components-preview/library/etc/toolbar.api.md b/packages/react-components/react-headless-components-preview/library/etc/toolbar.api.md index 3ed290735b0365..5f004981ba0d28 100644 --- a/packages/react-components/react-headless-components-preview/library/etc/toolbar.api.md +++ b/packages/react-components/react-headless-components-preview/library/etc/toolbar.api.md @@ -10,26 +10,26 @@ import { DividerBaseState } from '@fluentui/react-divider'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import { JSXElement } from '@fluentui/react-utilities'; import type * as React_2 from 'react'; -import type { ToolbarBaseProps } from '@fluentui/react-toolbar'; import { ToolbarBaseState } from '@fluentui/react-toolbar'; import type { ToolbarButtonBaseProps } from '@fluentui/react-toolbar'; import type { ToolbarButtonBaseState } from '@fluentui/react-toolbar'; import { ToolbarContextValue } from '@fluentui/react-toolbar'; -import { ToolbarContextValues as ToolbarContextValues_2 } from '@fluentui/react-toolbar'; +import { ToolbarContextValues } from '@fluentui/react-toolbar'; import type { ToolbarDividerBaseProps } from '@fluentui/react-toolbar'; import type { ToolbarDividerBaseState } from '@fluentui/react-toolbar'; import type { ToolbarGroupProps as ToolbarGroupProps_2 } from '@fluentui/react-toolbar'; import { ToolbarGroupState as ToolbarGroupState_2 } from '@fluentui/react-toolbar'; +import { ToolbarBaseProps as ToolbarProps } from '@fluentui/react-toolbar'; import type { ToolbarRadioButtonBaseProps } from '@fluentui/react-toolbar'; import type { ToolbarRadioButtonBaseState } from '@fluentui/react-toolbar'; import type { ToolbarRadioGroupProps as ToolbarRadioGroupProps_2 } from '@fluentui/react-toolbar'; import type { ToolbarRadioGroupState as ToolbarRadioGroupState_2 } from '@fluentui/react-toolbar'; -import type { ToolbarSlots as ToolbarSlots_2 } from '@fluentui/react-toolbar'; +import { ToolbarSlots } from '@fluentui/react-toolbar'; import type { ToolbarToggleButtonBaseProps } from '@fluentui/react-toolbar'; import type { ToolbarToggleButtonBaseState } from '@fluentui/react-toolbar'; // @public -export const renderToolbar: (state: ToolbarBaseState, contextValues: ToolbarContextValues_2) => JSXElement; +export const renderToolbar: (state: ToolbarBaseState, contextValues: ToolbarContextValues) => JSXElement; // @public export const renderToolbarButton: (state: ButtonBaseState) => JSXElement; @@ -68,8 +68,7 @@ export type ToolbarButtonState = ToolbarButtonBaseState & { }; }; -// @public (undocumented) -export type ToolbarContextValues = ToolbarContextValues_2; +export { ToolbarContextValues } // @public export const ToolbarDivider: ForwardRefComponent; @@ -97,8 +96,7 @@ export type ToolbarGroupState = ToolbarGroupState_2 & { }; }; -// @public (undocumented) -export type ToolbarProps = ToolbarBaseProps; +export { ToolbarProps } // @public export const ToolbarRadioButton: ForwardRefComponent; @@ -130,14 +128,12 @@ export type ToolbarRadioGroupState = ToolbarRadioGroupState_2 & { }; }; -// @public (undocumented) -export type ToolbarSlots = ToolbarSlots_2; +export { ToolbarSlots } // @public (undocumented) export type ToolbarState = ToolbarBaseState & { root: { 'data-vertical'?: string; - focusgroup?: string; }; }; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.types.ts b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.types.ts index 174713f82128e0..a1466f752bc5cf 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.types.ts +++ b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.types.ts @@ -10,7 +10,7 @@ export type { export type DropdownProps = Omit; export type DropdownState = DropdownBaseHookState & { - button: { + button: DropdownBaseHookState['button'] & { /** * Whether the dropdown is currently open. */ @@ -23,5 +23,18 @@ export type DropdownState = DropdownBaseHookState & { * Whether the trigger element is currently displaying a placeholder. */ 'data-placeholder'?: string; + /** + * Whether the trigger element is currently invalid. + */ + 'data-invalid'?: string; + }; + /** + * The resolved clear button slot state. + */ + clearButton?: DropdownBaseHookState['clearButton'] & { + /** + * Whether the clear button is currently visible. + */ + 'data-visible'?: string; }; }; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/index.ts b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/index.ts index a20079f0496b08..bf6e2ddcfb60aa 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/index.ts +++ b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/index.ts @@ -1,5 +1,5 @@ export { Dropdown } from './Dropdown'; -export type { DropdownProps, DropdownState } from './Dropdown.types'; +export type { DropdownProps, DropdownState, DropdownSlots } from './Dropdown.types'; export { renderDropdown } from './renderDropdown'; export { useDropdown } from './useDropdown'; export { useDropdownContextValues } from './useDropdownContextValues'; diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdown.ts b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdown.ts index 619ef256534ef9..79a41eca595931 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdown.ts +++ b/packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdown.ts @@ -60,6 +60,7 @@ export const useDropdown = (props: DropdownProps, ref: React.Ref & */ export type TagPickerGroupState = TagPickerGroupBaseState & { root: { - /** - * Native WICG `focusgroup` attribute for arrow-key navigation across the selected tags. - * Replaces the Tabster `useArrowNavigationGroup` used by the styled TagPickerGroup. - */ - focusgroup?: string; /** * Data attribute set when the group is disabled. */ diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Toolbar/Toolbar.types.ts b/packages/react-components/react-headless-components-preview/library/src/components/Toolbar/Toolbar.types.ts index 8986de93a7148c..1ceb56460e794c 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Toolbar/Toolbar.types.ts +++ b/packages/react-components/react-headless-components-preview/library/src/components/Toolbar/Toolbar.types.ts @@ -1,13 +1,6 @@ -import type { - ToolbarSlots as ToolbarBaseSlots, - ToolbarBaseProps, - ToolbarContextValues as ToolbarBaseContextValues, - ToolbarBaseState, -} from '@fluentui/react-toolbar'; +import type { ToolbarBaseState } from '@fluentui/react-toolbar'; -export type ToolbarSlots = ToolbarBaseSlots; - -export type ToolbarProps = ToolbarBaseProps; +export type { ToolbarContextValues, ToolbarSlots, ToolbarBaseProps as ToolbarProps } from '@fluentui/react-toolbar'; export type ToolbarState = ToolbarBaseState & { root: { @@ -15,12 +8,5 @@ export type ToolbarState = ToolbarBaseState & { * Data attribute set when the toolbar is vertically oriented. */ 'data-vertical'?: string; - - /** - * Data attribute to define the focus behavior of the toolbar's children - */ - focusgroup?: string; }; }; - -export type ToolbarContextValues = ToolbarBaseContextValues; diff --git a/packages/react-components/react-headless-components-preview/library/tsconfig.cy.json b/packages/react-components/react-headless-components-preview/library/tsconfig.cy.json index 3cfb39a1086ac0..1c0a3781330fe8 100644 --- a/packages/react-components/react-headless-components-preview/library/tsconfig.cy.json +++ b/packages/react-components/react-headless-components-preview/library/tsconfig.cy.json @@ -2,8 +2,8 @@ "extends": "./tsconfig.json", "compilerOptions": { "isolatedModules": false, - "types": ["node", "cypress", "cypress-real-events"], - "typeRoots": ["../../../../node_modules", "../../../../node_modules/@types"], + "types": ["node", "cypress", "cypress-real-events", "focusgroup"], + "typeRoots": ["../../../../node_modules", "../../../../node_modules/@types", "../../../../typings"], "lib": ["ES2019", "dom"] }, "include": ["**/*.cy.ts", "**/*.cy.tsx"] diff --git a/packages/react-components/react-headless-components-preview/library/tsconfig.lib.json b/packages/react-components/react-headless-components-preview/library/tsconfig.lib.json index 9832fd3ff7df46..780872b2c87ec1 100644 --- a/packages/react-components/react-headless-components-preview/library/tsconfig.lib.json +++ b/packages/react-components/react-headless-components-preview/library/tsconfig.lib.json @@ -7,7 +7,7 @@ "declarationDir": "../../../../dist/out-tsc/types", "outDir": "../../../../dist/out-tsc", "inlineSources": true, - "types": ["static-assets", "environment"] + "types": ["static-assets", "environment", "focusgroup"] }, "exclude": [ "./src/testing/**", diff --git a/packages/react-components/react-headless-components-preview/library/tsconfig.spec.json b/packages/react-components/react-headless-components-preview/library/tsconfig.spec.json index 0e881941843de8..4e0de2559eeea6 100644 --- a/packages/react-components/react-headless-components-preview/library/tsconfig.spec.json +++ b/packages/react-components/react-headless-components-preview/library/tsconfig.spec.json @@ -3,7 +3,7 @@ "compilerOptions": { "module": "CommonJS", "outDir": "dist", - "types": ["jest", "node", "@testing-library/jest-dom"] + "types": ["jest", "node", "@testing-library/jest-dom", "focusgroup"] }, "include": [ "**/*.spec.ts", diff --git a/packages/react-components/react-headless-components-preview/stories/tsconfig.lib.json b/packages/react-components/react-headless-components-preview/stories/tsconfig.lib.json index a24e9b9b7eca50..2a5b72b5004d33 100644 --- a/packages/react-components/react-headless-components-preview/stories/tsconfig.lib.json +++ b/packages/react-components/react-headless-components-preview/stories/tsconfig.lib.json @@ -5,7 +5,7 @@ "moduleResolution": "bundler", "outDir": "../../../../dist/out-tsc", "inlineSources": true, - "types": ["static-assets", "environment"] + "types": ["static-assets", "environment", "focusgroup"] }, "include": ["./src/**/*.ts", "./src/**/*.tsx"] } diff --git a/typings/focusgroup/README.md b/typings/focusgroup/README.md new file mode 100644 index 00000000000000..5709f02c05213f --- /dev/null +++ b/typings/focusgroup/README.md @@ -0,0 +1,73 @@ +# focusgroup + +Adds a typed `focusgroup` attribute to React's `HTMLAttributes` for components that use the experimental [Open UI Focusgroup V2 proposal](https://open-ui.org/components/focusgroup-v2.explainer/). + +This typing is intended for development-time validation only. It does not provide focusgroup behavior or become part of a package's public API. + +## Usage + +Opt into the typing from the consuming project's TypeScript configuration: + +```json +{ + "compilerOptions": { + "types": ["focusgroup"] + } +} +``` + +The repository's root `typeRoots` already includes `typings`. A project that overrides `typeRoots` must also include the repository typings directory. + +React elements then accept supported focusgroup values: + +```tsx +
; +
; +
; +
; +