@cdx-ui/components 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +24 -0
- package/README.md +2 -0
- package/coverage.svg +1 -1
- package/docs/components/accordion/content.md +538 -0
- package/docs/components/alert/content.md +203 -0
- package/docs/components/alert-dialog/content.md +319 -0
- package/docs/components/avatar/content.md +368 -0
- package/docs/components/avatar/design.md +202 -0
- package/docs/components/badge/content.md +235 -0
- package/docs/components/bottom-sheet/content.md +351 -0
- package/docs/components/bottom-sheet/design.md +249 -0
- package/docs/components/breadcrumbs/content.md +169 -0
- package/docs/components/button/content.md +398 -0
- package/docs/components/button/design.md +258 -0
- package/docs/components/calendar/content.md +137 -0
- package/docs/components/card/content.md +283 -0
- package/docs/components/carousel/content.md +416 -0
- package/docs/components/checkbox/content.md +236 -0
- package/docs/components/checkbox/design.md +214 -0
- package/docs/components/chip/content.md +439 -0
- package/docs/components/chip/design.md +418 -0
- package/docs/components/color-area/content.md +86 -0
- package/docs/components/color-field/content.md +134 -0
- package/docs/components/color-picker/content.md +208 -0
- package/docs/components/color-slider/content.md +120 -0
- package/docs/components/color-swatch/content.md +64 -0
- package/docs/components/color-swatch-picker/content.md +126 -0
- package/docs/components/content.md +97 -0
- package/docs/components/currency-input/content.md +205 -0
- package/docs/components/data-grid/content.md +522 -0
- package/docs/components/date-field/content.md +147 -0
- package/docs/components/date-picker/content.md +360 -0
- package/docs/components/date-range-picker/content.md +301 -0
- package/docs/components/date-time-field/content.md +157 -0
- package/docs/components/date-time-list/content.md +105 -0
- package/docs/components/date-time-picker/content.md +272 -0
- package/docs/components/dialog/content.md +436 -0
- package/docs/components/dialog/design.md +192 -0
- package/docs/components/divider/content.md +112 -0
- package/docs/components/drawer/content.md +436 -0
- package/docs/components/field/content.md +277 -0
- package/docs/components/form/content.md +62 -0
- package/docs/components/full-screen-dialog/content.md +881 -0
- package/docs/components/heading/content.md +55 -0
- package/docs/components/icon/content.md +190 -0
- package/docs/components/icon-button/content.md +200 -0
- package/docs/components/icon-button/design.md +165 -0
- package/docs/components/image/content.md +104 -0
- package/docs/components/input/content.md +281 -0
- package/docs/components/input/design.md +389 -0
- package/docs/components/insurance-disclosure/content.md +103 -0
- package/docs/components/link/content.md +180 -0
- package/docs/components/link/design.md +160 -0
- package/docs/components/list-item/content.md +695 -0
- package/docs/components/menu/content.md +1247 -0
- package/docs/components/month-calendar/content.md +84 -0
- package/docs/components/month-picker/content.md +273 -0
- package/docs/components/otp-input/content.md +327 -0
- package/docs/components/progress-bar/content.md +87 -0
- package/docs/components/progress-circle/content.md +176 -0
- package/docs/components/progress-segmented/content.md +119 -0
- package/docs/components/quick-action-button/content.md +154 -0
- package/docs/components/quick-action-button/design.md +170 -0
- package/docs/components/radio/content.md +224 -0
- package/docs/components/range-calendar/content.md +123 -0
- package/docs/components/reorderable-list/content.md +309 -0
- package/docs/components/scoped-theme/content.md +47 -0
- package/docs/components/scroll-shadow/content.md +686 -0
- package/docs/components/select/content.md +400 -0
- package/docs/components/select/design.md +301 -0
- package/docs/components/skeleton/content.md +103 -0
- package/docs/components/slider/content.md +423 -0
- package/docs/components/stack/content.md +161 -0
- package/docs/components/switch/content.md +150 -0
- package/docs/components/switch/design.md +208 -0
- package/docs/components/table/content.md +548 -0
- package/docs/components/table-pagination/content.md +177 -0
- package/docs/components/tabs/content.md +292 -0
- package/docs/components/text/content.md +142 -0
- package/docs/components/textarea/content.md +222 -0
- package/docs/components/tile/content.md +703 -0
- package/docs/components/toast/content.md +456 -0
- package/docs/components/toggle-button/content.md +505 -0
- package/docs/components/tooltip/content.md +350 -0
- package/docs/components/virtualized-list/content.md +250 -0
- package/docs/foundations/content.md +9 -0
- package/docs/foundations/expressions/icons/content.md +19 -0
- package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
- package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
- package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
- package/docs/getting-started/content.md +4 -0
- package/docs/getting-started/designers/content.md +13 -0
- package/docs/getting-started/designers/design.md +13 -0
- package/docs/getting-started/developers/accessibility/content.md +31 -0
- package/docs/getting-started/developers/content.md +612 -0
- package/docs/getting-started/developers/styling/content.md +193 -0
- package/docs/llms.txt +193 -0
- package/docs/patterns/account-management/content.md +75 -0
- package/docs/patterns/authentication/content.md +52 -0
- package/docs/patterns/content.md +10 -0
- package/docs/patterns/dashboard/content.md +39 -0
- package/docs/patterns/empty-states/content.md +31 -0
- package/docs/patterns/forms/content.md +109 -0
- package/docs/patterns/onboarding/content.md +69 -0
- package/docs/patterns/payments/content.md +35 -0
- package/docs/patterns/settings/content.md +72 -0
- package/docs/resources/content.md +9 -0
- package/docs/resources/figma-libraries/content.md +42 -0
- package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
- package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
- package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
- package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
- package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
- package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
- package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
- package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
- package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
- package/docs/resources/release-notes/0.1.1/content.md +23 -0
- package/docs/resources/release-notes/0.1.2/content.md +7 -0
- package/docs/resources/release-notes/0.1.4/content.md +19 -0
- package/docs/resources/release-notes/0.1.5/content.md +7 -0
- package/docs/resources/release-notes/0.1.6/content.md +7 -0
- package/docs/resources/release-notes/0.2.0/content.md +31 -0
- package/docs/resources/release-notes/0.2.1/content.md +17 -0
- package/docs/resources/release-notes/0.2.2/content.md +11 -0
- package/docs/resources/release-notes/0.2.3/content.md +15 -0
- package/docs/resources/release-notes/content.md +64 -0
- package/lib/commonjs/components/DateTimeList/index.js +11 -26
- package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/styles.js +2 -12
- package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
- package/lib/commonjs/components/ListItem/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/styles.js +2 -1
- package/lib/commonjs/components/ListItem/styles.js.map +1 -1
- package/lib/commonjs/components/Menu/index.js +11 -5
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
- package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/index.js +171 -44
- package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
- package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Tabs/index.js +16 -23
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -10
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/module/components/DateTimeList/index.js +12 -27
- package/lib/module/components/DateTimeList/index.js.map +1 -1
- package/lib/module/components/DateTimeList/styles.js +1 -11
- package/lib/module/components/DateTimeList/styles.js.map +1 -1
- package/lib/module/components/ListItem/index.js.map +1 -1
- package/lib/module/components/ListItem/styles.js +2 -1
- package/lib/module/components/ListItem/styles.js.map +1 -1
- package/lib/module/components/Menu/index.js +11 -5
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/gradient.js +24 -0
- package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/module/components/ScrollShadow/index.js +170 -48
- package/lib/module/components/ScrollShadow/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/styles.js +57 -13
- package/lib/module/components/ScrollShadow/styles.js.map +1 -1
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Tabs/index.js +17 -24
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +0 -9
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
- package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/index.d.ts +5 -4
- package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
- package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
- package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
- package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/styles.d.ts +0 -7
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
- package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
- package/package.json +8 -6
- package/src/components/DateTimeList/index.tsx +13 -26
- package/src/components/DateTimeList/styles.ts +1 -10
- package/src/components/ListItem/index.tsx +5 -4
- package/src/components/ListItem/styles.ts +1 -0
- package/src/components/Menu/index.tsx +11 -8
- package/src/components/ScrollShadow/gradient.ts +48 -0
- package/src/components/ScrollShadow/index.tsx +300 -69
- package/src/components/ScrollShadow/styles.ts +45 -28
- package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
- package/src/components/Tabs/index.tsx +25 -35
- package/src/components/Tabs/styles.ts +0 -9
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Dialog (https://design.candescent.com/docs/components/dialog)
|
|
2
|
+
|
|
3
|
+
Resources: [Figma](https://www.figma.com/design/0lEVMVlLj8ZS4fgItr3pmd/%E2%AD%90-Forge-DS--Components?node-id=4331-429), [Storybook](https://victorious-wave-0daadf00f.6.azurestaticapps.net/?path=/story/overlays-dialog--default)
|
|
4
|
+
|
|
5
|
+
A compound modal with a backdrop, focus trapping, keyboard dismissal, and animated presentation.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button, Dialog } from '@cdx-ui/components';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
>
|
|
13
|
+
> **Portal host**
|
|
14
|
+
>
|
|
15
|
+
> `ForgeDesignProvider` mounts the shared portal host automatically. If you do not use the
|
|
16
|
+
> provider, mount one `PortalHost` from `@cdx-ui/primitives` near your app root. On web, to keep
|
|
17
|
+
> the dialog inside an embedded surface such as a device preview, wrap that surface in
|
|
18
|
+
> `PortalProvider` and give its `PortalHost` a `container` element inside it; the fixed overlay
|
|
19
|
+
> stays inside only when an ancestor of the container sets a `transform`.
|
|
20
|
+
|
|
21
|
+
## Basic
|
|
22
|
+
|
|
23
|
+
The popup mounts in the app-level portal and covers the viewport, while the trigger remains at its original position.
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { useState } from 'react';
|
|
27
|
+
import { Button, Dialog } from '@cdx-ui/components';
|
|
28
|
+
|
|
29
|
+
export default function DialogBasicDemo() {
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Dialog isOpen={open} onOpenChange={setOpen}>
|
|
34
|
+
<Dialog.Trigger asChild>
|
|
35
|
+
<Button>
|
|
36
|
+
<Button.Label>Open dialog</Button.Label>
|
|
37
|
+
</Button>
|
|
38
|
+
</Dialog.Trigger>
|
|
39
|
+
<Dialog.Popup>
|
|
40
|
+
<Dialog.Header>
|
|
41
|
+
<Dialog.Title>Confirm transfer</Dialog.Title>
|
|
42
|
+
<Dialog.Description>
|
|
43
|
+
Review the details before submitting this transfer.
|
|
44
|
+
</Dialog.Description>
|
|
45
|
+
<Dialog.Close />
|
|
46
|
+
</Dialog.Header>
|
|
47
|
+
<Dialog.Content>
|
|
48
|
+
<Dialog.Text>$250.00 will be sent from Checking to Savings.</Dialog.Text>
|
|
49
|
+
</Dialog.Content>
|
|
50
|
+
<Dialog.Footer>
|
|
51
|
+
<Button variant="outline" onPress={() => setOpen(false)}>
|
|
52
|
+
<Button.Label>Cancel</Button.Label>
|
|
53
|
+
</Button>
|
|
54
|
+
<Button onPress={() => setOpen(false)}>
|
|
55
|
+
<Button.Label>Confirm</Button.Label>
|
|
56
|
+
</Button>
|
|
57
|
+
</Dialog.Footer>
|
|
58
|
+
</Dialog.Popup>
|
|
59
|
+
</Dialog>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Title severities
|
|
65
|
+
|
|
66
|
+
Set `severity` on `Dialog.Title` to add a status icon and semantic color treatment.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
import { useState } from 'react';
|
|
70
|
+
import { Button, Dialog, HStack, type DialogTitleSeverity } from '@cdx-ui/components';
|
|
71
|
+
|
|
72
|
+
interface DialogScenario {
|
|
73
|
+
severity: DialogTitleSeverity;
|
|
74
|
+
color: 'action' | 'danger' | 'warning' | 'success';
|
|
75
|
+
trigger: string;
|
|
76
|
+
title: string;
|
|
77
|
+
description: string;
|
|
78
|
+
body: string;
|
|
79
|
+
action: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const scenarios: DialogScenario[] = [
|
|
83
|
+
{
|
|
84
|
+
severity: 'neutral',
|
|
85
|
+
color: 'action',
|
|
86
|
+
trigger: 'Review transfer',
|
|
87
|
+
title: 'Confirm transfer',
|
|
88
|
+
description: 'From Everyday Checking to High-Yield Savings',
|
|
89
|
+
body: 'You are moving $500.00 today. Your available checking balance after the transfer will be $1,842.36.',
|
|
90
|
+
action: 'Transfer $500',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
severity: 'danger',
|
|
94
|
+
color: 'danger',
|
|
95
|
+
trigger: 'Report stolen card',
|
|
96
|
+
title: 'Report card as stolen?',
|
|
97
|
+
description: 'Debit card ending in 4821 will be permanently blocked.',
|
|
98
|
+
body: 'Pending transactions will continue to process. A replacement card will arrive at your mailing address in 5–7 business days.',
|
|
99
|
+
action: 'Report stolen',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
severity: 'warning',
|
|
103
|
+
color: 'warning',
|
|
104
|
+
trigger: 'Review low balance',
|
|
105
|
+
title: 'Balance may fall below $100',
|
|
106
|
+
description: 'A scheduled payment could leave your checking account low.',
|
|
107
|
+
body: 'Your $1,250.00 mortgage payment is scheduled for tomorrow. Based on your current balance, approximately $74.18 will remain.',
|
|
108
|
+
action: 'View account',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
severity: 'success',
|
|
112
|
+
color: 'success',
|
|
113
|
+
trigger: 'View deposit status',
|
|
114
|
+
title: 'Mobile deposit submitted',
|
|
115
|
+
description: 'Your $725.00 check was accepted for review.',
|
|
116
|
+
body: 'Up to $225.00 may be available today. We will notify you when the remaining funds are available.',
|
|
117
|
+
action: 'Done',
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
function SeverityDialog({ scenario }: { scenario: DialogScenario }) {
|
|
122
|
+
const [open, setOpen] = useState(false);
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Dialog isOpen={open} onOpenChange={setOpen}>
|
|
126
|
+
<Dialog.Trigger asChild>
|
|
127
|
+
<Button variant="outline" color={scenario.color}>
|
|
128
|
+
<Button.Label>{scenario.trigger}</Button.Label>
|
|
129
|
+
</Button>
|
|
130
|
+
</Dialog.Trigger>
|
|
131
|
+
<Dialog.Popup>
|
|
132
|
+
<Dialog.Header>
|
|
133
|
+
<Dialog.Title severity={scenario.severity}>{scenario.title}</Dialog.Title>
|
|
134
|
+
<Dialog.Description>{scenario.description}</Dialog.Description>
|
|
135
|
+
<Dialog.Close />
|
|
136
|
+
</Dialog.Header>
|
|
137
|
+
<Dialog.Content>
|
|
138
|
+
<Dialog.Text>{scenario.body}</Dialog.Text>
|
|
139
|
+
</Dialog.Content>
|
|
140
|
+
<Dialog.Footer>
|
|
141
|
+
<Button color={scenario.color} onPress={() => setOpen(false)}>
|
|
142
|
+
<Button.Label>{scenario.action}</Button.Label>
|
|
143
|
+
</Button>
|
|
144
|
+
</Dialog.Footer>
|
|
145
|
+
</Dialog.Popup>
|
|
146
|
+
</Dialog>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export default function DialogTitleSeveritiesDemo() {
|
|
151
|
+
return (
|
|
152
|
+
<HStack space="sm" className="flex-wrap justify-center">
|
|
153
|
+
{scenarios.map((scenario) => (
|
|
154
|
+
<SeverityDialog key={scenario.severity} scenario={scenario} />
|
|
155
|
+
))}
|
|
156
|
+
</HStack>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Scrollable content
|
|
162
|
+
|
|
163
|
+
`Dialog.Content` is not scrollable by default. Wrap long content in a `ScrollView` so the header and footer remain visible. On native, set `keyboardShouldPersistTaps="handled"` so a focused field keeps the keyboard open while the body scrolls.
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
import { useState } from 'react';
|
|
167
|
+
import { Button, Dialog, ScrollView } from '@cdx-ui/components';
|
|
168
|
+
|
|
169
|
+
const terms = [
|
|
170
|
+
'Authorization. You authorize us to debit the selected funding account and credit the destination account for the amount shown in the transfer review.',
|
|
171
|
+
'Account details. You are responsible for confirming the routing number, account number, recipient name, and transfer amount before submitting the transfer.',
|
|
172
|
+
'Processing times. Transfers submitted after the daily cutoff, on weekends, or on bank holidays may begin processing on the next business day.',
|
|
173
|
+
'Available balance. Scheduled and recurring transfers are completed only when the funding account has enough available funds on the processing date.',
|
|
174
|
+
'Fees. Your review screen displays any transfer fee before submission. The receiving financial institution may charge additional fees.',
|
|
175
|
+
'Cancellation. A pending transfer can be canceled from Transfer Activity until processing begins. Completed transfers cannot be canceled.',
|
|
176
|
+
'Returns. A transfer may be returned if the destination account is closed, restricted, or entered incorrectly. Any returned funds will be credited back to the funding account.',
|
|
177
|
+
'Limits. Daily and monthly transfer limits apply across all eligible accounts and may change based on account history and security reviews.',
|
|
178
|
+
'Notifications. We will send a confirmation when the transfer is submitted and another notification if its status changes.',
|
|
179
|
+
'Records. Keep the confirmation number for your records and review your account activity to verify that the transfer was completed as expected.',
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
export default function DialogScrollableContentDemo() {
|
|
183
|
+
const [open, setOpen] = useState(false);
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<Dialog isOpen={open} onOpenChange={setOpen}>
|
|
187
|
+
<Dialog.Trigger asChild>
|
|
188
|
+
<Button variant="outline">
|
|
189
|
+
<Button.Label>Review transfer agreement</Button.Label>
|
|
190
|
+
</Button>
|
|
191
|
+
</Dialog.Trigger>
|
|
192
|
+
<Dialog.Popup>
|
|
193
|
+
<Dialog.Header>
|
|
194
|
+
<Dialog.Title>Electronic transfer agreement</Dialog.Title>
|
|
195
|
+
<Dialog.Description>
|
|
196
|
+
Review the complete agreement before linking an external account.
|
|
197
|
+
</Dialog.Description>
|
|
198
|
+
<Dialog.Close />
|
|
199
|
+
</Dialog.Header>
|
|
200
|
+
<Dialog.Content>
|
|
201
|
+
<ScrollView
|
|
202
|
+
className="h-60"
|
|
203
|
+
contentContainerClassName="gap-3"
|
|
204
|
+
keyboardShouldPersistTaps="handled"
|
|
205
|
+
>
|
|
206
|
+
{terms.map((term, index) => (
|
|
207
|
+
<Dialog.Text key={term}>
|
|
208
|
+
{index + 1}. {term}
|
|
209
|
+
</Dialog.Text>
|
|
210
|
+
))}
|
|
211
|
+
</ScrollView>
|
|
212
|
+
</Dialog.Content>
|
|
213
|
+
<Dialog.Footer>
|
|
214
|
+
<Button variant="outline" onPress={() => setOpen(false)}>
|
|
215
|
+
<Button.Label>Not now</Button.Label>
|
|
216
|
+
</Button>
|
|
217
|
+
<Button onPress={() => setOpen(false)}>
|
|
218
|
+
<Button.Label>Agree and continue</Button.Label>
|
|
219
|
+
</Button>
|
|
220
|
+
</Dialog.Footer>
|
|
221
|
+
</Dialog.Popup>
|
|
222
|
+
</Dialog>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Required action
|
|
228
|
+
|
|
229
|
+
Disable backdrop and Escape-key dismissal when the user must explicitly respond. Omit `Dialog.Close` so no close button is shown. Escape dismissal is web only; Android's hardware back button still closes the dialog.
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { useState } from 'react';
|
|
233
|
+
import { Button, Dialog } from '@cdx-ui/components';
|
|
234
|
+
|
|
235
|
+
export default function DialogNonDismissibleDemo() {
|
|
236
|
+
const [open, setOpen] = useState(false);
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<Dialog isOpen={open} onOpenChange={setOpen} closeOnBackdropPress={false} closeOnEscKey={false}>
|
|
240
|
+
<Dialog.Trigger asChild>
|
|
241
|
+
<Button variant="outline">
|
|
242
|
+
<Button.Label>Open required action</Button.Label>
|
|
243
|
+
</Button>
|
|
244
|
+
</Dialog.Trigger>
|
|
245
|
+
<Dialog.Popup>
|
|
246
|
+
<Dialog.Header>
|
|
247
|
+
<Dialog.Title severity="warning">Session expired</Dialog.Title>
|
|
248
|
+
<Dialog.Description>This dialog requires an explicit response.</Dialog.Description>
|
|
249
|
+
</Dialog.Header>
|
|
250
|
+
<Dialog.Content>
|
|
251
|
+
<Dialog.Text>Sign in again to continue using online banking.</Dialog.Text>
|
|
252
|
+
</Dialog.Content>
|
|
253
|
+
<Dialog.Footer>
|
|
254
|
+
<Button onPress={() => setOpen(false)}>
|
|
255
|
+
<Button.Label>Sign in</Button.Label>
|
|
256
|
+
</Button>
|
|
257
|
+
</Dialog.Footer>
|
|
258
|
+
</Dialog.Popup>
|
|
259
|
+
</Dialog>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## API
|
|
265
|
+
|
|
266
|
+
### Dialog
|
|
267
|
+
|
|
268
|
+
Controls open state, dismissal behavior, size, and content padding for the compound component.
|
|
269
|
+
|
|
270
|
+
| Prop | Type | Default | Description |
|
|
271
|
+
| --- | --- | --- | --- |
|
|
272
|
+
| `className?` | `string` | — | — |
|
|
273
|
+
| `children` | `ReactNode` | — | — |
|
|
274
|
+
| `role?` | `'dialog' \| 'alertdialog'` | — | — |
|
|
275
|
+
| `size?` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' \| null` | — | — |
|
|
276
|
+
| `animation?` | `'disable-all'` | — | Pass `'disable-all'` to turn off enter/exit and swipe animations for the popup and overlay. |
|
|
277
|
+
| `isOpen?` | `boolean` | — | Controlled open state. Use `defaultOpen` for the uncontrolled variant. |
|
|
278
|
+
| `onOpenChange?` | `((open: boolean) => void)` | — | — |
|
|
279
|
+
| `defaultOpen?` | `boolean` | — | — |
|
|
280
|
+
| `closeOnBackdropPress?` | `boolean` | — | — |
|
|
281
|
+
| `closeOnEscKey?` | `boolean` | `true` | Close when Escape is pressed. Web only. When `false`, an open dialog still consumes Escape while topmost, so overlays beneath it stay open. Android's hardware back button dismisses independently of this prop. |
|
|
282
|
+
| `fullBleed?` | `boolean` | `false` | Removes horizontal padding from `Dialog.Content`. |
|
|
283
|
+
|
|
284
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props), except `children` and `role`.
|
|
285
|
+
|
|
286
|
+
## Sub-components
|
|
287
|
+
|
|
288
|
+
### Dialog.Trigger
|
|
289
|
+
|
|
290
|
+
Opens the dialog when pressed. Use `asChild` to merge trigger behavior onto a `Button` or another pressable element.
|
|
291
|
+
|
|
292
|
+
| Prop | Type | Default | Description |
|
|
293
|
+
| --- | --- | --- | --- |
|
|
294
|
+
| `className?` | `string` | — | — |
|
|
295
|
+
| `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
|
|
296
|
+
| `asChild?` | `boolean` | — | — |
|
|
297
|
+
|
|
298
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
299
|
+
|
|
300
|
+
### Dialog.Popup
|
|
301
|
+
|
|
302
|
+
The modal panel, backdrop, and portal composition. The popup inherits `size` from `Dialog` unless overridden.
|
|
303
|
+
|
|
304
|
+
| Prop | Type | Default | Description |
|
|
305
|
+
| --- | --- | --- | --- |
|
|
306
|
+
| `className?` | `string` | — | — |
|
|
307
|
+
| `children?` | `ReactNode` | — | — |
|
|
308
|
+
| `forceMount?` | `boolean` | `false` | Keep the popup mounted while the dialog is closed. |
|
|
309
|
+
| `avoidKeyboard?` | `boolean` | — | Whether the content shifts up to clear the on-screen keyboard (native only). No-op on web. Defaults to `true`. |
|
|
310
|
+
| `keyboardVerticalOffset?` | `number` | — | Distance (in px) between the keyboard and the content when keyboard avoidance is active. Forwarded to the underlying keyboard-avoiding layer on native. Defaults to `0`. |
|
|
311
|
+
| `size?` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full' \| null` | — | — |
|
|
312
|
+
| `disableFullWindowOverlay?` | `boolean` | `false` | On iOS, render in a plain `View` instead of `FullWindowOverlay`. Re-enables the element inspector, but the popup no longer layers above native modals. |
|
|
313
|
+
| `isSwipeable?` | `boolean` | `true` | Allow swipe-down to dismiss. Native only; no effect on web. |
|
|
314
|
+
| `zIndex?` | `number` | — | Stacking order for the portal overlay layer (backdrop + popup panel). Defaults to {@link MODAL_OVERLAY_Z_INDEX} (10000). |
|
|
315
|
+
|
|
316
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
317
|
+
|
|
318
|
+
### Dialog.Header
|
|
319
|
+
|
|
320
|
+
Container for the title, description, and close action.
|
|
321
|
+
|
|
322
|
+
| Prop | Type | Default | Description |
|
|
323
|
+
| --- | --- | --- | --- |
|
|
324
|
+
| `className?` | `string` | — | — |
|
|
325
|
+
| `children?` | `ReactNode` | — | — |
|
|
326
|
+
| `asChild?` | `boolean` | — | — |
|
|
327
|
+
|
|
328
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
329
|
+
|
|
330
|
+
### Dialog.Title
|
|
331
|
+
|
|
332
|
+
Heading announced when the dialog opens. Non-neutral severities render a corresponding status icon.
|
|
333
|
+
|
|
334
|
+
| Prop | Type | Default | Description |
|
|
335
|
+
| --- | --- | --- | --- |
|
|
336
|
+
| `className?` | `string` | — | — |
|
|
337
|
+
| `children?` | `ReactNode` | — | — |
|
|
338
|
+
| `severity?` | `'neutral' \| 'danger' \| 'warning' \| 'success' \| 'default'` | `'neutral'` | Severity level that controls the status icon and color treatment. |
|
|
339
|
+
| `asChild?` | `boolean` | — | — |
|
|
340
|
+
| `appearance?` | `'danger' \| 'warning' \| 'success' \| 'default'` | — | **Deprecated.** |
|
|
341
|
+
|
|
342
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props). `appearance` and the `"default"` severity are deprecated; use `severity="neutral"` instead.
|
|
343
|
+
|
|
344
|
+
### Dialog.Description
|
|
345
|
+
|
|
346
|
+
Secondary text linked to the popup through `aria-describedby`.
|
|
347
|
+
|
|
348
|
+
| Prop | Type | Default | Description |
|
|
349
|
+
| --- | --- | --- | --- |
|
|
350
|
+
| `className?` | `string` | — | — |
|
|
351
|
+
| `children?` | `ReactNode` | — | — |
|
|
352
|
+
| `asChild?` | `boolean` | — | — |
|
|
353
|
+
|
|
354
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
355
|
+
|
|
356
|
+
### Dialog.Content
|
|
357
|
+
|
|
358
|
+
Body container between the header and footer. Set `fullBleed` on `Dialog` to remove its horizontal padding.
|
|
359
|
+
|
|
360
|
+
| Prop | Type | Default | Description |
|
|
361
|
+
| --- | --- | --- | --- |
|
|
362
|
+
| `className?` | `string` | — | — |
|
|
363
|
+
| `children?` | `ReactNode` | — | — |
|
|
364
|
+
| `asChild?` | `boolean` | — | — |
|
|
365
|
+
|
|
366
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
367
|
+
|
|
368
|
+
### Dialog.Text
|
|
369
|
+
|
|
370
|
+
Body text styled with the default dialog typography and content color.
|
|
371
|
+
|
|
372
|
+
| Prop | Type | Default | Description |
|
|
373
|
+
| --- | --- | --- | --- |
|
|
374
|
+
| `className?` | `string` | — | — |
|
|
375
|
+
| `children?` | `ReactNode` | — | — |
|
|
376
|
+
| `asChild?` | `boolean` | — | — |
|
|
377
|
+
|
|
378
|
+
Inherits all [`TextProps`](https://reactnative.dev/docs/text#props).
|
|
379
|
+
|
|
380
|
+
### Dialog.Footer
|
|
381
|
+
|
|
382
|
+
Action row rendered below the content.
|
|
383
|
+
|
|
384
|
+
| Prop | Type | Default | Description |
|
|
385
|
+
| --- | --- | --- | --- |
|
|
386
|
+
| `className?` | `string` | — | — |
|
|
387
|
+
| `children?` | `ReactNode` | — | — |
|
|
388
|
+
| `asChild?` | `boolean` | — | — |
|
|
389
|
+
|
|
390
|
+
Inherits all [`ViewProps`](https://reactnative.dev/docs/view#props).
|
|
391
|
+
|
|
392
|
+
### Dialog.Close
|
|
393
|
+
|
|
394
|
+
Closes the dialog when pressed. Renders a close icon with the screen-reader label `Close` by default, or merges the behavior onto a custom child when `asChild` is set.
|
|
395
|
+
|
|
396
|
+
| Prop | Type | Default | Description |
|
|
397
|
+
| --- | --- | --- | --- |
|
|
398
|
+
| `className?` | `string` | — | — |
|
|
399
|
+
| `children?` | `ReactNode` | — | Either children or a render prop that receives a boolean reflecting whether the component is currently pressed. |
|
|
400
|
+
| `accessibilityLabel?` | `string` | — | Overrides the text that's read by the screen reader when the user interacts with the element. By default, the label is constructed by traversing all the children and accumulating all the Text nodes separated by space. |
|
|
401
|
+
| `asChild?` | `boolean` | — | — |
|
|
402
|
+
|
|
403
|
+
Inherits all [`PressableProps`](https://reactnative.dev/docs/pressable#props).
|
|
404
|
+
|
|
405
|
+
## Hook
|
|
406
|
+
|
|
407
|
+
### useDialog
|
|
408
|
+
|
|
409
|
+
Returns the nearest dialog's state and dismissal configuration:
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
const { open, onOpenChange } = useDialog();
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
| Return | Type | Description |
|
|
416
|
+
| ---------------------- | --------------------------- | -------------------------------------------------------- |
|
|
417
|
+
| `open` | `boolean` | Current open state |
|
|
418
|
+
| `onOpenChange` | `(open: boolean) => void` | Updates the open state |
|
|
419
|
+
| `closeOnBackdropPress` | `boolean` | Whether backdrop presses dismiss the dialog |
|
|
420
|
+
| `closeOnEscKey` | `boolean` | Whether Escape dismisses the dialog on web |
|
|
421
|
+
| `nativeID` | `string` | Generated identifier used by accessibility relationships |
|
|
422
|
+
| `role` | `'dialog' \| 'alertdialog'` | Accessibility role applied to the popup |
|
|
423
|
+
|
|
424
|
+
## Accessibility
|
|
425
|
+
|
|
426
|
+
- The popup receives `aria-modal`, `aria-labelledby`, and `aria-describedby` relationships automatically.
|
|
427
|
+
- Focus moves into the popup, remains trapped while open, and returns to the trigger after close.
|
|
428
|
+
- Escape closes the dialog on web when `closeOnEscKey` is enabled. When it is disabled, Escape is still consumed while the dialog is topmost, so an overlay beneath it stays open.
|
|
429
|
+
- Android's hardware back button closes the dialog.
|
|
430
|
+
|
|
431
|
+
## Constraints
|
|
432
|
+
|
|
433
|
+
- A shared `PortalHost` must be mounted once near the app root. `ForgeDesignProvider` includes one.
|
|
434
|
+
- The popup is capped at 85% of viewport height. Put a `ScrollView` inside `Dialog.Content` for long content.
|
|
435
|
+
- Swipe-to-dismiss is native only. Set `isSwipeable={false}` on `Dialog.Popup` to disable it, or pass `animation="disable-all"` on `Dialog` to turn off enter/exit and swipe animations.
|
|
436
|
+
- Native keyboard avoidance is built into `Dialog.Popup`. Do not wrap `Dialog` in another `KeyboardAvoidingView`. On iOS the popup renders inside a `FullWindowOverlay`, a separate native window the surrounding tree cannot reach. Web ignores keyboard avoidance. On Android, confirm the behavior on a device; it varies with edge-to-edge configuration.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Dialog — Design (https://design.candescent.com/docs/components/dialog)
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
**Common alternative names**: Modal, dialog box, popup, lightbox, interstitial
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Anatomy
|
|
10
|
+
|
|
11
|
+
In its most basic form, a dialog is made up of a title, a message, and a dismiss action. Dialogs also commonly contain an affirmative action button and a close button.
|
|
12
|
+
|
|
13
|
+
**Anatomy\_dialog**
|
|
14
|
+
|
|
15
|
+
1. **Title**: A short title that describes the content of the dialog.
|
|
16
|
+
2. **Content container**: By default, contains a message that communicates the purpose of the dialog and describes any recommended actions. It also allows for more complex content to be placed inside.
|
|
17
|
+
3. **Close button**: An icon button that closes the dialog and does not execute the affirmative action.
|
|
18
|
+
4. **Affirmative action**: A button used to initiate the action, then close the dialog.
|
|
19
|
+
5. **Dismissive action**: A button that closes the dialog and does not execute the affirmative action.
|
|
20
|
+
|
|
21
|
+
### Max width
|
|
22
|
+
|
|
23
|
+
The width of a dialog hugs its content up to a maximum width. The **Size** property is used to set that maximum width.
|
|
24
|
+
|
|
25
|
+
- **Size: xs** _**(default)**_ – 320px
|
|
26
|
+
- **Size: sm** – 384px
|
|
27
|
+
- **Size: md** – 448px
|
|
28
|
+
- **Size: lg** – 512px
|
|
29
|
+
- **Size: lx** – 576px
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
Usage
|
|
34
|
+
|
|
35
|
+
### When to use
|
|
36
|
+
|
|
37
|
+
> [!TIP]
|
|
38
|
+
>
|
|
39
|
+
> **When user action is required to proceed**
|
|
40
|
+
>
|
|
41
|
+
> - ex: To ask the user for confirmation or acknowledgement before proceeding
|
|
42
|
+
> - ex: To ask for user input or selection inside the dialog before proceeding
|
|
43
|
+
|
|
44
|
+
> [!TIP]
|
|
45
|
+
>
|
|
46
|
+
> **When the user must be blocked from proceeding**
|
|
47
|
+
>
|
|
48
|
+
> - ex: A critical system error is preventing the user from moving to the next step in a flow
|
|
49
|
+
> - ex: A critical system error is preventing the user from submitting a form, saving changes, etc.
|
|
50
|
+
> - ex: A critical system error is preventing the user from submitting a form, saving changes, etc.
|
|
51
|
+
|
|
52
|
+
> [!TIP]
|
|
53
|
+
>
|
|
54
|
+
> **When the user requests additional information**
|
|
55
|
+
>
|
|
56
|
+
> - ex: User clicks a “View more details” button, and a dialog opens with the additional details the user requested
|
|
57
|
+
|
|
58
|
+
### When not to use
|
|
59
|
+
|
|
60
|
+
> [!CAUTION]
|
|
61
|
+
>
|
|
62
|
+
> **Interrupting the user’s task against their best interest**
|
|
63
|
+
>
|
|
64
|
+
> - ex: Promotional content or announcements
|
|
65
|
+
> - ex: A notification that does not require the user’s immediate action
|
|
66
|
+
|
|
67
|
+
> [!CAUTION]
|
|
68
|
+
>
|
|
69
|
+
> **Contextual feedback that can be handled with an Alert, Snackbar, or Flag**
|
|
70
|
+
>
|
|
71
|
+
> - ex: The Dialog should be used sparingly, because interrupting a user’s current task is a drastic action. Determine if the feedback can be handled with an Alert, Snackbar, or other feedback component before resorting to a Dialog.
|
|
72
|
+
|
|
73
|
+
> [!CAUTION]
|
|
74
|
+
>
|
|
75
|
+
> **Information not related to the user’s current task**
|
|
76
|
+
>
|
|
77
|
+
> - ex: System-level notifications
|
|
78
|
+
> - ex: Application-level notifications
|
|
79
|
+
|
|
80
|
+
### Dialog vs Alert Dialog
|
|
81
|
+
|
|
82
|
+
Both components float above the page with an overlay and center on the viewport. They share layout, max width, and action placement. The difference is **whether the user can leave without choosing**.
|
|
83
|
+
|
|
84
|
+
**Dialog** — optional **close button**, **backdrop** tap, or **dismissive** action. Use when the user may ignore the message, read optional detail, or complete a short form.
|
|
85
|
+
|
|
86
|
+
**Alert Dialog** — **no close button**; user **must** select an explicit action. Use when every user must make a clear choice before the app proceeds.
|
|
87
|
+
|
|
88
|
+
| **Need** | **Use** |
|
|
89
|
+
| ------------------------------------------------------- | ------------------------------------ |
|
|
90
|
+
| Optional detail, short form, or “not now” is acceptable | **Dialog** |
|
|
91
|
+
| User must choose; ignoring the message is not allowed | **Alert Dialog** |
|
|
92
|
+
| Lightweight feedback; no modal required | **Alert**, **Snackbar**, or **Flag** |
|
|
93
|
+
|
|
94
|
+
### Choosing the right notification type
|
|
95
|
+
|
|
96
|
+
The Dialog component is just one of several different components that can be used to provide notifications and feedback to users. In addition to the guidance below, see the Notifications pattern for more details on choosing the correct notification type.
|
|
97
|
+
|
|
98
|
+
### Placement
|
|
99
|
+
|
|
100
|
+
Dialogs should float above all content on the screen, with a translucent background overlay between the dialog and the content. The dialog should be positioned in the direct center of the screen.
|
|
101
|
+
|
|
102
|
+
_usage\_layout\_wireframe_
|
|
103
|
+
|
|
104
|
+
### Action is required
|
|
105
|
+
|
|
106
|
+
Dialogs force the user to stop their current task by appearing on top of all content, and are persistent until either action is taken or dismissed by the user. This makes dialogs a great tool for when the user is required to make a decision, confirmation, or acknowledgement before proceeding.
|
|
107
|
+
|
|
108
|
+
Inside the dialog, use the message to communicate the purpose of the dialog and communicate any recommended actions. The dismissive button should decline the primary action and close the dialog. The affirmative button should serve as the confirmation/acknowledgement, then close the dialog.
|
|
109
|
+
|
|
110
|
+
- **Do:** Use a dialog when the user must make a decision in order to continue.
|
|
111
|
+
- **Do:** Use a dialog when a user must acknowledge a piece of information to continue.
|
|
112
|
+
|
|
113
|
+
### Preventing user errors
|
|
114
|
+
|
|
115
|
+
When a user initiates a destructive action, users should be shown a confirmation dialog, as it is possible they pressed the button by mistake.
|
|
116
|
+
|
|
117
|
+
Inside the dialog, describe the consequences of proceeding with the destructive action. The dismissive button should cancel the action and close the dialog. The affirmative button should be a destructive button that serves as the actual trigger for the destructive action.
|
|
118
|
+
|
|
119
|
+
- **Do:** Always use a dialog to ask user for confirmation before proceeding with a destructive action.
|
|
120
|
+
- **Don't:** Don’t proceed with a destructive action without asking the user for confirmation.
|
|
121
|
+
|
|
122
|
+
### Short forms
|
|
123
|
+
|
|
124
|
+
Dialogs can also be used to present short forms to the user. This type of dialog is very useful for getting quick user input or selection without leaving the current page. It can also be useful when a user has already gone through an entire process, and completing an additional step in a dialog can help them complete an entire other process.
|
|
125
|
+
|
|
126
|
+
In the example below, the user is completing their purchase. They already provided their name and email address during the checkout process, so simply confirming their details and agreeing to the terms of service can help them complete the registration process as well.
|
|
127
|
+
|
|
128
|
+
- **Do:** When using a dialog for a form, keep the form short and simple.
|
|
129
|
+
- **Don't:** Do not include multi-step forms in a dialog.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Behavior
|
|
134
|
+
|
|
135
|
+
### Opening
|
|
136
|
+
|
|
137
|
+
Dialogs should only open when the user does something on the page, such as clicking a button, following a link, or selecting an option. The user must be aware of the specific context that prompted the dialog.
|
|
138
|
+
|
|
139
|
+
### Actions
|
|
140
|
+
|
|
141
|
+
Actions within the dialog should not prompt another dialog to open. Never overlay an overlay.
|
|
142
|
+
|
|
143
|
+
- **Do:** If the user can take action with just a couple of clicks, a dialog is a good fit.
|
|
144
|
+
- **Don't:** If the user cannot complete the action without an additional dialog, then this action doesn’t make sense in a dialog.
|
|
145
|
+
|
|
146
|
+
### Closing
|
|
147
|
+
|
|
148
|
+
For surfaces where the user **must** choose an action and cannot dismiss without committing, use **[Alert Dialog](https://design.candescent.com/docs/components/alert-dialog/content.md)** instead. There are several different ways that a user can close or dismiss a dialog:
|
|
149
|
+
|
|
150
|
+
1. **Backdrop**: Clicking the backdrop should cancel the action and close the dialog.
|
|
151
|
+
2. **Close button**: Clicking the close button should cancel the action and close the dialog.
|
|
152
|
+
3. **Affirmative action**: Clicking the affirmative action should initiate the action, then close the dialog.
|
|
153
|
+
4. **Dismissive action**: Clicking the dismissive should cancel the action and close the dialog.
|
|
154
|
+
|
|
155
|
+
_behavior\_closing\_interactions_
|
|
156
|
+
|
|
157
|
+
### Scrolling
|
|
158
|
+
|
|
159
|
+
Dialog contents should be fully on screen and not require scrolling. If there is enough content to scroll in the dialog, put it on a separate page instead.
|
|
160
|
+
|
|
161
|
+
- **Do:** If absolutely necessary, use links within content to share additional information that can’t fit within the dialog. Links should open a new tab within the user’s default device browser.
|
|
162
|
+
- **Don't:** If there is enough content for a scroll bar, a dialog isn’t the right choice. Put long content on a new page, not in a dialog.
|
|
163
|
+
|
|
164
|
+
## Content
|
|
165
|
+
|
|
166
|
+
## Number of actions
|
|
167
|
+
|
|
168
|
+
Dialogs should have two actions: an **affirmative action** or a **dismissive action**. Dialogs are essentially asking the user to decide between these two options.
|
|
169
|
+
|
|
170
|
+
If additional information is important for the user to understand the need for the action, first determine if a dialog is the right choice. A new page may be a better fit. If additional information is needed only for a small subset of users, a link to additional information that opens in a new tab may be used.
|
|
171
|
+
|
|
172
|
+
- **Do:** If absolutely necessary, use links within content to share additional information that can’t fit within the dialog. Links should open in a new tab within the user’s default device.
|
|
173
|
+
- **Don't:** Links to additional content should not appear as actions the user can take, as these don’t resolve the reason for interrupting the user’s task.
|
|
174
|
+
|
|
175
|
+
A notable exception to the two actions rule is when the dialog is asking for a simple acknowledgement, because that action is both affirmative and dismissive. However, be careful that you are not taking away a choice from the user by doing this.
|
|
176
|
+
|
|
177
|
+
- **Do:** Using a single action is permissible in this case, because the only purpose of the dialog is to notify the user that their payment was sent. Clicking Done serves as both the user’s acknowledgement and desire to dismiss the modal.
|
|
178
|
+
- **Don't:** Don’t use a single action when asking the user to make a choice. In this example, all the user can do is click Agree, which will make them feel forced into that choice. Ensure that users have a way to back out.
|
|
179
|
+
|
|
180
|
+
## Close button
|
|
181
|
+
|
|
182
|
+
Dialogs are a conversation between the user and the system. It is required that all dialogs have a way to close, either with a close button in the top right, or a cancel button, that when acted upon by a screen reader or keyboard user will close the dialog.
|
|
183
|
+
|
|
184
|
+
- **Do:** Best practice is to include a close button AND a dismissive action. However, removing one of those is acceptable, as long as the user has one explicit way to dismiss the dialog.
|
|
185
|
+
- **Don't:** Don’t hide both the close button and the dismissive action. Users must have a way to close the dialog. Don’t assume that users know to click the backdrop. If the user must make a decision before continuing, use an alert dialog instead.
|
|
186
|
+
|
|
187
|
+
## Complex dialogs
|
|
188
|
+
|
|
189
|
+
As mentioned in the Short forms section, dialogs can be used to gather quick user input or selection without having to leave the current page. However, multi-step forms or long, scrollable content should not be included in a dialog. Instead, consider using a separate page.
|
|
190
|
+
|
|
191
|
+
- **Do:** When using a dialog for more complex content, keep it short and simple.
|
|
192
|
+
- **Don't:** Do not include multi-step forms or scrollable content in a dialog. Instead, consider using a separate page.
|