From e568c22c9cb017ea304e62d47ddcffcc2f92fbac Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Tue, 27 Sep 2022 19:01:01 +0000 Subject: [PATCH 1/2] feat(react-dialog): `as="div"` by default --- ...-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json | 7 +++++++ .../react-components/react-dialog/etc/react-dialog.api.md | 2 +- .../src/components/DialogSurface/DialogSurface.types.ts | 2 +- .../src/components/DialogSurface/useDialogSurface.ts | 7 +++++-- 4 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json diff --git a/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json b/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json new file mode 100644 index 00000000000000..00564482434282 --- /dev/null +++ b/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: as=\"div\" by default", + "packageName": "@fluentui/react-dialog", + "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 23305f289c0e59..30cf7b86153a33 100644 --- a/packages/react-components/react-dialog/etc/react-dialog.api.md +++ b/packages/react-components/react-dialog/etc/react-dialog.api.md @@ -137,7 +137,7 @@ export type DialogSurfaceProps = Omit, 'open' // @public (undocumented) export type DialogSurfaceSlots = { backdrop?: Slot<'div'>; - root: NonNullable>; + root: NonNullable>; }; // @public diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index 2ebd2543c8e0f4..79ff1680d81219 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -12,7 +12,7 @@ export type DialogSurfaceSlots = { * since native `` element supports [::backdrop](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop) */ backdrop?: Slot<'div'>; - root: NonNullable>; + root: NonNullable>; }; /** diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts index 02915c7b50388d..4ccfc99392fecd 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts @@ -15,6 +15,9 @@ import type { import { useDialogContext_unstable } from '../../contexts'; import { isEscapeKeyDismiss, HTMLDialogElementProps } from '../../utils'; import { useModalAttributes } from '@fluentui/react-tabster'; + +const defaultDialogType: 'div' | 'dialog' = 'div'; + /** * Create the state required to render DialogSurface. * @@ -118,7 +121,7 @@ export const useDialogSurface_unstable = ( return { components: { backdrop: 'div', - root: 'dialog', + root: defaultDialogType, }, backdrop: resolveShorthand(backdrop, { required: !isNativeDialog && open && modalType !== 'non-modal', @@ -127,7 +130,7 @@ export const useDialogSurface_unstable = ( onClick: handledBackdropClick, }, }), - root: getNativeElementProps(as ?? 'dialog', { + root: getNativeElementProps(as ?? defaultDialogType, { ...(isNativeDialog ? { role: modalType === 'alert' ? 'alertdialog' : undefined, From 3be490f0cf602037a00ced5b7177e8b0eda1b956 Mon Sep 17 00:00:00 2001 From: Bernardo Sunderhus Date: Fri, 30 Sep 2022 09:30:28 +0000 Subject: [PATCH 2/2] feat: completely remove native dialog support --- ...-f0db0d7e-2454-43bc-91e3-d5eed405519c.json | 2 +- .../react-dialog/etc/react-dialog.api.md | 2 +- .../src/components/Dialog/useDialog.ts | 13 +--- .../DialogSurface/DialogSurface.types.ts | 2 +- .../DialogSurface/useDialogSurface.ts | 72 ++----------------- .../react-dialog/src/utils/index.ts | 2 - .../react-dialog/src/utils/isEscapeKeyDown.ts | 9 +-- .../src/utils/isHTMLDialogElement.ts | 23 ------ .../utils/useControlNativeDialogOpenState.ts | 24 ------- .../src/utils/useFocusFirstElement.ts | 6 +- 10 files changed, 14 insertions(+), 141 deletions(-) delete mode 100644 packages/react-components/react-dialog/src/utils/isHTMLDialogElement.ts delete mode 100644 packages/react-components/react-dialog/src/utils/useControlNativeDialogOpenState.ts diff --git a/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json b/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json index 00564482434282..5c3822621ea60f 100644 --- a/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json +++ b/change/@fluentui-react-dialog-f0db0d7e-2454-43bc-91e3-d5eed405519c.json @@ -1,6 +1,6 @@ { "type": "prerelease", - "comment": "feat: as=\"div\" by default", + "comment": "feat: removes DialogSurface native dialog support ", "packageName": "@fluentui/react-dialog", "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 30cf7b86153a33..39d3c6cc52e984 100644 --- a/packages/react-components/react-dialog/etc/react-dialog.api.md +++ b/packages/react-components/react-dialog/etc/react-dialog.api.md @@ -137,7 +137,7 @@ export type DialogSurfaceProps = Omit, 'open' // @public (undocumented) export type DialogSurfaceSlots = { backdrop?: Slot<'div'>; - root: NonNullable>; + root: Slot<'div'>; }; // @public diff --git a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts index 763ddf618ddd42..8a96386c788a25 100644 --- a/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts +++ b/packages/react-components/react-dialog/src/components/Dialog/useDialog.ts @@ -1,13 +1,7 @@ import * as React from 'react'; -import { - useControllableState, - useEventCallback, - useId, - useIsomorphicLayoutEffect, - useMergedRefs, -} from '@fluentui/react-utilities'; +import { useControllableState, useEventCallback, useId, useIsomorphicLayoutEffect } from '@fluentui/react-utilities'; import { useHasParentContext } from '@fluentui/react-context-selector'; -import { useControlNativeDialogOpenState, useDisableBodyScroll, useFocusFirstElement } from '../../utils'; +import { useDisableBodyScroll, useFocusFirstElement } from '../../utils'; import { DialogContext } from '../../contexts'; import type { DialogOpenChangeData, DialogProps, DialogState } from './Dialog.types'; @@ -42,7 +36,6 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { }); const focusRef = useFocusFirstElement(open, modalType); - const nativeControlRef = useControlNativeDialogOpenState(open, modalType); const disableBodyScroll = useDisableBodyScroll(); const isBodyScrollLocked = Boolean(open && modalType !== 'non-modal'); @@ -64,7 +57,7 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { dialogContentId: useId('dialog-content-'), dialogTitleId: useId('dialog-title-'), isNestedDialog: useHasParentContext(DialogContext), - dialogRef: useMergedRefs(focusRef, nativeControlRef), + dialogRef: focusRef, }; }; diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts index 79ff1680d81219..32e4ef164e8d92 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/DialogSurface.types.ts @@ -12,7 +12,7 @@ export type DialogSurfaceSlots = { * since native `` element supports [::backdrop](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop) */ backdrop?: Slot<'div'>; - root: NonNullable>; + root: Slot<'div'>; }; /** diff --git a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts index 4ccfc99392fecd..a3e72e019ca73e 100644 --- a/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/src/components/DialogSurface/useDialogSurface.ts @@ -13,11 +13,9 @@ import type { DialogSurfaceState, } from './DialogSurface.types'; import { useDialogContext_unstable } from '../../contexts'; -import { isEscapeKeyDismiss, HTMLDialogElementProps } from '../../utils'; +import { isEscapeKeyDismiss } from '../../utils'; import { useModalAttributes } from '@fluentui/react-tabster'; -const defaultDialogType: 'div' | 'dialog' = 'div'; - /** * Create the state required to render DialogSurface. * @@ -32,7 +30,6 @@ export const useDialogSurface_unstable = ( ref: React.Ref, ): DialogSurfaceState => { const { backdrop, as } = props; - const isNativeDialog = as === 'dialog' || as === undefined; const modalType = useDialogContext_unstable(ctx => ctx.modalType); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); const open = useDialogContext_unstable(ctx => ctx.open); @@ -40,54 +37,6 @@ export const useDialogSurface_unstable = ( const dialogTitleID = useDialogContext_unstable(ctx => ctx.dialogTitleId); const dialogContentId = useDialogContext_unstable(ctx => ctx.dialogContentId); - const handleNativeClick = useEventCallback((event: React.MouseEvent) => { - props.onClick?.(event); - if (modalType === 'alert' || event.target !== event.currentTarget) { - return; - } - const { clientX, clientY } = event; - const { top, left, width, height } = event.currentTarget.getBoundingClientRect(); - const isBackdropClick = top > clientY || clientY > top + height || left > clientX || clientX > left + width; - if (isBackdropClick) { - requestOpenChange({ - event, - open: false, - type: 'backdropClick', - }); - } - }); - - const handleNativeCancel = useEventCallback((event: React.SyntheticEvent) => { - (props as HTMLDialogElementProps).onCancel?.(event); - if (event.currentTarget !== event.target) { - return; - } - if (modalType !== 'alert') { - requestOpenChange({ - event, - open: false, - type: 'dialogCancel', - }); - } - event.preventDefault(); - }); - - const handleNativeClose = useEventCallback((event: React.SyntheticEvent) => { - (props as HTMLDialogElementProps).onClose?.(event); - // Ensure dialog remains open if force closed by close event - if (event.currentTarget.open !== open) { - if (open) { - if (modalType === 'non-modal') { - event.currentTarget.show(); - } else { - event.currentTarget.showModal(); - } - } else { - event.currentTarget.close(); - } - } - }); - const handledBackdropClick = useEventCallback((event: React.MouseEvent) => { if (isResolvedShorthand(props.backdrop)) { props.backdrop.onClick?.(event); @@ -121,27 +70,18 @@ export const useDialogSurface_unstable = ( return { components: { backdrop: 'div', - root: defaultDialogType, + root: 'div', }, backdrop: resolveShorthand(backdrop, { - required: !isNativeDialog && open && modalType !== 'non-modal', + required: open && modalType !== 'non-modal', defaultProps: { 'aria-hidden': 'true', onClick: handledBackdropClick, }, }), - root: getNativeElementProps(as ?? defaultDialogType, { - ...(isNativeDialog - ? { - role: modalType === 'alert' ? 'alertdialog' : undefined, - onClose: handleNativeClose, - onClick: handleNativeClick, - onCancel: handleNativeCancel, - } - : { - 'aria-modal': modalType !== 'non-modal', - role: modalType === 'alert' ? 'alertdialog' : 'dialog', - }), + root: getNativeElementProps(as ?? 'div', { + 'aria-modal': modalType !== 'non-modal', + role: modalType === 'alert' ? 'alertdialog' : 'dialog', ...props, ...modalAttributes, onKeyDown: handleKeyDown, diff --git a/packages/react-components/react-dialog/src/utils/index.ts b/packages/react-components/react-dialog/src/utils/index.ts index d713825c749b09..ed3d5165c8c5a9 100644 --- a/packages/react-components/react-dialog/src/utils/index.ts +++ b/packages/react-components/react-dialog/src/utils/index.ts @@ -1,5 +1,3 @@ export * from './isEscapeKeyDown'; export * from './useDisableBodyScroll'; export * from './useFocusFirstElement'; -export * from './isHTMLDialogElement'; -export * from './useControlNativeDialogOpenState'; diff --git a/packages/react-components/react-dialog/src/utils/isEscapeKeyDown.ts b/packages/react-components/react-dialog/src/utils/isEscapeKeyDown.ts index 66dc354389f70c..6acb06762e5e0e 100644 --- a/packages/react-components/react-dialog/src/utils/isEscapeKeyDown.ts +++ b/packages/react-components/react-dialog/src/utils/isEscapeKeyDown.ts @@ -2,7 +2,6 @@ import * as React from 'react'; import { Escape } from '@fluentui/keyboard-keys'; import type { DialogModalType } from '../components/Dialog/Dialog.types'; import { DialogSurfaceElement } from '../DialogSurface'; -import { isHTMLDialogElement } from './isHTMLDialogElement'; /** * Checks if keydown event is a proper Escape key dismiss @@ -11,11 +10,5 @@ export function isEscapeKeyDismiss( event: React.KeyboardEvent, modalType: DialogModalType, ): boolean { - return ( - event.key === Escape && - // `non-modal` should always have Escape key handling - // `modal` should only be handled in the case of non native dialog - (modalType === 'non-modal' || (!isHTMLDialogElement(event.currentTarget) && modalType === 'modal')) && - !event.isDefaultPrevented() - ); + return event.key === Escape && modalType !== 'alert' && !event.isDefaultPrevented(); } diff --git a/packages/react-components/react-dialog/src/utils/isHTMLDialogElement.ts b/packages/react-components/react-dialog/src/utils/isHTMLDialogElement.ts deleted file mode 100644 index 537a09b44963e3..00000000000000 --- a/packages/react-components/react-dialog/src/utils/isHTMLDialogElement.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from 'react'; - -export function isHTMLDialogElement(element?: HTMLElement | null): element is HTMLDialogElement { - return Boolean(element && 'open' in element && 'show' in element && 'showModal' in element && 'close' in element); -} - -/** - * adds additional types which are missing from current version of react - * @internal - */ -export type HTMLDialogElementProps = JSX.IntrinsicElements['dialog'] & { - /** - * The close event is fired on a when it has been closed. - */ - onClose?: (event: React.SyntheticEvent) => void; - /** - * The cancel event fires on a when - * the user instructs the browser that they wish to dismiss the current open dialog. - * For example, the browser might fire this event when the user presses the Esc - * key. - */ - onCancel?: (event: React.SyntheticEvent) => void; -}; diff --git a/packages/react-components/react-dialog/src/utils/useControlNativeDialogOpenState.ts b/packages/react-components/react-dialog/src/utils/useControlNativeDialogOpenState.ts deleted file mode 100644 index a49fa6b938ae09..00000000000000 --- a/packages/react-components/react-dialog/src/utils/useControlNativeDialogOpenState.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from 'react'; -import { isHTMLDialogElement } from './isHTMLDialogElement'; -import type { DialogModalType } from '../Dialog'; -import type { DialogSurfaceElement } from '../DialogSurface'; - -export function useControlNativeDialogOpenState(open: boolean, modalType: DialogModalType) { - const dialogSurfaceRef = React.useRef(null); - - React.useEffect(() => { - if (isHTMLDialogElement(dialogSurfaceRef.current) && dialogSurfaceRef.current.open !== open) { - if (open) { - if (modalType === 'non-modal') { - dialogSurfaceRef.current.show(); - } else { - dialogSurfaceRef.current.showModal(); - } - } else { - dialogSurfaceRef.current.close(); - } - } - }, [open, modalType]); - - return dialogSurfaceRef; -} diff --git a/packages/react-components/react-dialog/src/utils/useFocusFirstElement.ts b/packages/react-components/react-dialog/src/utils/useFocusFirstElement.ts index 4bab59d08f110c..bd565daefde7c1 100644 --- a/packages/react-components/react-dialog/src/utils/useFocusFirstElement.ts +++ b/packages/react-components/react-dialog/src/utils/useFocusFirstElement.ts @@ -3,7 +3,6 @@ import { useFocusFinders } from '@fluentui/react-tabster'; import { useFluent_unstable } from '@fluentui/react-shared-contexts'; import type { DialogSurfaceElement } from '../DialogSurface'; import type { DialogModalType } from '../Dialog'; -import { isHTMLDialogElement } from './isHTMLDialogElement'; /** * Focus first element on content when dialog is opened, @@ -21,10 +20,7 @@ export function useFocusFirstElement(open: boolean, modalType: DialogModalType) triggerRef.current = targetDocument?.activeElement as HTMLElement | undefined; const element = dialogRef.current && findFirstFocusable(dialogRef.current); if (element) { - // this is only required for non native dialogs - if (!isHTMLDialogElement(dialogRef.current)) { - element.focus(); - } + element.focus(); } else if (process.env.NODE_ENV !== 'production') { triggerRef.current?.blur(); // eslint-disable-next-line no-console