Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Modal内のDropdownSelectorの挙動修正 #384

Merged
merged 10 commits into from
Oct 23, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export function DropdownPopover({ children, ...props }: DropdownPopoverProps) {
const selectedElement = document.querySelector(
`[data-key="${props.value.toString()}"]`
) as HTMLElement | undefined
selectedElement?.scrollIntoView({ block: 'center' })
selectedElement?.focus()
window.scrollTo(windowScrollX, windowScrollY)
}
Expand Down
19 changes: 17 additions & 2 deletions packages/react/src/components/DropdownSelector/Popover/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { RefObject, useRef } from 'react'
import { RefObject, useContext, useRef } from 'react'
import { ReactNode } from 'react'
import { DismissButton, Overlay, usePopover } from '@react-aria/overlays'
import styled from 'styled-components'
import { theme } from '../../../styled'
import { ModalBackgroundContext } from '../../Modal/ModalBackgroundContext'
import { usePreventScroll } from './usePreventScroll'

export type PopoverProps = {
isOpen: boolean
Expand Down Expand Up @@ -40,11 +42,24 @@ export default function Popover(props: PopoverProps) {
}
)

const modalBackground = useContext(ModalBackgroundContext)
usePreventScroll(modalBackground, props.isOpen)

if (!props.isOpen) return null

return (
<Overlay portalContainer={document.body}>
<div {...underlayProps} style={{ position: 'fixed', inset: 0 }} />
<div
{...underlayProps}
style={{
position: 'fixed',
zIndex:
typeof popoverProps.style?.zIndex === 'number'
? popoverProps.style.zIndex - 1
: 99999,
inset: 0,
}}
/>
<DropdownPopoverDiv {...popoverProps} ref={finalPopoverRef}>
<DismissButton onDismiss={() => props.onClose()} />
{props.children}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useEffect } from 'react'

export function usePreventScroll(element: HTMLElement | null, isOpen: boolean) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/overlays/src/usePreventScroll.ts#L45

paddingRight がどこから来てるのか調べていたらここにあたって ios 用の分岐が必要そうだったが、実機で確認したところ問題なかった

useEffect(() => {
if (isOpen && element) {
const defaultPaddingRight = element.style.paddingRight
const defaultOverflow = element.style.overflow
element.style.paddingRight = `${
window.innerWidth - element.clientWidth
}px`
element.style.overflow = 'hidden'
return () => {
element.style.paddingRight = defaultPaddingRight
element.style.overflow = defaultOverflow
}
}
}, [element, isOpen])
}
82 changes: 69 additions & 13 deletions packages/react/src/components/DropdownSelector/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { Story } from '../../_lib/compat'
import { Divider } from './Divider'
import MenuItemGroup from './MenuItemGroup'
import DropdownMenuItem from './DropdownMenuItem'
import Modal from '../Modal'
import { ModalBody, ModalHeader } from '../Modal/ModalPlumbing'
import styled from 'styled-components'
import Button from '../Button'

