Skip to content

Commit

Permalink
Merge pull request #472 from pixiv/toshusai/fix-modal-scroll
Browse files Browse the repository at this point in the history
Fix: The modal closed when clicking the scrollbar on the modal background.
  • Loading branch information
toshusai authored Mar 4, 2024
2 parents 95da174 + 6eb24b7 commit 5d178a4
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ exports[`Storybook Tests DropdownSelector InModal 1`] = `
</button>
<div
className="c0"
onClick={[Function]}
onPointerDown={[Function]}
style={
Object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ exports[`Storybook Tests Modal BottomSheet 1`] = `
</button>
<div
className="c2"
onClick={[Function]}
onPointerDown={[Function]}
style={
Object {
Expand Down Expand Up @@ -1200,6 +1201,7 @@ exports[`Storybook Tests Modal Default 1`] = `
</button>
<div
className="c2"
onClick={[Function]}
onPointerDown={[Function]}
style={Object {}}
>
Expand Down Expand Up @@ -2024,6 +2026,7 @@ exports[`Storybook Tests Modal FullBottomSheet 1`] = `
</button>
<div
className="c2"
onClick={[Function]}
onPointerDown={[Function]}
style={
Object {
Expand Down Expand Up @@ -2491,6 +2494,7 @@ exports[`Storybook Tests Modal InternalScroll 1`] = `
</button>
<div
className="c2"
onClick={[Function]}
onPointerDown={[Function]}
style={Object {}}
>
Expand Down Expand Up @@ -2870,6 +2874,7 @@ exports[`Storybook Tests Modal NotDismmissableStory 1`] = `
</button>
<div
className="c2"
onClick={[Function]}
onPointerDown={[Function]}
style={Object {}}
>
Expand Down
35 changes: 15 additions & 20 deletions packages/react/src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { useContext, forwardRef, memo } from 'react'
import * as React from 'react'
import {
AriaModalOverlayProps,
Overlay,
useModalOverlay,
} from '@react-aria/overlays'
import { AriaModalOverlayProps, Overlay } from '@react-aria/overlays'
import styled, { css, useTheme } from 'styled-components'
import { AriaDialogProps } from '@react-types/dialog'
import { maxWidth } from '@charcoal-ui/utils'
Expand All @@ -15,6 +11,7 @@ import IconButton from '../IconButton'
import { useObjectRef } from '@react-aria/utils'
import { Dialog } from './Dialog'
import { ModalBackgroundContext } from './ModalBackgroundContext'
import { useCharcoalModalOverlay } from './useCustomModalOverlay'

export type BottomSheet = boolean | 'full'
export type Size = 'S' | 'M' | 'L'
Expand Down Expand Up @@ -81,27 +78,15 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(

const ref = useObjectRef<HTMLDivElement>(external)

const { modalProps, underlayProps } = useModalOverlay(
const { modalProps, underlayProps } = useCharcoalModalOverlay(
{
...props,
isKeyboardDismissDisabled:
isDismissable === undefined || isDismissable === false,
},

{
close: onClose,
isOpen: isOpen,
// these props are not used actually.
// https://github.com/adobe/react-spectrum/blob/df14e3fb129b94b310f0397a701b83f006b51dfe/packages/%40react-aria/overlays/src/useModalOverlay.ts
open: () => {
// nope
},
setOpen: () => {
// nope
},
toggle: () => {
// nope
},
onClose,
isOpen,
},
ref
)
Expand Down Expand Up @@ -136,6 +121,15 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(

const bgRef = React.useRef<HTMLElement>(null)

const handleClick = React.useCallback(
(e: MouseEvent) => {
if (e.currentTarget === e.target) {
onClose()
}
},
[onClose]
)

return transition(
({ backgroundColor, overflow, transform }, item) =>
item && (
Expand All @@ -146,6 +140,7 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(
{...underlayProps}
style={transitionEnabled ? { backgroundColor, overflow } : {}}
$bottomSheet={bottomSheet}
onClick={handleClick}
>
<ModalBackgroundContext.Provider value={bgRef.current}>
<Dialog
Expand Down
42 changes: 42 additions & 0 deletions packages/react/src/components/Modal/useCustomModalOverlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as React from 'react'
import {
AriaModalOverlayProps,
ModalOverlayAria,
ariaHideOutside,
useOverlay,
useOverlayFocusContain,
} from '@react-aria/overlays'

/**
* We want to enable scrolling on the modal background,
* but `useModalOverlay` (specifically, `useOverlay` within it) detects pointer events on the scrollbar.
* Therefore, to prevent this issue, we need to override `shouldCloseOnInteractOutside` in `useModalOverlay`.
*/
export function useCharcoalModalOverlay(
props: AriaModalOverlayProps,
state: { isOpen: boolean; onClose: () => void },
ref: React.RefObject<HTMLElement>
): ModalOverlayAria {
const { overlayProps, underlayProps } = useOverlay(
{
...props,
isOpen: state.isOpen,
onClose: state.onClose,
shouldCloseOnInteractOutside: () => false,
},
ref
)

useOverlayFocusContain()

React.useEffect(() => {
if (state.isOpen && ref.current) {
return ariaHideOutside([ref.current])
}
}, [state.isOpen, ref])

return {
modalProps: overlayProps,
underlayProps,
}
}

0 comments on commit 5d178a4

Please sign in to comment.