@indico-data/design-system 2.60.15 → 3.0.0
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/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +1 -1
- package/lib/components/index.d.ts +1 -1
- package/lib/components/modal/ConfirmationModal.d.ts +2 -0
- package/lib/components/modal/Modal.d.ts +1 -1
- package/lib/components/modal/Modal.stories.d.ts +2 -1
- package/lib/components/modal/index.d.ts +1 -0
- package/lib/components/modal/types.d.ts +15 -0
- package/lib/index.css +1827 -677
- package/lib/index.d.ts +19 -5
- package/lib/index.esm.css +1827 -677
- package/lib/index.esm.js +25 -6
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +25 -5
- package/lib/index.js.map +1 -1
- package/lib/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/badge/styles/Badge.scss +6 -6
- package/src/components/button/styles/Button.scss +7 -13
- package/src/components/button/styles/_variables.scss +47 -48
- package/src/components/card/styles/Card.scss +15 -6
- package/src/components/floatUI/styles/_variables.scss +3 -3
- package/src/components/forms/date/datePicker/styles/DatePicker.scss +15 -16
- package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +1 -1
- package/src/components/forms/form/styles/Form.scss +25 -25
- package/src/components/forms/select/__tests__/Select.test.tsx +11 -6
- package/src/components/forms/select/styles/Select.scss +9 -10
- package/src/components/index.ts +1 -1
- package/src/components/menu/styles/_variables.scss +8 -8
- package/src/components/modal/ConfirmationModal.mdx +69 -0
- package/src/components/modal/ConfirmationModal.tsx +103 -0
- package/src/components/modal/Modal.mdx +72 -42
- package/src/components/modal/Modal.stories.tsx +131 -40
- package/src/components/modal/Modal.tsx +20 -3
- package/src/components/modal/__tests__/Modal.test.tsx +12 -0
- package/src/components/modal/index.ts +1 -0
- package/src/components/modal/styles/Modal.scss +51 -20
- package/src/components/modal/types.ts +18 -0
- package/src/components/pill/Pill.stories.tsx +28 -14
- package/src/components/pill/Pill.tsx +7 -1
- package/src/components/pill/styles/Pill.scss +79 -55
- package/src/components/tanstackTable/styles/_variables.scss +23 -24
- package/src/components/tanstackTable/styles/table.scss +3 -3
- package/src/components/toast/Toast.mdx +32 -1
- package/src/components/toast/Toast.stories.tsx +32 -9
- package/src/components/toast/styles/Toast.scss +62 -12
- package/src/components/tooltip/Tooltip.tsx +6 -2
- package/src/components/tooltip/styles/Tooltip.scss +6 -2
- package/src/components/truncate/Truncate.tsx +1 -1
- package/src/index.ts +1 -1
- package/src/styles/_borders.scss +35 -4
- package/src/styles/_colors.scss +1 -1
- package/src/styles/globals.scss +107 -25
- package/src/styles/storybook.scss +23 -6
- package/src/styles/variables/_borders.scss +23 -7
- package/src/styles/variables/_padding.scss +0 -4
- package/src/styles/variables/themes/dark.scss +193 -133
- package/src/styles/variables/themes/light.scss +193 -133
- package/src/stylesAndAnimations/borders/BorderColor.tsx +87 -40
- package/src/stylesAndAnimations/colors/Swatch.tsx +0 -1
- package/src/stylesAndAnimations/colors/constants.ts +316 -193
- package/src/types.ts +5 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls, Story } from '@storybook/blocks';
|
|
2
|
+
import * as ModalStories from './Modal.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Components/Modal" name="ConfirmationModal" of={ModalStories} />
|
|
5
|
+
|
|
6
|
+
# ConfirmationModal
|
|
7
|
+
The ConfirmationModal is a component that provides us with a way to display a confirmation modal. It extends the Modal component. For more information on props not listed here, please visit the [react-modal documentation](https://github.com/reactjs/react-modal).
|
|
8
|
+
|
|
9
|
+
<Canvas of={ModalStories.ConfirmationModalStory} source={{
|
|
10
|
+
code: `
|
|
11
|
+
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
12
|
+
|
|
13
|
+
const handleOpen = () => {
|
|
14
|
+
setIsOpen(true);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const handleClose = () => {
|
|
18
|
+
setIsOpen(false);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Container>
|
|
23
|
+
<Row>
|
|
24
|
+
<Col sm={4}>
|
|
25
|
+
<ConfirmationModal
|
|
26
|
+
className: '',
|
|
27
|
+
onRequestClose: () => {
|
|
28
|
+
console.log('closed');
|
|
29
|
+
},
|
|
30
|
+
title: 'Disable User',
|
|
31
|
+
confirmationButtonText: 'Delete',
|
|
32
|
+
confirmationButtonVariant: 'destructive',
|
|
33
|
+
cancelButtonText: 'Cancel',
|
|
34
|
+
shouldCloseOnOverlayClick: true,
|
|
35
|
+
icon: 'trash',
|
|
36
|
+
status: 'error',
|
|
37
|
+
shouldCloseOnEsc: true,
|
|
38
|
+
parentSelector: () => document.body,
|
|
39
|
+
maxWidthInPixels: 600,
|
|
40
|
+
isOpen={isOpen}
|
|
41
|
+
onRequestClose={handleClose}
|
|
42
|
+
>
|
|
43
|
+
<p>
|
|
44
|
+
Deleting user will no longer allow this person to have access to this organization.
|
|
45
|
+
You can always invite them again if you change your mind.
|
|
46
|
+
</p>
|
|
47
|
+
</ConfirmationModal>
|
|
48
|
+
<Button ariaLabel="open modal" onClick={handleOpen}>
|
|
49
|
+
Open modal
|
|
50
|
+
</Button>
|
|
51
|
+
</Col>
|
|
52
|
+
</Row>
|
|
53
|
+
</Container>
|
|
54
|
+
`
|
|
55
|
+
}} />
|
|
56
|
+
|
|
57
|
+
### The following props are available for the ConfirmationModal component:
|
|
58
|
+
|
|
59
|
+
<Controls of={ModalStories.ConfirmationModalStory} />
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
The ConfirmationModal component is designed to be used as a confirmation modal. It is a wrapper around the Modal component and extends it with the following additional props:
|
|
64
|
+
|
|
65
|
+
- **confirmationButtonText** (string): The text displayed on the confirmation button. Default: 'Confirm'
|
|
66
|
+
- **confirmationButtonVariant** (string): The variant of the confirmation button. Options: 'solid', 'outline', 'link', 'action', 'destructive', 'soft'. Default: 'solid'
|
|
67
|
+
- **cancelButtonText** (string): The text displayed on the cancel button. Default: 'Cancel'
|
|
68
|
+
- **status** (string): The status of the modal which determines the icon color. Options: 'info', 'success', 'error'. Default: 'info'
|
|
69
|
+
- **icon** (string): The icon to display in the modal. Uses the available icon names from the design system.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Button } from '../button/Button';
|
|
2
|
+
import { ButtonVariants } from '../button/types';
|
|
3
|
+
import { Col, Row } from '../grid';
|
|
4
|
+
import { Icon } from '../icons/Icon';
|
|
5
|
+
import { Modal } from './Modal';
|
|
6
|
+
import { ConfirmationModalProps } from './types';
|
|
7
|
+
import classNames from 'classnames';
|
|
8
|
+
|
|
9
|
+
const defaultFooter = ({
|
|
10
|
+
onCancelRequest,
|
|
11
|
+
onConfirmRequest,
|
|
12
|
+
confirmationButtonText,
|
|
13
|
+
confirmationButtonVariant,
|
|
14
|
+
cancelButtonText,
|
|
15
|
+
}: {
|
|
16
|
+
onCancelRequest?: () => void;
|
|
17
|
+
onConfirmRequest?: () => void;
|
|
18
|
+
confirmationButtonText?: string;
|
|
19
|
+
confirmationButtonVariant?: ButtonVariants;
|
|
20
|
+
cancelButtonText?: string;
|
|
21
|
+
}) => (
|
|
22
|
+
<Row gutterWidth={12} justify="end" align="center">
|
|
23
|
+
<Col xs="content">
|
|
24
|
+
<Button onClick={onCancelRequest} ariaLabel={cancelButtonText || 'Cancel'} variant="outline">
|
|
25
|
+
{cancelButtonText}
|
|
26
|
+
</Button>
|
|
27
|
+
</Col>
|
|
28
|
+
<Col xs="content">
|
|
29
|
+
<Button
|
|
30
|
+
onClick={onConfirmRequest}
|
|
31
|
+
ariaLabel={confirmationButtonText || 'Confirm'}
|
|
32
|
+
variant={confirmationButtonVariant}
|
|
33
|
+
>
|
|
34
|
+
{confirmationButtonText}
|
|
35
|
+
</Button>
|
|
36
|
+
</Col>
|
|
37
|
+
</Row>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const ConfirmationModal = ({
|
|
41
|
+
className,
|
|
42
|
+
overlayClassName,
|
|
43
|
+
testId,
|
|
44
|
+
isOpen,
|
|
45
|
+
onRequestClose,
|
|
46
|
+
portalClassName,
|
|
47
|
+
appElement,
|
|
48
|
+
parentSelector,
|
|
49
|
+
shouldCloseOnOverlayClick,
|
|
50
|
+
shouldCloseOnEsc,
|
|
51
|
+
contentElement,
|
|
52
|
+
overlayElement,
|
|
53
|
+
footer,
|
|
54
|
+
children,
|
|
55
|
+
onConfirmRequest,
|
|
56
|
+
onCancelRequest,
|
|
57
|
+
confirmationButtonText = 'Confirm',
|
|
58
|
+
cancelButtonText = 'Cancel',
|
|
59
|
+
confirmationButtonVariant = 'solid',
|
|
60
|
+
icon,
|
|
61
|
+
title,
|
|
62
|
+
status = 'info',
|
|
63
|
+
maxWidthInPixels,
|
|
64
|
+
}: ConfirmationModalProps) => {
|
|
65
|
+
const modalFooter =
|
|
66
|
+
footer ||
|
|
67
|
+
defaultFooter({
|
|
68
|
+
onCancelRequest,
|
|
69
|
+
onConfirmRequest,
|
|
70
|
+
confirmationButtonText,
|
|
71
|
+
cancelButtonText,
|
|
72
|
+
confirmationButtonVariant,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Modal
|
|
77
|
+
className={classNames('confirmation-modal', className)}
|
|
78
|
+
overlayClassName={overlayClassName}
|
|
79
|
+
testId={testId}
|
|
80
|
+
isOpen={isOpen}
|
|
81
|
+
onRequestClose={onRequestClose}
|
|
82
|
+
portalClassName={portalClassName}
|
|
83
|
+
appElement={appElement}
|
|
84
|
+
parentSelector={parentSelector}
|
|
85
|
+
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
|
|
86
|
+
shouldCloseOnEsc={shouldCloseOnEsc}
|
|
87
|
+
contentElement={contentElement}
|
|
88
|
+
overlayElement={overlayElement}
|
|
89
|
+
footer={modalFooter}
|
|
90
|
+
maxWidthInPixels={maxWidthInPixels}
|
|
91
|
+
>
|
|
92
|
+
{icon && (
|
|
93
|
+
<Icon
|
|
94
|
+
name={icon}
|
|
95
|
+
className={classNames('confirmation-modal-icon', `color-${status}`)}
|
|
96
|
+
size="xl"
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
{title && <h2 className="confirmation-modal-title">{title}</h2>}
|
|
100
|
+
{children}
|
|
101
|
+
</Modal>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
@@ -5,17 +5,85 @@ import * as Modal from './Modal.stories';
|
|
|
5
5
|
|
|
6
6
|
# Modal
|
|
7
7
|
The Modal component provides us with a way to display content in a modal window. It can be used as a confirmation modal or as a content modal. It extends the react-modal library. For more information on props not listed here, please visit the [react-modal documentation](https://github.com/reactjs/react-modal).
|
|
8
|
-
<Canvas of={Modal.Default}
|
|
9
|
-
|
|
8
|
+
<Canvas of={Modal.Default} source={{
|
|
9
|
+
code: `
|
|
10
|
+
const [isOpen, setIsOpen] = useState<boolean>(false);
|
|
11
|
+
|
|
12
|
+
const handleOpen = () => {
|
|
13
|
+
setIsOpen(true);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const handleClose = () => {
|
|
17
|
+
setIsOpen(false);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const footer = (
|
|
21
|
+
<>
|
|
22
|
+
<Row gutterWidth={12} justify="end" align="center">
|
|
23
|
+
<Col xs="content">
|
|
24
|
+
<Button onClick={handleClose} ariaLabel="Cancel" variant="outline">
|
|
25
|
+
Cancel
|
|
26
|
+
</Button>
|
|
27
|
+
</Col>
|
|
28
|
+
<Col xs="content">
|
|
29
|
+
<Button onClick={handleClose} ariaLabel="Confirm" variant="solid">
|
|
30
|
+
Confirm
|
|
31
|
+
</Button>
|
|
32
|
+
</Col>
|
|
33
|
+
</Row>
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Container>
|
|
39
|
+
<Row>
|
|
40
|
+
<Col sm={4}>
|
|
41
|
+
<Modal
|
|
42
|
+
title: 'The Legend of Zelda',
|
|
43
|
+
subtitle: 'A timeless tale of courage and adventure',
|
|
44
|
+
className: '',
|
|
45
|
+
maxWidthInPixels: 800,
|
|
46
|
+
onRequestClose: () => {
|
|
47
|
+
console.log('closed');
|
|
48
|
+
},
|
|
49
|
+
shouldCloseOnOverlayClick: true,
|
|
50
|
+
shouldCloseOnEsc: true,
|
|
51
|
+
>
|
|
52
|
+
<p className="mb-4">
|
|
53
|
+
In the mystical realm of Hyrule, where ancient magic flows through verdant fields
|
|
54
|
+
and towering mountains, a timeless tale unfolds across countless generations. The
|
|
55
|
+
legendary hero Link, eternally reborn when darkness threatens the land, stands as
|
|
56
|
+
the chosen wielder of the Master Sword and bearer of the Triforce of Courage.
|
|
57
|
+
Through the ages, he has faced the malevolent forces of Ganon, whose insatiable
|
|
58
|
+
desire for power has brought destruction and chaos to this peaceful kingdom.
|
|
59
|
+
</p>
|
|
60
|
+
<p className="mb-4">
|
|
61
|
+
Princess Zelda, blessed with the wisdom of the goddesses and keeper of the royal
|
|
62
|
+
bloodline, serves as both ruler and guardian of Hyrule's most sacred treasures.
|
|
63
|
+
Together with Link, she maintains the delicate balance between light and shadow,
|
|
64
|
+
protecting the realm from those who would seek to corrupt its divine power. From the
|
|
65
|
+
windswept peaks of Death Mountain to the depths of Lake Hylia, their epic saga
|
|
66
|
+
continues to inspire hope and courage in all who call Hyrule home, as they battle
|
|
67
|
+
against the forces of evil that would plunge their world into darkness.
|
|
68
|
+
</p>
|
|
69
|
+
</Modal>
|
|
70
|
+
<Button ariaLabel="open modal" onClick={handleOpen}>
|
|
71
|
+
Open modal
|
|
72
|
+
</Button>
|
|
73
|
+
</Col>
|
|
74
|
+
</Row>
|
|
75
|
+
</Container>
|
|
76
|
+
)
|
|
77
|
+
` }} />
|
|
10
78
|
### The following props are available for the Modal component:
|
|
11
79
|
|
|
12
80
|
<Controls of={Modal.Default} />
|
|
13
81
|
|
|
14
82
|
## Usage
|
|
15
|
-
|
|
83
|
+
The modal component is designed to be an opinionated wrapper for your content. The header has conditionals that will reveal a horizontal line if there is atleast a title or a subtitle. The footer is also conditionally rendered based on the presence of a footer prop. If you need a simple confirmation modal, you can use the ConfirmationModal component. If you need something a little more custom, you should use this component.
|
|
16
84
|
|
|
17
85
|
### Parent Selector
|
|
18
|
-
You may have an issue with styling and require the modal to be displayed in a specific part of your application. You can use the `parentSelector` prop to pass in a function that returns the element you want to use as the parent element for the modal. In insights we have the following example.
|
|
86
|
+
You may have an issue with styling and require the modal to be displayed in a specific part of your application. You can use the `parentSelector` prop to pass in a function that returns the element you want to use as the parent element for the modal. In insights we have the following example. This is baked into the modal as of the latst version.
|
|
19
87
|
|
|
20
88
|
```tsx
|
|
21
89
|
const parentSelector = () =>
|
|
@@ -81,41 +149,3 @@ The content inside of the Modal tag will be displayed when you open the modal. A
|
|
|
81
149
|
</div>
|
|
82
150
|
</Modal>
|
|
83
151
|
```
|
|
84
|
-
|
|
85
|
-
### Confirmation Modal
|
|
86
|
-
If you are looking for a confirmation modal, you can use the following as an example.
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
<Modal isOpen={isOpen} onRequestClose={handleClose}>
|
|
90
|
-
<h2>Would you like to continue?</h2>
|
|
91
|
-
<p>
|
|
92
|
-
If you would like to proceed, please click the confirm button. Otherwise, click the
|
|
93
|
-
cancel button.
|
|
94
|
-
</p>
|
|
95
|
-
<hr />
|
|
96
|
-
<Row nogutter justify="end" align="center">
|
|
97
|
-
<Col xs="content">
|
|
98
|
-
<Button
|
|
99
|
-
onClick={() => {
|
|
100
|
-
console.log('cancelled');
|
|
101
|
-
handleClose();
|
|
102
|
-
}}
|
|
103
|
-
className="mr-2"
|
|
104
|
-
variant="outline"
|
|
105
|
-
ariaLabel="Cancel"
|
|
106
|
-
>
|
|
107
|
-
Cancel
|
|
108
|
-
</Button>
|
|
109
|
-
<Button
|
|
110
|
-
onClick={() => {
|
|
111
|
-
console.log('confirmed');
|
|
112
|
-
handleClose();
|
|
113
|
-
}}
|
|
114
|
-
ariaLabel="Confirm"
|
|
115
|
-
>
|
|
116
|
-
Confirm
|
|
117
|
-
</Button>
|
|
118
|
-
</Col>
|
|
119
|
-
</Row>
|
|
120
|
-
</Modal>
|
|
121
|
-
```
|
|
@@ -6,6 +6,7 @@ import { Button } from '../button';
|
|
|
6
6
|
import { registerFontAwesomeIcons } from '@/setup/setupIcons';
|
|
7
7
|
import { indiconDefinitions } from '@/components/icons/indicons';
|
|
8
8
|
import { fas } from '@fortawesome/free-solid-svg-icons';
|
|
9
|
+
import { ConfirmationModal } from './ConfirmationModal';
|
|
9
10
|
|
|
10
11
|
registerFontAwesomeIcons(...Object.values(fas), ...indiconDefinitions);
|
|
11
12
|
|
|
@@ -150,6 +151,99 @@ const meta: Meta = {
|
|
|
150
151
|
disable: true,
|
|
151
152
|
},
|
|
152
153
|
},
|
|
154
|
+
title: {
|
|
155
|
+
control: 'text',
|
|
156
|
+
description: 'The title of the modal',
|
|
157
|
+
table: {
|
|
158
|
+
category: 'Props',
|
|
159
|
+
type: {
|
|
160
|
+
summary: 'string',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
footer: {
|
|
165
|
+
control: false,
|
|
166
|
+
description: 'The footer of the modal. It accepts a React Component',
|
|
167
|
+
table: {
|
|
168
|
+
category: 'Props',
|
|
169
|
+
type: {
|
|
170
|
+
summary: 'React.ReactNode',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
subtitle: {
|
|
175
|
+
control: 'text',
|
|
176
|
+
description: 'The subtitle of the modal',
|
|
177
|
+
table: {
|
|
178
|
+
category: 'Props',
|
|
179
|
+
type: {
|
|
180
|
+
summary: 'string',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
maxWidthInPixels: {
|
|
185
|
+
control: 'number',
|
|
186
|
+
description: 'The maximum width of the modal in pixels',
|
|
187
|
+
table: {
|
|
188
|
+
category: 'Props',
|
|
189
|
+
type: {
|
|
190
|
+
summary: 'number',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
confirmationButtonText: {
|
|
195
|
+
control: 'text',
|
|
196
|
+
description: 'The text of the confirmation button',
|
|
197
|
+
table: {
|
|
198
|
+
category: 'Confirmation Modal',
|
|
199
|
+
type: {
|
|
200
|
+
summary: 'string',
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
confirmationButtonVariant: {
|
|
205
|
+
control: 'select',
|
|
206
|
+
options: ['solid', 'outline', 'link', 'action', 'destructive', 'soft'],
|
|
207
|
+
description: 'The variant of the confirmation button. ',
|
|
208
|
+
table: {
|
|
209
|
+
category: 'Confirmation Modal',
|
|
210
|
+
type: {
|
|
211
|
+
summary: 'string',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
cancelButtonText: {
|
|
216
|
+
control: 'text',
|
|
217
|
+
description: 'The text of the cancel button',
|
|
218
|
+
table: {
|
|
219
|
+
category: 'Confirmation Modal',
|
|
220
|
+
type: {
|
|
221
|
+
summary: 'string',
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
status: {
|
|
226
|
+
control: 'select',
|
|
227
|
+
options: ['info', 'success', 'error'],
|
|
228
|
+
description: 'The status of the modal. This will determine the color of the icon.',
|
|
229
|
+
table: {
|
|
230
|
+
category: 'Confirmation Modal',
|
|
231
|
+
type: {
|
|
232
|
+
summary: 'string',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
icon: {
|
|
237
|
+
control: 'select',
|
|
238
|
+
options: Object.keys(indiconDefinitions),
|
|
239
|
+
description: 'The icon of the modal',
|
|
240
|
+
table: {
|
|
241
|
+
category: 'Confirmation Modal',
|
|
242
|
+
type: {
|
|
243
|
+
summary: 'string',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
153
247
|
},
|
|
154
248
|
};
|
|
155
249
|
|
|
@@ -159,12 +253,17 @@ type Story = StoryObj<typeof Modal>;
|
|
|
159
253
|
|
|
160
254
|
export const Default: Story = {
|
|
161
255
|
args: {
|
|
256
|
+
title: 'The Legend of Zelda',
|
|
257
|
+
subtitle: 'A timeless tale of courage and adventure',
|
|
162
258
|
className: '',
|
|
259
|
+
maxWidthInPixels: 800,
|
|
163
260
|
onRequestClose: () => {
|
|
164
261
|
console.log('closed');
|
|
165
262
|
},
|
|
166
263
|
shouldCloseOnOverlayClick: true,
|
|
167
264
|
shouldCloseOnEsc: true,
|
|
265
|
+
parentSelector: () =>
|
|
266
|
+
document.getElementById('theme-root') || document.getElementById('root') || document.body,
|
|
168
267
|
},
|
|
169
268
|
|
|
170
269
|
render: (args) => {
|
|
@@ -178,12 +277,28 @@ export const Default: Story = {
|
|
|
178
277
|
setIsOpen(false);
|
|
179
278
|
};
|
|
180
279
|
|
|
280
|
+
const footer = (
|
|
281
|
+
<>
|
|
282
|
+
<Row gutterWidth={12} justify="end" align="center">
|
|
283
|
+
<Col xs="content">
|
|
284
|
+
<Button onClick={handleClose} ariaLabel="Cancel" variant="outline">
|
|
285
|
+
Cancel
|
|
286
|
+
</Button>
|
|
287
|
+
</Col>
|
|
288
|
+
<Col xs="content">
|
|
289
|
+
<Button onClick={handleClose} ariaLabel="Confirm" variant="solid">
|
|
290
|
+
Confirm
|
|
291
|
+
</Button>
|
|
292
|
+
</Col>
|
|
293
|
+
</Row>
|
|
294
|
+
</>
|
|
295
|
+
);
|
|
296
|
+
|
|
181
297
|
return (
|
|
182
298
|
<Container>
|
|
183
299
|
<Row>
|
|
184
300
|
<Col sm={4}>
|
|
185
|
-
<Modal {...args} isOpen={isOpen} onRequestClose={handleClose}>
|
|
186
|
-
<h2 className="mb-4">The Legend of Zelda</h2>
|
|
301
|
+
<Modal {...args} isOpen={isOpen} onRequestClose={handleClose} footer={footer}>
|
|
187
302
|
<p className="mb-4">
|
|
188
303
|
In the mystical realm of Hyrule, where ancient magic flows through verdant fields
|
|
189
304
|
and towering mountains, a timeless tale unfolds across countless generations. The
|
|
@@ -201,12 +316,6 @@ export const Default: Story = {
|
|
|
201
316
|
continues to inspire hope and courage in all who call Hyrule home, as they battle
|
|
202
317
|
against the forces of evil that would plunge their world into darkness.
|
|
203
318
|
</p>
|
|
204
|
-
<div className="actions text-align--right">
|
|
205
|
-
<hr />
|
|
206
|
-
<Button onClick={handleClose} ariaLabel="Close" variant="outline">
|
|
207
|
-
Close
|
|
208
|
-
</Button>
|
|
209
|
-
</div>
|
|
210
319
|
</Modal>
|
|
211
320
|
<Button ariaLabel="open modal" onClick={handleOpen}>
|
|
212
321
|
Open modal
|
|
@@ -218,14 +327,22 @@ export const Default: Story = {
|
|
|
218
327
|
},
|
|
219
328
|
};
|
|
220
329
|
|
|
221
|
-
export const
|
|
330
|
+
export const ConfirmationModalStory: StoryObj<typeof ConfirmationModal> = {
|
|
222
331
|
args: {
|
|
223
332
|
className: '',
|
|
224
333
|
onRequestClose: () => {
|
|
225
334
|
console.log('closed');
|
|
226
335
|
},
|
|
336
|
+
title: 'Disable User',
|
|
337
|
+
confirmationButtonText: 'Delete',
|
|
338
|
+
confirmationButtonVariant: 'destructive',
|
|
339
|
+
cancelButtonText: 'Cancel',
|
|
227
340
|
shouldCloseOnOverlayClick: true,
|
|
341
|
+
icon: 'trash',
|
|
342
|
+
status: 'error',
|
|
228
343
|
shouldCloseOnEsc: true,
|
|
344
|
+
parentSelector: () => document.body,
|
|
345
|
+
maxWidthInPixels: 600,
|
|
229
346
|
},
|
|
230
347
|
|
|
231
348
|
render: (args) => {
|
|
@@ -243,38 +360,12 @@ export const ConfirmationModal: Story = {
|
|
|
243
360
|
<Container>
|
|
244
361
|
<Row>
|
|
245
362
|
<Col sm={4}>
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
cancel button.
|
|
363
|
+
<ConfirmationModal {...args} isOpen={isOpen} onRequestClose={handleClose}>
|
|
364
|
+
<p>
|
|
365
|
+
Deleting user will no longer allow this person to have access to this organization.
|
|
366
|
+
You can always invite them again if you change your mind.
|
|
251
367
|
</p>
|
|
252
|
-
|
|
253
|
-
<Row nogutter justify="end" align="center">
|
|
254
|
-
<Col xs="content">
|
|
255
|
-
<Button
|
|
256
|
-
onClick={() => {
|
|
257
|
-
console.log('cancelled');
|
|
258
|
-
handleClose();
|
|
259
|
-
}}
|
|
260
|
-
className="mr-2"
|
|
261
|
-
variant="outline"
|
|
262
|
-
ariaLabel="Cancel"
|
|
263
|
-
>
|
|
264
|
-
Cancel
|
|
265
|
-
</Button>
|
|
266
|
-
<Button
|
|
267
|
-
onClick={() => {
|
|
268
|
-
console.log('confirmed');
|
|
269
|
-
handleClose();
|
|
270
|
-
}}
|
|
271
|
-
ariaLabel="Confirm"
|
|
272
|
-
>
|
|
273
|
-
Confirm
|
|
274
|
-
</Button>
|
|
275
|
-
</Col>
|
|
276
|
-
</Row>
|
|
277
|
-
</Modal>
|
|
368
|
+
</ConfirmationModal>
|
|
278
369
|
<Button ariaLabel="open modal" onClick={handleOpen}>
|
|
279
370
|
Open modal
|
|
280
371
|
</Button>
|
|
@@ -20,15 +20,20 @@ export const Modal = ({
|
|
|
20
20
|
contentElement,
|
|
21
21
|
overlayElement,
|
|
22
22
|
position = 'center',
|
|
23
|
-
parentSelector,
|
|
23
|
+
parentSelector = () => document.getElementById('theme-root') || document.getElementById('root')!, // default for our apps, storybook needs a different one
|
|
24
|
+
title,
|
|
25
|
+
subtitle,
|
|
26
|
+
footer,
|
|
27
|
+
maxWidthInPixels,
|
|
24
28
|
...rest
|
|
25
29
|
}: ModalProps) => {
|
|
26
30
|
const modalClasses = classNames('modal', `modal--${position}`, className);
|
|
27
31
|
const overlayClasses = classNames('modal-overlay', overlayClassName);
|
|
28
32
|
|
|
33
|
+
const hasHeader = title || subtitle;
|
|
34
|
+
|
|
29
35
|
return (
|
|
30
36
|
<ReactModal
|
|
31
|
-
style={{}}
|
|
32
37
|
className={modalClasses}
|
|
33
38
|
overlayClassName={overlayClasses}
|
|
34
39
|
testId={testId}
|
|
@@ -43,7 +48,7 @@ export const Modal = ({
|
|
|
43
48
|
overlayElement={overlayElement}
|
|
44
49
|
{...rest}
|
|
45
50
|
>
|
|
46
|
-
<div className="modal-content">
|
|
51
|
+
<div className="modal-content" style={{ maxWidth: `${maxWidthInPixels}px` }}>
|
|
47
52
|
<Button
|
|
48
53
|
className="modal-close-button"
|
|
49
54
|
onClick={onRequestClose}
|
|
@@ -52,7 +57,19 @@ export const Modal = ({
|
|
|
52
57
|
iconLeft="x-close"
|
|
53
58
|
ariaLabel="Close"
|
|
54
59
|
/>
|
|
60
|
+
{hasHeader && (
|
|
61
|
+
<div className="modal-header">
|
|
62
|
+
<Row justify="between" align="center">
|
|
63
|
+
<Col>
|
|
64
|
+
{title && <h2 className="modal-title">{title}</h2>}
|
|
65
|
+
{subtitle && <p className="modal-subtitle">{subtitle}</p>}
|
|
66
|
+
</Col>
|
|
67
|
+
</Row>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
|
|
55
71
|
<div className="modal-body">{children}</div>
|
|
72
|
+
{footer && <div className="modal-footer">{footer}</div>}
|
|
56
73
|
</div>
|
|
57
74
|
</ReactModal>
|
|
58
75
|
);
|
|
@@ -57,4 +57,16 @@ describe('Modal', () => {
|
|
|
57
57
|
const modal = screen.getByRole('dialog');
|
|
58
58
|
expect(modal).toHaveClass('custom-modal');
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
it('shows header when title is provided', () => {
|
|
62
|
+
render(<Modal {...defaultProps} title="Test Modal" />);
|
|
63
|
+
const header = screen.getByRole('heading');
|
|
64
|
+
expect(header).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('shows header when subtitle is provided', () => {
|
|
68
|
+
render(<Modal {...defaultProps} subtitle="Test Modal" />);
|
|
69
|
+
const header = screen.getByRole('heading');
|
|
70
|
+
expect(header).toBeInTheDocument();
|
|
71
|
+
});
|
|
60
72
|
});
|