diff --git a/change/@fluentui-react-breadcrumb-78372ea0-823d-4939-b9f5-600d215feb68.json b/change/@fluentui-react-breadcrumb-78372ea0-823d-4939-b9f5-600d215feb68.json new file mode 100644 index 00000000000000..07a3955938b1b2 --- /dev/null +++ b/change/@fluentui-react-breadcrumb-78372ea0-823d-4939-b9f5-600d215feb68.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: make BreadcrumbButtonBaseProps distribute over the ARIA button union so the anchor arm's href stays assignable", + "packageName": "@fluentui/react-breadcrumb", + "email": "array.knight@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md b/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md index 21b597d5eb78d6..e16f1f764e21fa 100644 --- a/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md +++ b/packages/react-components/react-breadcrumb/library/etc/react-breadcrumb.api.md @@ -9,6 +9,7 @@ import type { ButtonSlots } from '@fluentui/react-button'; import type { ButtonState } from '@fluentui/react-button'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; +import type { DistributiveOmit } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import * as React_2 from 'react'; @@ -29,7 +30,7 @@ export type BreadcrumbBaseState = Omit; export const BreadcrumbButton: ForwardRefComponent; // @public (undocumented) -export type BreadcrumbButtonBaseProps = Omit; +export type BreadcrumbButtonBaseProps = DistributiveOmit; // @public (undocumented) export type BreadcrumbButtonBaseState = Omit; diff --git a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.test.tsx b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.test.tsx index 6e39e8274ceac9..1fdfad49551d75 100644 --- a/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.test.tsx +++ b/packages/react-components/react-breadcrumb/library/src/components/BreadcrumbButton/BreadcrumbButton.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; -import { render } from '@testing-library/react'; +import { render, renderHook } from '@testing-library/react'; import { BreadcrumbButton } from './BreadcrumbButton'; +import { useBreadcrumbButtonBase_unstable } from './useBreadcrumbButton'; import type { BreadcrumbButtonProps } from './BreadcrumbButton.types'; import { isConformant } from '../../testing/isConformant'; import { breadcrumbButtonClassNames } from './useBreadcrumbButtonStyles.styles'; @@ -67,4 +68,32 @@ describe('BreadcrumbButton', () => { `); }); + + // Regression test for https://github.com/microsoft/fluentui/issues/36645: a plain `Omit` + // collapsed the distributive ARIA button union, so the anchor arm's `href` was not assignable. + // Type-check runs against tests, so this covers the base hook and the types together. + it('accepts the anchor arm of the ARIA button union in the base hook', () => { + const { result } = renderHook(() => + useBreadcrumbButtonBase_unstable({ as: 'a', href: '/somewhere' }, React.createRef()), + ); + + // `components.root` stays 'button' (the slot's declared default); the anchor arm resolves + // through the slot props, where useARIAButtonProps carries `as: 'a'` to the render layer. + expect(result.current).toMatchObject({ + root: { as: 'a', href: '/somewhere' }, + }); + }); + + // Regression test for the controlType operator-precedence bug: `as ?? href ? 'a' : 'button'` + // parses as `(as ?? href) ? 'a' : 'button'`, so an explicit `as: 'button'` (truthy) computed + // controlType 'a' and the ARIA button pipeline emitted the anchor arm (`as: 'a'`, role="button") + // for a caller who asked for a real