diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
new file mode 100644
index 00000000..f7d1c10b
--- /dev/null
+++ b/.storybook/preview-head.html
@@ -0,0 +1,5 @@
+
diff --git a/src/components/Backdrop/Backdrop.tsx b/src/components/Backdrop/Backdrop.tsx
index 03d7010f..4df1c104 100644
--- a/src/components/Backdrop/Backdrop.tsx
+++ b/src/components/Backdrop/Backdrop.tsx
@@ -1,6 +1,7 @@
-import { Content, Overlay, Root } from '@radix-ui/react-dialog'
-import React, { FC } from 'react'
+import { Content, Overlay, Portal, Root } from '@radix-ui/react-dialog'
+import React, { ComponentProps, FC } from 'react'
import { CSS, styled } from '../../stitches.config'
+import { ConditionalWrapper } from '../../utils'
import { overlayAnimationStyles, overlayStyles } from '../Overlay'
const StyledOverlay = styled(Overlay, overlayStyles, overlayAnimationStyles, {
@@ -33,6 +34,10 @@ type BackdropProps = React.ComponentProps & {
overlayCss?: CSS
/** Modify the default styling of the content wrapper */
contentCss?: CSS
+ /** By default, portals your overlay and content parts into the body, set false to add at dom location. */
+ portalled?: boolean
+ /** Specify a container element to portal the content into. */
+ container?: ComponentProps['container']
}
/**
@@ -45,13 +50,22 @@ type BackdropProps = React.ComponentProps & {
export const Backdrop: FC = ({
overlayCss,
contentCss,
+ container,
+ portalled = true,
children,
...props
}) => {
return (
-
- {children}
+ {child}}
+ >
+ <>
+
+ {children}
+ >
+
)
}
diff --git a/src/components/ComponentsProvider/ComponentsProvider.tsx b/src/components/ComponentsProvider/ComponentsProvider.tsx
index 9df606a1..980f5df8 100644
--- a/src/components/ComponentsProvider/ComponentsProvider.tsx
+++ b/src/components/ComponentsProvider/ComponentsProvider.tsx
@@ -1,4 +1,5 @@
import React, { FC, PropsWithChildren } from 'react'
+import { CSSProps, styled } from '../../stitches.config'
import { ConditionalWrapper } from '../../utils'
import { ThemeProvider, ThemeProviderProps } from '../ThemeProvider'
import { ToastProvider, ToastViewport } from '../Toast'
@@ -40,7 +41,15 @@ export type ComponentsProviderProps = {
* Toast viewport configuration options
*/
viewport?: false | ToastViewportPropsWithoutChildren
-}
+ /**
+ * By default the childre are put into their own stacking context to better separate the content from the portalled dialog elements. Set false to turn this off and controll it yourself.
+ */
+ isolated?: boolean
+} & CSSProps
+
+const Isolate = styled('div', {
+ variants: { isolated: { false: {}, true: { isolation: 'isolate' } } },
+})
/**
* The `ComponentsProvider` should wrap you application.
@@ -53,7 +62,15 @@ export type ComponentsProviderProps = {
*/
export const ComponentsProvider: FC<
PropsWithChildren
-> = ({ theme = {}, tooltip = {}, toast = {}, viewport = {}, children }) => (
+> = ({
+ theme = {},
+ tooltip = {},
+ toast = {},
+ viewport = {},
+ css,
+ isolated = true,
+ children,
+}) => (
(
@@ -73,7 +90,9 @@ export const ComponentsProvider: FC<
)}
>
<>
- {children}
+
+ {children}
+
{viewport && }
>
diff --git a/src/components/ConfirmDialog/ConfirmDialog.tsx b/src/components/ConfirmDialog/ConfirmDialog.tsx
index 8ce0d2c1..0f37841e 100644
--- a/src/components/ConfirmDialog/ConfirmDialog.tsx
+++ b/src/components/ConfirmDialog/ConfirmDialog.tsx
@@ -4,6 +4,7 @@ import {
Content,
Description,
Overlay,
+ Portal,
Root,
Title,
Trigger,
@@ -11,6 +12,7 @@ import {
import React, { ComponentProps, ElementRef, FC, forwardRef } from 'react'
import type { CSSProps } from '../../stitches.config'
import { CSS, styled } from '../../stitches.config'
+import { ConditionalWrapper } from '../../utils'
import { Button } from '../Button'
import { Heading } from '../Heading'
import { overlayAnimationStyles, overlayStyles } from '../Overlay'
@@ -56,11 +58,6 @@ export const StyledContent = styled(
overlayAnimationStyles
)
-type ConfirmDialogProps = ComponentProps & {
- /** Modify the default styling of the overlay */
- overlayCss?: CSS
-}
-
/**
* The `ConfirmDialog` component can be used get confirmation of an action from the user.
* This is done by isolating the user from the main window by overlaying
@@ -80,18 +77,7 @@ type ConfirmDialogProps = ComponentProps & {
*
* Based on [Radix Alert Dialog](https://radix-ui.com/primitives/docs/components/alert-dialog).
*/
-export const ConfirmDialog: FC = ({
- children,
- overlayCss,
- ...props
-}) => {
- return (
-
-
- {children}
-
- )
-}
+export const ConfirmDialog = Root
type ConfirmDialogContentProps = ComponentProps &
CSSProps & {
@@ -99,20 +85,47 @@ type ConfirmDialogContentProps = ComponentProps &
title?: string
/** Add a description to the content. */
description?: string
+ /** Modify the default styling of the overlay */
+ overlayCss?: CSS
+ /** By default, portals your overlay and content parts into the body, set false to add at dom location. */
+ portalled?: boolean
+ /** Specify a container element to portal the content into. */
+ container?: ComponentProps['container']
}
export const ConfirmDialogContent = forwardRef<
ElementRef,
ConfirmDialogContentProps
->(({ title, description, children, ...props }, forwardedRef) => (
-
- {title && {title}}
- {description && (
- {description}
- )}
- {children}
-
-))
+>(
+ (
+ {
+ title,
+ description,
+ overlayCss,
+ container,
+ portalled = true,
+ children,
+ ...props
+ },
+ forwardedRef
+ ) => (
+ {child}}
+ >
+ <>
+
+
+ {title && {title}}
+ {description && (
+ {description}
+ )}
+ {children}
+
+ >
+
+ )
+)
ConfirmDialogContent.toString = () => `.${StyledContent.className}`
export const ConfirmDialogTrigger = forwardRef<
diff --git a/src/components/Dialog/Dialog.stories.tsx b/src/components/Dialog/Dialog.stories.tsx
index e9f2d5ce..120ce9f4 100644
--- a/src/components/Dialog/Dialog.stories.tsx
+++ b/src/components/Dialog/Dialog.stories.tsx
@@ -1,7 +1,7 @@
import { useBoolean } from '@committed/hooks'
import { action } from '@storybook/addon-actions'
import { Meta, Story } from '@storybook/react'
-import React from 'react'
+import React, { useState } from 'react'
import {
Dialog,
DialogClose,
@@ -10,7 +10,7 @@ import {
DialogTitle,
DialogTrigger,
} from '.'
-import { Button, IconButton, Link, Row, Text } from '../'
+import { Box, Button, Column, IconButton, Link, Row, Text } from '../'
import { Close as CloseIcon } from '../Icons'
export default {
@@ -94,3 +94,254 @@ export const CloseButton: Story = () => {
>
)
}
+
+/**
+ * The dialog is portalled by default. This means the dialog will be appended to the body and so over lay other elements.
+ * In addition, the `CompontsProvider`, by default, isolates the children in their own stacking context so this will not be afected by the z-index of other elements of the UI.
+ *
+ * This example shows how the z-index of other elements in the UI do not affect the dialog.
+ *
+ * This may not be the desired behaviour for your application so these defaults can be overridden, see below.
+ */
+export const Portalled: Story = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+/**
+ * Turning off portalling will mean the zIndex of the dialog will be relative to the zIndex of the element it is appended to.
+ */
+export const zIndex: Story = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+/**
+ * The element the dialog portalls to can be provided, and different effects can be achieved by supplying additional css. For example:
+ */
+export const Container: Story = () => {
+ const [element, setElement] = useState(null)
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This area will now contain the dialog
+
+
+ )
+}
diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx
index 63f0b9c9..ad641e5d 100644
--- a/src/components/Dialog/Dialog.tsx
+++ b/src/components/Dialog/Dialog.tsx
@@ -3,6 +3,7 @@ import {
Content,
Description,
Overlay,
+ Portal,
Root,
Title,
Trigger,
@@ -10,6 +11,7 @@ import {
import React, { ComponentProps, ElementRef, FC, forwardRef } from 'react'
import type { CSSProps } from '../../stitches.config'
import { CSS, styled } from '../../stitches.config'
+import { ConditionalWrapper } from '../../utils'
import { Heading } from '../Heading'
import { IconButton } from '../IconButton'
import { Close as Icon } from '../Icons'
@@ -63,11 +65,6 @@ const StyledIconButton = styled(IconButton, {
top: '$1',
})
-type DialogProps = React.ComponentProps & {
- /** Modify the default styling of the overlay */
- overlayCss?: CSS
-}
-
/**
* The Dialog component can be used to isolate the user from the main window by overlaying
* another window that requires the users attention.
@@ -81,36 +78,55 @@ type DialogProps = React.ComponentProps & {
*
* Based on [Radix Dialog](https://radix-ui.com/primitives/docs/components/dialog).
*/
-export const Dialog: FC = ({ children, overlayCss, ...props }) => {
- return (
-
-
- {children}
-
- )
-}
+export const Dialog = Root
type DialogContentProps = Omit, 'asChild'> &
CSSProps & {
/** Closable, add a standard close icon. */
defaultClose?: boolean
+ /** Modify the default styling of the overlay */
+ overlayCss?: CSS
+ /** By default, portals your overlay and content parts into the body, set false to add at dom location. */
+ portalled?: boolean
+ /** Specify a container element to portal the content into. */
+ container?: ComponentProps['container']
}
export const DialogContent = forwardRef<
ElementRef,
DialogContentProps
->(({ children, defaultClose = true, ...props }, forwardedRef) => (
-
- {defaultClose && (
-
-
-
-
-
- )}
- {children}
-
-))
+>(
+ (
+ {
+ children,
+ overlayCss,
+ container,
+ portalled = true,
+ defaultClose = true,
+ ...props
+ },
+ forwardedRef
+ ) => (
+ {child}}
+ >
+ <>
+
+
+ {defaultClose && (
+
+
+
+
+
+ )}
+ {children}
+
+ >
+
+ )
+)
DialogContent.toString = () => `.${StyledContent.className}`
export const DialogTrigger = forwardRef<
diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx
index 97530186..ca9ac6bc 100644
--- a/src/components/Drawer/Drawer.tsx
+++ b/src/components/Drawer/Drawer.tsx
@@ -1,12 +1,8 @@
-import { Close, Content, Root } from '@radix-ui/react-dialog'
+import { Close, Content, Portal, Root } from '@radix-ui/react-dialog'
import React, { ComponentProps, ElementRef, forwardRef } from 'react'
-import type {
- ChildProps,
- CSS,
- CSSProps,
- VariantProps,
-} from '../../stitches.config'
+import type { CSS, CSSProps, VariantProps } from '../../stitches.config'
import { keyframes, styled } from '../../stitches.config'
+import { ConditionalWrapper } from '../../utils'
import { DialogClose, DialogTrigger, StyledOverlay } from '../Dialog/Dialog'
import { IconButton } from '../IconButton'
import { Close as Icon } from '../Icons'
@@ -90,34 +86,54 @@ type DrawerContentProps = Omit, 'asChild'> &
DrawerContentVariants & {
/** Closable, add a standard close icon. */
defaultClose?: boolean
+ /** Modify the default styling of the overlay */
+ overlayCss?: CSS
+ /** By default, portals your overlay and content parts into the body, set false to add at dom location. */
+ portalled?: boolean
+ /** Specify a container element to portal the content into. */
+ container?: ComponentProps['container']
}
export const DrawerContent = forwardRef<
ElementRef,
DrawerContentProps
->(({ defaultClose, children, ...props }, forwardedRef) => (
-
- {children}
- {defaultClose && (
-
-
-
-
-
- )}
-
-))
+>(
+ (
+ {
+ defaultClose,
+ children,
+ overlayCss,
+ container,
+ portalled = true,
+ ...props
+ },
+ forwardedRef
+ ) => (
+ {child}}
+ >
+ <>
+
+
+ {children}
+ {defaultClose && (
+
+
+
+
+
+ )}
+
+ >
+
+ )
+)
DrawerContent.toString = () => `.${StyledContent.className}`
export const DrawerTrigger = DialogTrigger
export const DrawerClose = DialogClose
-type DrawerProps = React.ComponentProps &
- ChildProps & {
- /** Modify the default styling of the overlay */
- overlayCss?: CSS
- }
-
/**
* The Drawer component can be used to overlay a panel from any side.
*
@@ -127,15 +143,4 @@ type DrawerProps = React.ComponentProps &
*
* Based on [Radix Dialog](https://radix-ui.com/primitives/docs/components/dialog).
*/
-export const Drawer: React.FC = ({
- children,
- overlayCss,
- ...props
-}) => {
- return (
-
-
- {children}
-
- )
-}
+export const Drawer = Root