diff --git a/change/@fluentui-react-dialog-492b7510-f518-4c17-a929-c500874ec6fc.json b/change/@fluentui-react-dialog-492b7510-f518-4c17-a929-c500874ec6fc.json new file mode 100644 index 00000000000000..974e4344916dc4 --- /dev/null +++ b/change/@fluentui-react-dialog-492b7510-f518-4c17-a929-c500874ec6fc.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: adds disableButtonEnhancement property on DialogTrigger", + "packageName": "@fluentui/react-dialog", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-menu-2a082257-9573-43fe-99e1-a4ce48003c46.json b/change/@fluentui-react-menu-2a082257-9573-43fe-99e1-a4ce48003c46.json new file mode 100644 index 00000000000000..b566629623846d --- /dev/null +++ b/change/@fluentui-react-menu-2a082257-9573-43fe-99e1-a4ce48003c46.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: adds disableButtonEnhancement property on MenuTrigger", + "packageName": "@fluentui/react-menu", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-popover-2a257879-7e46-4b0d-82f2-9dd83e7fcd36.json b/change/@fluentui-react-popover-2a257879-7e46-4b0d-82f2-9dd83e7fcd36.json new file mode 100644 index 00000000000000..5dae6fc8f6e9db --- /dev/null +++ b/change/@fluentui-react-popover-2a257879-7e46-4b0d-82f2-9dd83e7fcd36.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: adds disableButtonEnhancement property on PopoverTrigger", + "packageName": "@fluentui/react-popover", + "email": "bernardo.sunderhus@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-dialog/etc/react-dialog.api.md b/packages/react-components/react-dialog/etc/react-dialog.api.md index a8259f28f43acf..1d4dbc5ba5fde4 100644 --- a/packages/react-components/react-dialog/etc/react-dialog.api.md +++ b/packages/react-components/react-dialog/etc/react-dialog.api.md @@ -176,6 +176,7 @@ export type DialogTriggerChildProps & { action?: DialogTriggerAction; + disableButtonEnhancement?: boolean; }; // @public (undocumented) diff --git a/packages/react-components/react-dialog/src/components/DialogTitle/useDialogTitle.tsx b/packages/react-components/react-dialog/src/components/DialogTitle/useDialogTitle.tsx index cff1d3504c7d9a..23b55b8a9e60e6 100644 --- a/packages/react-components/react-dialog/src/components/DialogTitle/useDialogTitle.tsx +++ b/packages/react-components/react-dialog/src/components/DialogTitle/useDialogTitle.tsx @@ -35,7 +35,7 @@ export const useDialogTitle_unstable = (props: DialogTitleProps, ref: React.Ref< required: modalType === 'non-modal', defaultProps: { children: ( - + , ); @@ -46,7 +46,7 @@ describe('DialogTrigger', () => { // Arrange const ref = jest.fn(); render( - + , ); @@ -58,7 +58,6 @@ describe('DialogTrigger', () => { , @@ -75,7 +74,7 @@ describe('DialogTrigger', () => { cb(ref.current); }, []); return ( - + ); @@ -89,7 +88,6 @@ describe('DialogTrigger', () => { , @@ -116,7 +114,7 @@ describe('DialogTrigger', () => { mockUseDialogContext({ requestOpenChange }); const { getByRole } = render( - + , ); @@ -131,7 +129,7 @@ describe('DialogTrigger', () => { mockUseDialogContext({ requestOpenChange }); const { getByRole } = render( - + , ); @@ -143,7 +141,7 @@ describe('DialogTrigger', () => { it('should not keyboard click when event is default prevented', () => { const onClick = jest.fn(); const { getByRole } = render( - +
trigger
diff --git a/packages/react-components/react-dialog/src/components/DialogTrigger/DialogTrigger.types.ts b/packages/react-components/react-dialog/src/components/DialogTrigger/DialogTrigger.types.ts index c6417da97e9fe7..8b7d1254bdc410 100644 --- a/packages/react-components/react-dialog/src/components/DialogTrigger/DialogTrigger.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogTrigger/DialogTrigger.types.ts @@ -14,6 +14,11 @@ export type DialogTriggerProps = TriggerProps & { * If `DialogTrigger` is inside `DialogSurface` then it'll be `close` by default */ action?: DialogTriggerAction; + /** + * Disables internal trigger mechanism that ensures a child provided will be a compliant ARIA button. + * @default false + */ + disableButtonEnhancement?: boolean; }; /** diff --git a/packages/react-components/react-dialog/src/components/DialogTrigger/__snapshots__/DialogTrigger.test.tsx.snap b/packages/react-components/react-dialog/src/components/DialogTrigger/__snapshots__/DialogTrigger.test.tsx.snap index 0b42bea4c6d7ce..2b2fc0e0847e6e 100644 --- a/packages/react-components/react-dialog/src/components/DialogTrigger/__snapshots__/DialogTrigger.test.tsx.snap +++ b/packages/react-components/react-dialog/src/components/DialogTrigger/__snapshots__/DialogTrigger.test.tsx.snap @@ -5,7 +5,6 @@ exports[`DialogTrigger renders a default state 1`] = ` aria-haspopup="dialog" data-tabster="{\\"deloser\\":{}}" onClick={[Function]} - type="button" > Dialog trigger diff --git a/packages/react-components/react-dialog/src/components/DialogTrigger/useDialogTrigger.ts b/packages/react-components/react-dialog/src/components/DialogTrigger/useDialogTrigger.ts index f1c7d785ee4618..02cdc743bfbbfe 100644 --- a/packages/react-components/react-dialog/src/components/DialogTrigger/useDialogTrigger.ts +++ b/packages/react-components/react-dialog/src/components/DialogTrigger/useDialogTrigger.ts @@ -14,7 +14,7 @@ import { useARIAButtonProps } from '@fluentui/react-aria'; export const useDialogTrigger_unstable = (props: DialogTriggerProps): DialogTriggerState => { const isInsideSurfaceDialog = useDialogSurfaceContext_unstable(); - const { children, action = isInsideSurfaceDialog ? 'close' : 'open' } = props; + const { children, disableButtonEnhancement = false, action = isInsideSurfaceDialog ? 'close' : 'open' } = props; const child = getTriggerChild(children); @@ -35,17 +35,26 @@ export const useDialogTrigger_unstable = (props: DialogTriggerProps): DialogTrig }, ); + const triggerChildProps = { + ...child?.props, + 'aria-haspopup': action === 'close' ? undefined : 'dialog', + ref: child?.ref, + onClick: handleClick, + ...triggerAttributes, + } as const; + + const ariaButtonTriggerChildProps = useARIAButtonProps( + child?.type === 'button' || child?.type === 'a' ? child.type : 'div', + { + ...triggerChildProps, + type: 'button', + }, + ); + return { children: applyTriggerPropsToChildren( children, - useARIAButtonProps(child?.type === 'button' || child?.type === 'a' ? child.type : 'div', { - type: 'button', - ...child?.props, - 'aria-haspopup': action === 'close' ? undefined : 'dialog', - ref: child?.ref, - onClick: handleClick, - ...triggerAttributes, - }), + disableButtonEnhancement ? triggerChildProps : ariaButtonTriggerChildProps, ), }; }; diff --git a/packages/react-components/react-menu/etc/react-menu.api.md b/packages/react-components/react-menu/etc/react-menu.api.md index a6a64b763a0adb..f8309b3fd1711f 100644 --- a/packages/react-components/react-menu/etc/react-menu.api.md +++ b/packages/react-components/react-menu/etc/react-menu.api.md @@ -314,7 +314,9 @@ export type MenuTriggerChildProps; // @public (undocumented) -export type MenuTriggerProps = TriggerProps; +export type MenuTriggerProps = TriggerProps & { + disableButtonEnhancement?: boolean; +}; // @public (undocumented) export type MenuTriggerState = { diff --git a/packages/react-components/react-menu/src/components/Menu/Menu.test.tsx b/packages/react-components/react-menu/src/components/Menu/Menu.test.tsx index d8ab685555567c..74e17fee7ad820 100644 --- a/packages/react-components/react-menu/src/components/Menu/Menu.test.tsx +++ b/packages/react-components/react-menu/src/components/Menu/Menu.test.tsx @@ -25,7 +25,7 @@ describe('Menu', () => { displayName: 'Menu', requiredProps: { children: [ - + , @@ -47,7 +47,7 @@ describe('Menu', () => { it('renders a default state', () => { const { container } = render( - + @@ -66,7 +66,7 @@ describe('Menu', () => { const onOpenChange = jest.fn(); const { getByRole } = render( - + @@ -90,7 +90,7 @@ describe('Menu', () => { const onOpenChange = jest.fn(); const { getByRole } = render( - + @@ -119,7 +119,7 @@ describe('Menu', () => { // Arrange const { getByRole } = render( - + @@ -150,7 +150,7 @@ describe('Menu', () => { // Arrange const { getByRole } = render( - + @@ -178,7 +178,7 @@ describe('Menu', () => { // Arrange const { getByRole } = render( - + @@ -203,7 +203,7 @@ describe('Menu', () => { // Arrange const { getByRole } = render( - + @@ -230,7 +230,7 @@ describe('Menu', () => { // Arrange const { container } = render( - + @@ -250,7 +250,7 @@ describe('Menu', () => { // Arrange const { container } = render( - + diff --git a/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.test.tsx b/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.test.tsx index efcef32cefea6d..7406c1ac612823 100644 --- a/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.test.tsx +++ b/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.test.tsx @@ -34,7 +34,7 @@ describe('MenuTrigger', () => { */ it('renders a default state', () => { const component = renderer.create( - + , ); @@ -46,7 +46,7 @@ describe('MenuTrigger', () => { // Arrange const ref = jest.fn(); render( - + , ); @@ -74,7 +74,7 @@ describe('MenuTrigger', () => { cb(ref.current); }, []); return ( - + ); @@ -100,7 +100,7 @@ describe('MenuTrigger', () => { mockUseMenuContext({ setOpen }); const { getByRole } = render( - + , ); @@ -114,7 +114,7 @@ describe('MenuTrigger', () => { mockUseMenuContext({ setOpen }); const { getByRole } = render( - + , ); @@ -129,7 +129,7 @@ describe('MenuTrigger', () => { mockUseMenuContext({ setOpen }); const { getByRole } = render( - + , ); @@ -148,7 +148,7 @@ describe('MenuTrigger', () => { }; render( - + @@ -168,7 +168,7 @@ describe('MenuTrigger', () => { }; render( - + @@ -181,7 +181,7 @@ describe('MenuTrigger', () => { it('should not keyboard click when event is default prevented', () => { const onClick = jest.fn(); const { getByRole } = render( - +
trigger
diff --git a/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.types.ts b/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.types.ts index dd32cbb545592c..dda9513d0226df 100644 --- a/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.types.ts +++ b/packages/react-components/react-menu/src/components/MenuTrigger/MenuTrigger.types.ts @@ -2,7 +2,13 @@ import { ARIAButtonResultProps, ARIAButtonType } from '@fluentui/react-aria'; import type { TriggerProps } from '@fluentui/react-utilities'; import * as React from 'react'; -export type MenuTriggerProps = TriggerProps; +export type MenuTriggerProps = TriggerProps & { + /** + * Disables internal trigger mechanism that ensures a child provided will be a compliant ARIA button. + * @default false + */ + disableButtonEnhancement?: boolean; +}; /** * Props that are passed to the child of the MenuTrigger when cloned to ensure correct behaviour for the Menu diff --git a/packages/react-components/react-menu/src/components/MenuTrigger/useMenuTrigger.ts b/packages/react-components/react-menu/src/components/MenuTrigger/useMenuTrigger.ts index 93dd3cc48da31c..e2aadae6578a4b 100644 --- a/packages/react-components/react-menu/src/components/MenuTrigger/useMenuTrigger.ts +++ b/packages/react-components/react-menu/src/components/MenuTrigger/useMenuTrigger.ts @@ -21,7 +21,7 @@ import { useARIAButtonProps } from '@fluentui/react-aria'; * @param props - props from this instance of MenuTrigger */ export const useMenuTrigger_unstable = (props: MenuTriggerProps): MenuTriggerState => { - const { children } = props; + const { children, disableButtonEnhancement = false } = props; const triggerRef = useMenuContext_unstable(context => context.triggerRef); const menuPopoverRef = useMenuContext_unstable(context => context.menuPopoverRef); @@ -121,7 +121,7 @@ export const useMenuTrigger_unstable = (props: MenuTriggerProps): MenuTriggerSta } }; - const triggerProps = { + const contextMenuProps = { 'aria-haspopup': 'menu', 'aria-expanded': !open && !isSubmenu ? undefined : open, id: triggerId, @@ -133,18 +133,23 @@ export const useMenuTrigger_unstable = (props: MenuTriggerProps): MenuTriggerSta onMouseMove: useEventCallback(mergeCallbacks(child?.props.onMouseMove, onMouseMove)), } as const; - const ariaButtonTriggerProps = useARIAButtonProps( + const triggerChildProps = { + ...contextMenuProps, + onClick: useEventCallback(mergeCallbacks(child?.props.onClick, onClick)), + onKeyDown: useEventCallback(mergeCallbacks(child?.props.onKeyDown, onKeyDown)), + }; + + const ariaButtonTriggerChildProps = useARIAButtonProps( child?.type === 'button' || child?.type === 'a' ? child.type : 'div', - { - ...triggerProps, - onClick: useEventCallback(mergeCallbacks(child?.props.onClick, onClick)), - onKeyDown: useEventCallback(mergeCallbacks(child?.props.onKeyDown, onKeyDown)), - }, + triggerChildProps, ); return { isSubmenu, - children: applyTriggerPropsToChildren(children, openOnContext ? triggerProps : ariaButtonTriggerProps), + children: applyTriggerPropsToChildren( + children, + openOnContext ? contextMenuProps : disableButtonEnhancement ? triggerChildProps : ariaButtonTriggerChildProps, + ), }; }; diff --git a/packages/react-components/react-popover/etc/react-popover.api.md b/packages/react-components/react-popover/etc/react-popover.api.md index f05fb4073d9617..a6da770e029a3f 100644 --- a/packages/react-components/react-popover/etc/react-popover.api.md +++ b/packages/react-components/react-popover/etc/react-popover.api.md @@ -114,7 +114,9 @@ export type PopoverTriggerChildProps; // @public -export type PopoverTriggerProps = TriggerProps; +export type PopoverTriggerProps = TriggerProps & { + disableButtonEnhancement?: boolean; +}; // @public export type PopoverTriggerState = { diff --git a/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.test.tsx b/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.test.tsx index 82a5b6b0dd4dc4..e8e3fa5818d90c 100644 --- a/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.test.tsx +++ b/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.test.tsx @@ -32,7 +32,7 @@ describe('PopoverTrigger', () => { */ it('renders a default state', () => { const component = renderer.create( - + , ); @@ -50,7 +50,7 @@ describe('PopoverTrigger', () => { // Arrange const spy = jest.fn(); const { getByRole } = render( - + , ); @@ -65,7 +65,7 @@ describe('PopoverTrigger', () => { it('should set aria-expanded on trigger element', () => { // Arrange const { getByRole } = render( - + , ); @@ -79,7 +79,7 @@ describe('PopoverTrigger', () => { // Arrange const { getByRole } = render( - + , ); @@ -91,7 +91,7 @@ describe('PopoverTrigger', () => { it('should allow user to override aria-expanded on trigger element', () => { // Arrange const { getByRole } = render( - + , ); @@ -104,7 +104,7 @@ describe('PopoverTrigger', () => { // Arrange const ref = jest.fn(); render( - + , ); @@ -132,7 +132,7 @@ describe('PopoverTrigger', () => { cb(ref.current); }, []); return ( - + ); diff --git a/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.types.ts b/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.types.ts index e8fae847b58626..918cf0687646c9 100644 --- a/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.types.ts +++ b/packages/react-components/react-popover/src/components/PopoverTrigger/PopoverTrigger.types.ts @@ -5,7 +5,13 @@ import * as React from 'react'; /** * PopoverTrigger Props */ -export type PopoverTriggerProps = TriggerProps; +export type PopoverTriggerProps = TriggerProps & { + /** + * Disables internal trigger mechanism that ensures a child provided will be a compliant ARIA button. + * @default false + */ + disableButtonEnhancement?: boolean; +}; /** * PopoverTrigger State diff --git a/packages/react-components/react-popover/src/components/PopoverTrigger/usePopoverTrigger.ts b/packages/react-components/react-popover/src/components/PopoverTrigger/usePopoverTrigger.ts index 25e536cdf15b55..3754347bda1384 100644 --- a/packages/react-components/react-popover/src/components/PopoverTrigger/usePopoverTrigger.ts +++ b/packages/react-components/react-popover/src/components/PopoverTrigger/usePopoverTrigger.ts @@ -21,7 +21,7 @@ import { Escape } from '@fluentui/keyboard-keys'; * @param props - props from this instance of PopoverTrigger */ export const usePopoverTrigger_unstable = (props: PopoverTriggerProps): PopoverTriggerState => { - const { children } = props; + const { children, disableButtonEnhancement = false } = props; const child = getTriggerChild(children); const open = usePopoverContext_unstable(context => context.open); @@ -66,7 +66,7 @@ export const usePopoverTrigger_unstable = (props: PopoverTriggerProps): PopoverT } }; - const triggerProps = { + const contextMenuProps = { ...triggerAttributes, 'aria-expanded': `${open}`, ...child?.props, @@ -76,13 +76,15 @@ export const usePopoverTrigger_unstable = (props: PopoverTriggerProps): PopoverT ref: useMergedRefs(triggerRef, child?.ref), } as const; - const ariaButtonTriggerProps = useARIAButtonProps( + const triggerChildProps = { + ...contextMenuProps, + onClick: useEventCallback(mergeCallbacks(child?.props.onClick, onClick)), + onKeyDown: useEventCallback(mergeCallbacks(child?.props.onKeyDown, onKeyDown)), + }; + + const ariaButtonTriggerChildProps = useARIAButtonProps( child?.type === 'button' || child?.type === 'a' ? child.type : 'div', - { - ...triggerProps, - onClick: useEventCallback(mergeCallbacks(child?.props.onClick, onClick)), - onKeyDown: useEventCallback(mergeCallbacks(child?.props.onKeyDown, onKeyDown)), - }, + triggerChildProps, ); return { @@ -90,7 +92,7 @@ export const usePopoverTrigger_unstable = (props: PopoverTriggerProps): PopoverT props.children, useARIAButtonProps( child?.type === 'button' || child?.type === 'a' ? child.type : 'div', - openOnContext ? triggerProps : ariaButtonTriggerProps, + openOnContext ? contextMenuProps : disableButtonEnhancement ? triggerChildProps : ariaButtonTriggerChildProps, ), ), };