-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactor dialogs and bottom sheets (#538)
* feat: refactor dialogs and bottom sheets * test: add test * fix: update snapshot
- Loading branch information
Showing
8 changed files
with
222 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { useIsMobileWidth } from 'hooks/useIsMobileWidth' | ||
import { render } from 'test' | ||
import { Provider as ThemeProvider } from 'theme' | ||
|
||
import { useIsDialogPageCentered } from './Dialog' | ||
import { ResponsiveDialog } from './ResponsiveDialog' | ||
|
||
jest.mock('hooks/useIsMobileWidth') | ||
jest.mock('./Dialog') | ||
|
||
const mockUseIsMobileWidth = useIsMobileWidth as jest.Mock | ||
const mockUseIsDialogPageCentered = useIsDialogPageCentered as jest.Mock | ||
|
||
describe('ResponsiveDialog', () => { | ||
beforeEach(() => { | ||
mockUseIsMobileWidth.mockReturnValue(false) | ||
mockUseIsDialogPageCentered.mockReturnValue(false) | ||
}) | ||
|
||
it('renders a dialog by default (nothing rendered when closed)', () => { | ||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={false} setOpen={() => null}> | ||
<div>dialog content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.queryByText('dialog content')).toBeNull() | ||
}) | ||
|
||
it('renders a popover when defaultView is set to popover', () => { | ||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={true} setOpen={() => null} defaultView="popover"> | ||
<div>popover content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.getByTestId('popover-container')).toBeTruthy() | ||
}) | ||
|
||
it('renders a bottom sheet when on mobile and pageCenteredDialogsEnabled is true', () => { | ||
mockUseIsMobileWidth.mockReturnValue(true) | ||
mockUseIsDialogPageCentered.mockReturnValue(true) | ||
|
||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={true} setOpen={() => null}> | ||
<div>bottom sheet content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.getByTestId('BottomSheetModal__Wrapper')).toBeTruthy() | ||
}) | ||
|
||
it('renders a bottom sheet when on mobile and mobileBottomSheet is true', () => { | ||
mockUseIsMobileWidth.mockReturnValue(true) | ||
|
||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={true} setOpen={() => null} mobileBottomSheet={true}> | ||
<div>bottom sheet content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.getByTestId('BottomSheetModal__Wrapper')).toBeTruthy() | ||
}) | ||
|
||
it('renders a popover when on mobile and defaultView is set to popover', () => { | ||
mockUseIsMobileWidth.mockReturnValue(true) | ||
|
||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={true} setOpen={() => null} defaultView="popover"> | ||
<div>popover content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.getByTestId('popover-container')).toBeTruthy() | ||
}) | ||
|
||
it('renders an anchor when provided', () => { | ||
const view = render( | ||
<ThemeProvider> | ||
<ResponsiveDialog open={true} setOpen={() => null} anchor={<div>anchor</div>}> | ||
<div>dialog content</div> | ||
</ResponsiveDialog> | ||
</ThemeProvider> | ||
) | ||
|
||
expect(view.getByText('anchor')).toBeTruthy() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { useIsMobileWidth } from 'hooks/useIsMobileWidth' | ||
import { useOutsideClickHandler } from 'hooks/useOutsideClickHandler' | ||
import { Info } from 'icons' | ||
import { PropsWithChildren, useState } from 'react' | ||
|
||
import { BottomSheetModal } from './BottomSheetModal' | ||
import { IconButton } from './Button' | ||
import Dialog, { useIsDialogPageCentered } from './Dialog' | ||
import Popover, { PopoverBoundaryProvider } from './Popover' | ||
|
||
interface ResponsiveDialogProps { | ||
open: boolean | ||
setOpen: (open: boolean) => void | ||
// when not on a mobile width, use the default view. | ||
defaultView?: 'dialog' | 'popover' | ||
// an anchor view to render when the dialog is closed. Useful as an entry point to a bottom sheet or popover. | ||
// if not provided, a default info icon will be used. | ||
anchor?: React.ReactNode | ||
// If true, always render the dialog as a bottom sheet on mobile. | ||
// If false, it will only be a bottom sheet if it was page-centered. | ||
mobileBottomSheet?: boolean | ||
bottomSheetTitle?: string | ||
} | ||
|
||
/** | ||
* A Dialog or Popover that renders as a bottom sheet on mobile. | ||
*/ | ||
export function ResponsiveDialog({ | ||
children, | ||
open, | ||
setOpen, | ||
defaultView = 'dialog', | ||
anchor, | ||
mobileBottomSheet, | ||
bottomSheetTitle, | ||
}: PropsWithChildren<ResponsiveDialogProps>) { | ||
const isMobile = useIsMobileWidth() | ||
const pageCenteredDialogsEnabled = useIsDialogPageCentered() | ||
const [wrapper, setWrapper] = useState<HTMLDivElement | null>(null) | ||
useOutsideClickHandler(isMobile ? null : wrapper, () => setOpen(false)) | ||
|
||
if (isMobile && (pageCenteredDialogsEnabled || mobileBottomSheet)) { | ||
return ( | ||
<> | ||
{anchor} | ||
<BottomSheetModal onClose={() => setOpen(false)} open={open} title={bottomSheetTitle}> | ||
{children} | ||
</BottomSheetModal> | ||
</> | ||
) | ||
} else if (defaultView === 'popover') { | ||
return ( | ||
<div ref={setWrapper}> | ||
<PopoverBoundaryProvider value={wrapper}> | ||
<Popover showArrow={false} offset={10} show={open} placement="top-end" content={children}> | ||
{anchor ?? <IconButton icon={Info} />} | ||
</Popover> | ||
</PopoverBoundaryProvider> | ||
</div> | ||
) | ||
} else { | ||
return ( | ||
<> | ||
{anchor} | ||
{open && ( | ||
<Dialog color="container" onClose={() => setOpen(false)}> | ||
{children} | ||
</Dialog> | ||
)} | ||
</> | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
ba4aec1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
widgets – ./
widgets-seven-tau.vercel.app
widgets-git-main-uniswap.vercel.app
widgets-uniswap.vercel.app