export default {
title: 'DropdownSelector',
Expand All @@ -26,6 +30,7 @@ export const Playground: Story<DropdownSelectorProps> = (
const [selected, setSelected] = useState('50')
return (
<div style={{ width: 288 }}>
<Button></Button>
<DropdownSelector
{...props}
onChange={(value) => {
Expand All @@ -51,27 +56,78 @@ Playground.args = { ...baseProps }
export const Basic: Story<DropdownSelectorProps> = (
props: DropdownSelectorProps
) => {
const [selected, setSelected] = useState('')
return (
<div style={{ width: 288 }}>
<DropdownSelector
{...props}
onChange={(value) => {
setSelected(value)
}}
value={selected}
label="label"
>
<DropdownMenuItem value={'10'}>Apple</DropdownMenuItem>
<DropdownMenuItem value={'20'}>Banana</DropdownMenuItem>
<DropdownMenuItem value={'30'}>Orange</DropdownMenuItem>
</DropdownSelector>
<PlaygroundDropdownSelector {...props} />
</div>
)
}

Basic.args = { ...baseProps }

function PlaygroundDropdownSelector(props: Partial<DropdownSelectorProps>) {
const [selected, setSelected] = useState('10')
return (
<DropdownSelector
{...props}
onChange={(value) => {
setSelected(value)
}}
value={selected}
label="label"
>
<DropdownMenuItem value={'10'}>Apple</DropdownMenuItem>
<DropdownMenuItem value={'20'}>Banana</DropdownMenuItem>
<DropdownMenuItem value={'30'}>Orange</DropdownMenuItem>
</DropdownSelector>
)
}

const DummyBox = styled.div`
border: solid 1px ${({ theme }) => theme.border.default.color};
display: flex;
justify-content: center;
align-items: center;
height: 256px;
`

export const InModal: Story<DropdownSelectorProps> = () => {
const [open, setOpen] = useState(true)
return (
<div
style={{
height: '10px',
}}
>
<button onClick={() => setOpen(true)}>open</button>
<Modal
bottomSheet="full"
title="modal"
isOpen={open}
isDismissable
onClose={() => {
setOpen(false)
}}
>
<ModalHeader />
<ModalBody>
<div
style={{
padding: '16px',
}}
>
<DummyBox>256px</DummyBox>
<PlaygroundDropdownSelector />
<DummyBox>256px</DummyBox>
<PlaygroundDropdownSelector />
<DummyBox>256px</DummyBox>
</div>
</ModalBody>
</Modal>
</div>
)
}

export const InFormTag: Story<DropdownSelectorProps> = (
props: DropdownSelectorProps
) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as React from 'react'

/**
* ModalBackgroundのElementが入ったコンテキスト
*/
export const ModalBackgroundContext = React.createContext<HTMLElement | null>(
null
)
15 changes: 15 additions & 0 deletions packages/react/src/components/Modal/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
import styled from 'styled-components'
import { theme } from '../../styled'
import TextField from '../TextField'
import DropdownSelector from '../DropdownSelector'
import DropdownMenuItem from '../DropdownSelector/DropdownMenuItem'

export default {
title: 'Modal',
Expand Down Expand Up @@ -75,6 +77,19 @@ const M = (props: ModalProps) => {
placeholder="Tokyo"
></TextField>
</ModalAlign>
<ModalAlign>
<DropdownSelector
onChange={() => null}
value="10"
showLabel
label="Fruits"
placeholder="Apple"
>
<DropdownMenuItem value={'10'}>Apple</DropdownMenuItem>
<DropdownMenuItem value={'20'}>Banana</DropdownMenuItem>
<DropdownMenuItem value={'30'}>Orange</DropdownMenuItem>
</DropdownSelector>
</ModalAlign>
</ModalVStack>
<ModalButtons>
<Button variant="Primary" onClick={() => props.onClose()} fullWidth>
Expand Down
74 changes: 40 additions & 34 deletions packages/react/src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { animated, useTransition, easings } from 'react-spring'
import Button, { ButtonProps } from '../Button'
import IconButton from '../IconButton'
import { useObjectRef } from '@react-aria/utils'
import { ModalBackgroundContext } from './ModalBackgroundContext'

export type BottomSheet = boolean | 'full'
type Size = 'S' | 'M' | 'L'
Expand Down Expand Up @@ -131,50 +132,54 @@ const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(
: { duration: 0 },
})

return transition(({ backgroundColor, transform, overflow }, item) => {
return (
const bgRef = React.useRef<HTMLElement>(null)

return transition(
({ backgroundColor, overflow, transform }, item) =>
item && (
<Overlay portalContainer={portalContainer}>
<ModalBackground
ref={bgRef}
zIndex={zIndex}
{...underlayProps}
style={transitionEnabled ? { backgroundColor, overflow } : {}}
$bottomSheet={bottomSheet}
>
<ModalDialog
ref={ref}
{...modalProps}
style={transitionEnabled ? { transform } : {}}
size={size}
bottomSheet={bottomSheet}
className={className}
>
<Dialog>
<ModalContext.Provider
value={{
titleProps: {},
title,
close: onClose,
showDismiss,
bottomSheet,
}}
>
{children}
{isDismissable === true && (
<ModalCrossButton
size="S"
icon="24/Close"
onClick={onClose}
/>
)}
</ModalContext.Provider>
</Dialog>
</ModalDialog>
<ModalBackgroundContext.Provider value={bgRef.current}>
<ModalDialog
ref={ref}
{...modalProps}
style={transitionEnabled ? { transform } : {}}
size={size}
bottomSheet={bottomSheet}
className={className}
>
<Dialog>
<ModalContext.Provider
value={{
titleProps: {},
title,
close: onClose,
showDismiss,
bottomSheet,
}}
>
{children}
{isDismissable === true && (
<ModalCrossButton
size="S"
icon="24/Close"
onClick={onClose}
/>
)}
</ModalContext.Provider>
</Dialog>
</ModalDialog>
</ModalBackgroundContext.Provider>
</ModalBackground>
</Overlay>
)
)
})
)
})

export default memo(Modal)
Expand Down Expand Up @@ -217,7 +222,8 @@ const ModalBackground = animated(styled.div<{
position: fixed;
top: 0;
left: 0;
width: 100%;
width: -webkit-fill-available;
width: -moz-available;
height: 100%;
justify-content: center;
padding: 40px 0;
Expand Down
Loading