DialogTrigger
The DialogTrigger serves as a wrapper around a Dialog and its associated trigger, linking the Dialog's open state with the trigger's press state. Additionally, it allows you to customize the type and positioning of the Dialog.
install | yarn add @react-spectrum/dialog |
---|---|
version | 3.0.0-alpha.1 |
usage | import {DialogTrigger} from '@react-spectrum/dialog' |
Example#
<DialogTrigger type="popover">
<ActionButton>Disk Status</ActionButton>
<Dialog>
<Heading>C://</Heading>
<Divider />
<Content>
<Text>
50% disk space remaining.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Content#
The DialogTrigger accepts exactly two children: the element which triggers the opening of the Dialog and the Dialog itself. The trigger must be the first child passed into the DialogTrigger and should be an element that supports press events.
If your Dialog has buttons within it that should close the Dialog when pressed,
you must wrap the Dialog in a function in order to properly propagate the DialogTrigger's
close
state setter to the Dialog's children. Dialogs that do not contain such interactive
elements can simply provide the Dialog component as is to the DialogTrigger as its second child.
The example below demonstrates how to pass the DialogTrigger's close
state setter to the Dialog's buttons.
<DialogTrigger>
<ActionButton>Checkout</ActionButton>
(close) => (
<Dialog>
<Heading>Confirm checkout?</Heading>
<Divider />
<Content>
<Text>
You have 5 items in your cart. Proceed to checkout?
</Text>
</Content>
<ButtonGroup>
<Button variant="secondary" onPress= close>Cancel</Button>
<Button variant="cta" onPress= close autoFocus>Confirm</Button>
</ButtonGroup>
</Dialog>
)
</DialogTrigger>
Dialog types#
By providing a type
prop, you can specify the type of Dialog that is rendered
by your DialogTrigger. Note that pressing the esc
key will close the Dialog regardless
of its type
.
Modal#
Modal Dialogs create an underlay that blocks access to the underlying user interface until the Dialog is closed. Sizing options can be found in the Dialog page. Focus is trapped inside the Modal as per the accessibility guidelines laid out by W3C.
<DialogTrigger type="modal">
<ActionButton>Unlink</ActionButton>
(close) => (
<Dialog>
<Heading>Unlinking email</Heading>
<Divider />
<Content>
<Text>
This will unlink your email from your profile "TestUser". Are you sure?
</Text>
</Content>
<ButtonGroup>
<Button variant="secondary" onPress= close>Cancel</Button>
<Button variant="cta" onPress= close autoFocus>Confirm</Button>
</ButtonGroup>
</Dialog>
)
</DialogTrigger>
Popover#
If you want a Dialog that doesn't block access to the user interface, consider using a Popover Dialog. See Dialog placement for how you can customize the positioning.
<DialogTrigger type="popover">
<ActionButton>Info</ActionButton>
<Dialog>
<Heading>Version Info</Heading>
<Divider />
<Content>
<Text>
Version 1.0.0, Copyright 2020
</Text>
</Content>
</Dialog>
</DialogTrigger>
Tray#
Tray Dialogs are typically used to portray information on mobile device or smaller screens.
<DialogTrigger type="tray">
<ActionButton>Check Messages</ActionButton>
<Dialog>
<Heading>New Messages</Heading>
<Divider />
<Content>
<Text>
You have 5 new messages.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Fullscreen#
Fullscreen Dialogs are a fullscreen variant of the Modal Dialog, only revealing a small portion of the page behind the underlay. Use this variant for more complex workflow that do not fit on the available Modal Dialog sizes.
<DialogTrigger type="fullscreen">
<ActionButton>See Details</ActionButton>
(close) => (
<Dialog>
<Heading>Package details</Heading>
<Divider />
<Content>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sit amet tristique risus. In sit amet suscipit lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In condimentum imperdiet metus non condimentum. Duis eu velit et quam accumsan tempus at id velit. Duis elementum elementum purus, id tempus mauris posuere a. Nunc vestibulum sapien pellentesque lectus commodo ornare.
</Text>
</Content>
<ButtonGroup>
<Button variant="secondary" onPress= close>Cancel</Button>
<Button variant="cta" onPress= close autoFocus>Buy</Button>
</ButtonGroup>
</Dialog>
)
</DialogTrigger>
Fullscreen takeover#
Fullscreen takeover Dialogs are similar to the fullscreen variant except that the background covers the entire screen. This variant should be reserved for workflows where displaying a second dialog on top of the first one is to be expected.
<DialogTrigger type="fullscreenTakeover">
<ActionButton>Register</ActionButton>
(close) => (
<Dialog>
<Heading>Register a new account</Heading>
<Divider />
<Content>
<Form>
<TextField label="Name" />
<TextField label="Email address" />
<Checkbox>Make profile private</Checkbox>
</Form>
<DialogTrigger isDismissable type="modal">
<ActionButton>Privacy Policy</ActionButton>
<Dialog>
<Heading>Privacy Policy</Heading>
<Divider />
<Content>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sit amet tristique risus. In sit amet suscipit lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In condimentum imperdiet metus non condimentum. Duis eu velit et quam accumsan tempus at id velit. Duis elementum elementum purus, id tempus mauris posuere a. Nunc vestibulum sapien pellentesque lectus commodo ornare.
</Text>
</Content>
</Dialog>
</DialogTrigger>
</Content>
<ButtonGroup>
<Button variant="secondary" onPress= close>Cancel</Button>
<Button variant="cta" onPress= close autoFocus>Confirm</Button>
</ButtonGroup>
</Dialog>
)
</DialogTrigger>
Dismissable#
If your Modal Dialog doesn't require the user to make a confirmation, you can set isDismissable
on the DialogTrigger. This adds an exit button that the user can click to dismiss the Dialog.
<DialogTrigger isDismissable type="modal">
<ActionButton>User Status</ActionButton>
<Dialog>
<Heading>Status: Bob</Heading>
<Divider />
<Content>
<Text>
Last Login: December 12, 1989
</Text>
</Content>
</Dialog>
</DialogTrigger>
Mobile type#
The mobileType
prop allows you to specify what kind of Dialog should be displayed when viewed in
mobile devices or smaller viewports. Note that Popover type Dialogs are not supported as
a valid mobileType
and will default to the Modal type if specified.
The example below renders as a Popover in desktop but switches to a Tray in mobile.
<DialogTrigger type="popover" mobileType="tray">
<ActionButton>Info</ActionButton>
<Dialog>
<Heading>Version Info</Heading>
<Divider />
<Content>
<Text>
Version 1.0.0, Copyright 2020
</Text>
</Content>
</Dialog>
</DialogTrigger>
Dialog placement#
Popover Dialogs support a variety of placement options since they do not take over the user interface like Modal or Tray Dialogs.
Dialog anchor#
By default, Popover Dialogs anchor themselves to their associated trigger. This can be overridden by providing
a separate ref to the targetRef
prop.
function Example() {
let ref = ReactuseRef()
return (
<div>
<DialogTrigger type="popover" targetRef= ref>
<ActionButton>Trigger</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover anchored to the span.
</Text>
</Content>
</Dialog>
</DialogTrigger>
<span ref= ref style={marginInlineStart: '200px' width: '50px'}>Popover appears over here</span>
</div>
);
}
Placement#
The Popover Dialog's placement with respect to its anchor element can be adjusted using the placement
prop. See the props table below for a full list of available placement combinations.
<DialogTrigger type="popover" placement="right top">
<ActionButton>Trigger</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover placed to the right of its trigger and offset so the arrow is at the top of the dialog.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Offset and cross offset#
The Popover Dialog's offset with respect to its anchor element can be adjusted using the offset
and
crossOffset
props. The offset
prop controls the spacing applied along the main axis between the element and its
anchor element whereas the crossOffset
prop handles the spacing applied along the cross axis.
<DialogTrigger type="popover" placement="top" offset=50>
<ActionButton>Trigger</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover offset by an additional 50px above the trigger.
</Text>
</Content>
</Dialog>
</DialogTrigger>
<DialogTrigger type="popover" placement="top" crossOffset=100>
<ActionButton>Trigger</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover offset by an additional 100px to the right of the trigger.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Flipping#
Applying shouldFlip
to the DialogTrigger makes the Popover Dialog attempt to flip
on its main axis in situations where the original placement would cause it to render out of view.
<DialogTrigger type="popover" placement="bottom" shouldFlip>
<ActionButton>Trigger with shouldFlip</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover that will flip if it can't fully render below the button.
</Text>
</Content>
</Dialog>
</DialogTrigger>
<DialogTrigger type="popover" placement="bottom" shouldFlip=false>
<ActionButton>Trigger with shouldFlip=false</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover that won't flip if it can't fully render below the button.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Container padding#
You can control the minimum padding required between the Popover Dialog and the
surrounding container via the containerPadding
prop. This affects the positioning
breakpoints that determine when the Dialog will attempt to flip.
The example below will flip the Dialog from above the trigger button to below the trigger button if the Dialog cannot render fully while maintaining 50px of padding between itself and the top of the browser.
<DialogTrigger type="popover" shouldFlip placement="top" containerPadding=50>
<ActionButton>Trigger</ActionButton>
<Dialog>
<Heading>The Heading</Heading>
<Divider />
<Content>
<Text>
This is a popover.
</Text>
</Content>
</Dialog>
</DialogTrigger>
Events#
DialogTrigger accepts an onOpenChange
handler which is triggered whenever the Dialog is opened or closed.
The example below uses onOpenChange
to update a separate span
element with the current open state of the
Dialog.
function Example() {
let [state setState] = ReactuseState(false);
return (
<div>
<DialogTrigger type="popover" placement="top" onOpenChange=(isOpen) => setState(isOpen)>
<ActionButton>Whispers</ActionButton>
<Dialog>
<Heading>Whispers and DMs</Heading>
<Divider />
<Content>
<Text>
You have 0 new messages.
</Text>
</Content>
</Dialog>
</DialogTrigger>
<span style={'margin-left': '8px'}>Current open state: statetoString()</span>
</div>
);
}
Props#
Name | Type | Default | Description |
children |
| — | The Dialog and its trigger element. See the DialogTrigger Content section for more information on what to provide as children. |
type | 'modal'
| 'popover'
| 'tray'
| 'fullscreen'
| 'fullscreenTakeover' | "modal" | The type of Dialog that should be rendered. See the DialogTrigger types section for an explaination on each. |
mobileType | 'modal'
| 'tray'
| 'fullscreen'
| 'fullscreenTakeover' | — | The type of Dialog that should be rendered when on a mobile device. See DialogTrigger types section for an explaination on each. |
hideArrow | boolean | "false" | Whether a popover type Dialog's arrow should be hidden. |
targetRef | RefObject<HTMLElement> | — | The ref of the element the Dialog should visually attach itself to. Defaults to the trigger button if not defined. |
isDismissable | boolean | — | Whether a modal type Dialog should be dismissable. |
isOpen | boolean | — | Whether the Dialog is open by default (controlled). |
defaultOpen | boolean | — | Whether the Dialog is open by default (uncontrolled). |
placement | Placement | "bottom" | The placement of the element with respect to its anchor element. |
containerPadding | number | "12px" | The placement padding that should be applied between the element and its surrounding container. |
offset | number | "0px" | The additional offset applied along the main axis between the element and its anchor element. |
crossOffset | number | "0px" | The additional offset applied along the cross axis between the element and its anchor element. |
shouldFlip | boolean | "true" | Whether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely. |
Events
Name | Type | Default | Description |
onOpenChange | (isOpen: boolean) => void | — | Handler that is called when the Dialog's open state changes. |
Visual options#
Hide arrow#
The hideArrow
prop hides the Popover Dialog's arrow if applied.
<DialogTrigger hideArrow type="popover">
<ActionButton>See Notifications</ActionButton>
<Dialog>
<Heading>Notifications</Heading>
<Divider />
<Content>
<Text>
You have no new notifications.
</Text>
</Content>
</Dialog>
</DialogTrigger>
isOpen and defaultOpen#
The isOpen
and defaultOpen
props control whether the Dialog is open by default.
They apply controlled and uncontrolled behavior on the DialogTrigger respectively.
'bottom'
| 'bottom left'
| 'bottom right'
| 'bottom start'
| 'bottom end'
| 'top'
| 'top left'
| 'top right'
| 'top start'
| 'top end'
| 'left'
| 'left top'
| 'left bottom'
| 'start'
| 'start top'
| 'start bottom'
| 'right'
| 'right top'
| 'right bottom'
| 'end'
| 'end top'
| 'end bottom'