Skip to content

Commit f4c37b7

Browse files
Yihui Liaoyihuiliao
authored andcommitted
Alert dialog (#195)
* initialize alert dialog * add alert dialog * update story, fix closing * remove comments * remove props.styles * update types * remove orientation prop * add aria-label to icons * fix lint --------- Co-authored-by: Yihui Liao <[email protected]>
1 parent c7516c8 commit f4c37b7

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {DOMProps, DOMRef} from '@react-types/shared';
14+
import {Button} from './Button';
15+
import {ButtonGroup} from './ButtonGroup';
16+
import {Content, Heading} from './Content';
17+
import {Dialog} from './Dialog';
18+
import {forwardRef, ReactNode} from 'react';
19+
import {UnsafeStyles} from './style-utils' with {type: 'macro'};
20+
import {chain} from '@react-aria/utils';
21+
import {style} from '../style/spectrum-theme' with {type: 'macro'};
22+
import AlertTriangle from '../s2wf-icons/assets/svg/S2_Icon_AlertTriangle_20_N.svg';
23+
import NoticeSquare from '../s2wf-icons/assets/svg/S2_Icon_Notice_20_N.svg';
24+
import {CenterBaseline} from './CenterBaseline';
25+
import {Provider} from 'react-aria-components';
26+
import {IconContext} from './Icon';
27+
28+
export interface AlertDialogProps extends DOMProps, UnsafeStyles {
29+
/** The [visual style](https://spectrum.adobe.com/page/alert-dialog/#Options) of the AlertDialog. */
30+
variant?: 'confirmation' | 'information' | 'destructive' | 'error' | 'warning',
31+
/** The title of the AlertDialog. */
32+
title: string,
33+
/** The contents of the AlertDialog. */
34+
children: ReactNode,
35+
/** The label to display within the cancel button. */
36+
cancelLabel?: string,
37+
/** The label to display within the confirm button. */
38+
primaryActionLabel: string,
39+
/** The label to display within the secondary button. */
40+
secondaryActionLabel?: string,
41+
/** Whether the primary button is disabled. */
42+
isPrimaryActionDisabled?: boolean,
43+
/** Whether the secondary button is disabled. */
44+
isSecondaryActionDisabled?: boolean,
45+
/** Handler that is called when the cancel button is pressed. */
46+
onCancel?: () => void,
47+
/** Handler that is called when the primary button is pressed. */
48+
onPrimaryAction?: () => void,
49+
/** Handler that is called when the secondary button is pressed. */
50+
onSecondaryAction?: () => void,
51+
/** Button to focus by default when the dialog opens. */
52+
autoFocusButton?: 'cancel' | 'primary' | 'secondary',
53+
/**
54+
* The size of the Dialog.
55+
*
56+
* @default 'M'
57+
*/
58+
size?: 'S' | 'M' | 'L'
59+
}
60+
61+
const icon = style({
62+
marginEnd: 8,
63+
'--iconPrimary': {
64+
type: 'fill',
65+
value: {
66+
variant: {
67+
error: 'negative',
68+
warning: 'notice'
69+
}
70+
}
71+
}
72+
});
73+
74+
function AlertDialog(props: AlertDialogProps, ref: DOMRef) {
75+
let {
76+
autoFocusButton,
77+
cancelLabel,
78+
secondaryActionLabel,
79+
primaryActionLabel,
80+
isSecondaryActionDisabled,
81+
isPrimaryActionDisabled,
82+
onCancel = () => {},
83+
onPrimaryAction = () => {},
84+
onSecondaryAction = () => {},
85+
title,
86+
children,
87+
variant = 'confirmation'
88+
} = props;
89+
90+
let buttonVariant = 'primary';
91+
if (variant === 'confirmation') {
92+
buttonVariant = 'accent';
93+
} else if (variant === 'destructive') {
94+
buttonVariant = 'negative';
95+
}
96+
97+
return (
98+
<Dialog
99+
role="alertdialog"
100+
ref={ref}
101+
size={props.size}
102+
UNSAFE_style={props.UNSAFE_style}
103+
UNSAFE_className={(props.UNSAFE_className || '')}>
104+
{({close}) => (
105+
<>
106+
<Provider
107+
values={[
108+
[IconContext, {styles: icon({variant})}]
109+
]}>
110+
<Heading slot="title">
111+
<CenterBaseline>
112+
{/* TODO: Add translations */}
113+
{variant === 'error' && <AlertTriangle aria-label="Alert" />}
114+
{variant === 'warning' && <NoticeSquare aria-label="Alert" />}
115+
{title}
116+
</CenterBaseline>
117+
</Heading>
118+
</Provider>
119+
<Content>{children}</Content>
120+
<ButtonGroup>
121+
{cancelLabel &&
122+
<Button
123+
onPress={() => chain(close(), onCancel())}
124+
variant="secondary"
125+
fillStyle="outline"
126+
autoFocus={autoFocusButton === 'cancel'}>
127+
{cancelLabel}
128+
</Button>
129+
}
130+
{secondaryActionLabel &&
131+
<Button
132+
onPress={() => chain(close(), onSecondaryAction())}
133+
variant="secondary"
134+
isDisabled={isSecondaryActionDisabled}
135+
fillStyle="outline"
136+
autoFocus={autoFocusButton === 'secondary'}>
137+
{secondaryActionLabel}
138+
</Button>
139+
}
140+
<Button
141+
variant={buttonVariant as 'primary' | 'accent' | 'negative'}
142+
isDisabled={isPrimaryActionDisabled}
143+
autoFocus={autoFocusButton === 'primary'}
144+
onPress={() => chain(close(), onPrimaryAction())}>
145+
{primaryActionLabel}
146+
</Button>
147+
</ButtonGroup>
148+
</>
149+
)}
150+
</Dialog>
151+
);
152+
}
153+
154+
/**
155+
* AlertDialogs are a specific type of Dialog. They display important information that users need to acknowledge.
156+
*/
157+
let _AlertDialog = forwardRef(AlertDialog);
158+
export {_AlertDialog as AlertDialog};

packages/@react-spectrum/s2/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
export {ActionButton} from './ActionButton';
1414
export {ActionMenu} from './ActionMenu';
15+
export {AlertDialog} from './AlertDialog';
1516
export {Avatar} from './Avatar';
1617
export {Badge} from './Badge';
1718
export {Breadcrumbs, Breadcrumb} from './Breadcrumbs';
@@ -61,6 +62,7 @@ export {FileTrigger} from 'react-aria-components';
6162

6263
export type {ActionButtonProps} from './ActionButton';
6364
export type {ActionMenuProps} from './ActionMenu';
65+
export type {AlertDialogProps} from './AlertDialog';
6466
export type {AvatarProps} from './Avatar';
6567
export type {BreadcrumbsProps, BreadcrumbProps} from './Breadcrumbs';
6668
export type {BadgeProps} from './Badge';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2024 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import type {Meta} from '@storybook/react';
14+
import {ActionButton, AlertDialog, DialogTrigger} from '../src';
15+
16+
const meta: Meta<typeof AlertDialog> = {
17+
component: AlertDialog as any,
18+
parameters: {
19+
layout: 'centered'
20+
},
21+
tags: ['autodocs']
22+
};
23+
24+
export default meta;
25+
26+
export const Example = (args: any) => (
27+
<DialogTrigger>
28+
<ActionButton>Save</ActionButton>
29+
<AlertDialog {...args} >
30+
You have not saved your profile information
31+
for this account. Would you like to register now?
32+
</AlertDialog>
33+
</DialogTrigger>
34+
);
35+
36+
Example.args = {
37+
title: 'Register profile',
38+
cancelLabel: 'Cancel',
39+
secondaryActionLabel: 'Remind me later',
40+
primaryActionLabel: 'Register'
41+
};

0 commit comments

Comments
 (0)