@indico-data/design-system 2.60.15 → 3.0.1

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.
Files changed (61) hide show
  1. package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +1 -1
  2. package/lib/components/index.d.ts +1 -1
  3. package/lib/components/modal/ConfirmationModal.d.ts +2 -0
  4. package/lib/components/modal/Modal.d.ts +1 -1
  5. package/lib/components/modal/Modal.stories.d.ts +3 -1
  6. package/lib/components/modal/index.d.ts +1 -0
  7. package/lib/components/modal/types.d.ts +18 -0
  8. package/lib/index.css +1827 -677
  9. package/lib/index.d.ts +22 -5
  10. package/lib/index.esm.css +1827 -677
  11. package/lib/index.esm.js +29 -6
  12. package/lib/index.esm.js.map +1 -1
  13. package/lib/index.js +29 -5
  14. package/lib/index.js.map +1 -1
  15. package/lib/types.d.ts +1 -1
  16. package/package.json +1 -1
  17. package/src/components/badge/styles/Badge.scss +6 -6
  18. package/src/components/button/styles/Button.scss +7 -13
  19. package/src/components/button/styles/_variables.scss +47 -48
  20. package/src/components/card/styles/Card.scss +15 -6
  21. package/src/components/floatUI/styles/_variables.scss +3 -3
  22. package/src/components/forms/date/datePicker/styles/DatePicker.scss +15 -16
  23. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +1 -1
  24. package/src/components/forms/form/styles/Form.scss +25 -25
  25. package/src/components/forms/select/__tests__/Select.test.tsx +11 -6
  26. package/src/components/forms/select/styles/Select.scss +9 -10
  27. package/src/components/index.ts +1 -1
  28. package/src/components/menu/styles/_variables.scss +8 -8
  29. package/src/components/modal/ConfirmationModal.mdx +69 -0
  30. package/src/components/modal/ConfirmationModal.tsx +132 -0
  31. package/src/components/modal/Modal.mdx +72 -42
  32. package/src/components/modal/Modal.stories.tsx +200 -40
  33. package/src/components/modal/Modal.tsx +20 -3
  34. package/src/components/modal/__tests__/Modal.test.tsx +12 -0
  35. package/src/components/modal/index.ts +1 -0
  36. package/src/components/modal/styles/Modal.scss +51 -20
  37. package/src/components/modal/types.ts +22 -0
  38. package/src/components/pill/Pill.stories.tsx +28 -14
  39. package/src/components/pill/Pill.tsx +7 -1
  40. package/src/components/pill/styles/Pill.scss +79 -55
  41. package/src/components/tanstackTable/styles/_variables.scss +23 -24
  42. package/src/components/tanstackTable/styles/table.scss +3 -3
  43. package/src/components/toast/Toast.mdx +32 -1
  44. package/src/components/toast/Toast.stories.tsx +32 -9
  45. package/src/components/toast/styles/Toast.scss +62 -12
  46. package/src/components/tooltip/Tooltip.tsx +6 -2
  47. package/src/components/tooltip/styles/Tooltip.scss +6 -2
  48. package/src/components/truncate/Truncate.tsx +1 -1
  49. package/src/index.ts +1 -1
  50. package/src/styles/_borders.scss +35 -4
  51. package/src/styles/_colors.scss +1 -1
  52. package/src/styles/globals.scss +107 -25
  53. package/src/styles/storybook.scss +23 -6
  54. package/src/styles/variables/_borders.scss +23 -7
  55. package/src/styles/variables/_padding.scss +0 -4
  56. package/src/styles/variables/themes/dark.scss +193 -133
  57. package/src/styles/variables/themes/light.scss +192 -133
  58. package/src/stylesAndAnimations/borders/BorderColor.tsx +87 -40
  59. package/src/stylesAndAnimations/colors/Swatch.tsx +0 -1
  60. package/src/stylesAndAnimations/colors/constants.ts +316 -193
  61. 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,132 @@
1
+ import { Button } from '../button/Button';
2
+ import { ButtonVariants } from '../button/types';
3
+ import { Checkbox } from '../forms/checkbox/Checkbox';
4
+ import { Col, Row } from '../grid';
5
+ import { Icon } from '../icons/Icon';
6
+ import { Modal } from './Modal';
7
+ import { ConfirmationModalProps } from './types';
8
+ import classNames from 'classnames';
9
+ import { useState } from 'react';
10
+
11
+ const defaultFooter = ({
12
+ onCancelRequest,
13
+ onConfirmRequest,
14
+ confirmationButtonText,
15
+ confirmationButtonVariant,
16
+ cancelButtonText,
17
+ hasDontShowAgainCheckbox,
18
+ isChecked,
19
+ onDontShowAgainChange,
20
+ }: {
21
+ onCancelRequest?: () => void;
22
+ onConfirmRequest?: ({
23
+ dontShowAgain,
24
+ }: {
25
+ dontShowAgain?: boolean;
26
+ }) => void | Promise<void> | Promise<boolean>;
27
+ confirmationButtonText?: string;
28
+ confirmationButtonVariant?: ButtonVariants;
29
+ cancelButtonText?: string;
30
+ hasDontShowAgainCheckbox?: boolean;
31
+ isChecked?: boolean;
32
+ onDontShowAgainChange?: (checked: boolean) => void;
33
+ }) => (
34
+ <Row gutterWidth={12} justify="end" align="center">
35
+ {hasDontShowAgainCheckbox && (
36
+ <Col>
37
+ <Checkbox
38
+ label="Don't display this again."
39
+ onChange={(e) => onDontShowAgainChange?.(e.target.checked)}
40
+ isChecked={isChecked}
41
+ id="dont-show-again"
42
+ name="dont-show-again"
43
+ />
44
+ </Col>
45
+ )}
46
+ <Col xs="content">
47
+ <Button onClick={onCancelRequest} ariaLabel={cancelButtonText || 'Cancel'} variant="outline">
48
+ {cancelButtonText}
49
+ </Button>
50
+ </Col>
51
+ <Col xs="content">
52
+ <Button
53
+ onClick={() => onConfirmRequest?.({ dontShowAgain: isChecked })}
54
+ ariaLabel={confirmationButtonText || 'Confirm'}
55
+ variant={confirmationButtonVariant}
56
+ >
57
+ {confirmationButtonText}
58
+ </Button>
59
+ </Col>
60
+ </Row>
61
+ );
62
+
63
+ export const ConfirmationModal = ({
64
+ className,
65
+ overlayClassName,
66
+ testId,
67
+ isOpen,
68
+ onRequestClose,
69
+ portalClassName,
70
+ appElement,
71
+ parentSelector,
72
+ shouldCloseOnOverlayClick,
73
+ shouldCloseOnEsc,
74
+ contentElement,
75
+ overlayElement,
76
+ footer,
77
+ children,
78
+ onConfirmRequest,
79
+ onCancelRequest,
80
+ confirmationButtonText = 'Confirm',
81
+ cancelButtonText = 'Cancel',
82
+ confirmationButtonVariant = 'solid',
83
+ icon,
84
+ title,
85
+ status = 'info',
86
+ maxWidthInPixels,
87
+ hasDontShowAgainCheckbox,
88
+ }: ConfirmationModalProps) => {
89
+ const [dontShowAgain, setDontShowAgain] = useState(false);
90
+
91
+ const modalFooter =
92
+ footer ||
93
+ defaultFooter({
94
+ onCancelRequest,
95
+ onConfirmRequest,
96
+ confirmationButtonText,
97
+ cancelButtonText,
98
+ confirmationButtonVariant,
99
+ hasDontShowAgainCheckbox,
100
+ isChecked: dontShowAgain,
101
+ onDontShowAgainChange: setDontShowAgain,
102
+ });
103
+
104
+ return (
105
+ <Modal
106
+ className={classNames('confirmation-modal', className)}
107
+ overlayClassName={overlayClassName}
108
+ testId={testId}
109
+ isOpen={isOpen}
110
+ onRequestClose={onRequestClose}
111
+ portalClassName={portalClassName}
112
+ appElement={appElement}
113
+ parentSelector={parentSelector}
114
+ shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
115
+ shouldCloseOnEsc={shouldCloseOnEsc}
116
+ contentElement={contentElement}
117
+ overlayElement={overlayElement}
118
+ footer={modalFooter}
119
+ maxWidthInPixels={maxWidthInPixels}
120
+ >
121
+ {icon && (
122
+ <Icon
123
+ name={icon}
124
+ className={classNames('confirmation-modal-icon', `color-${status}`)}
125
+ size="xl"
126
+ />
127
+ )}
128
+ {title && <h2 className="confirmation-modal-title">{title}</h2>}
129
+ {children}
130
+ </Modal>
131
+ );
132
+ };
@@ -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
- We have designed this modal with the intention for it to be used as a wrapper for content as you will see below. The recommended way to best use this component is to create your own wrapper in your application for specific modal needs. For example, you may create a wrapper component that displays a confirmation modal. You may also wish to create one for a stepper modal. The idea is that you wrap this modal in a component and build the body of your modal in that wrapped component so that you can reuse the modal component for different modal needs.
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,109 @@ 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
+ },
247
+ hasDontShowAgainCheckbox: {
248
+ control: 'boolean',
249
+ description: 'Whether the modal should have a checkbox to not show again',
250
+ table: {
251
+ category: 'Confirmation Modal',
252
+ type: {
253
+ summary: 'boolean',
254
+ },
255
+ },
256
+ },
153
257
  },
154
258
  };
155
259
 
@@ -159,12 +263,17 @@ type Story = StoryObj<typeof Modal>;
159
263
 
160
264
  export const Default: Story = {
161
265
  args: {
266
+ title: 'The Legend of Zelda',
267
+ subtitle: 'A timeless tale of courage and adventure',
162
268
  className: '',
269
+ maxWidthInPixels: 800,
163
270
  onRequestClose: () => {
164
271
  console.log('closed');
165
272
  },
166
273
  shouldCloseOnOverlayClick: true,
167
274
  shouldCloseOnEsc: true,
275
+ parentSelector: () =>
276
+ document.getElementById('theme-root') || document.getElementById('root') || document.body,
168
277
  },
169
278
 
170
279
  render: (args) => {
@@ -178,12 +287,28 @@ export const Default: Story = {
178
287
  setIsOpen(false);
179
288
  };
180
289
 
290
+ const footer = (
291
+ <>
292
+ <Row gutterWidth={12} justify="end" align="center">
293
+ <Col xs="content">
294
+ <Button onClick={handleClose} ariaLabel="Cancel" variant="outline">
295
+ Cancel
296
+ </Button>
297
+ </Col>
298
+ <Col xs="content">
299
+ <Button onClick={handleClose} ariaLabel="Confirm" variant="solid">
300
+ Confirm
301
+ </Button>
302
+ </Col>
303
+ </Row>
304
+ </>
305
+ );
306
+
181
307
  return (
182
308
  <Container>
183
309
  <Row>
184
310
  <Col sm={4}>
185
- <Modal {...args} isOpen={isOpen} onRequestClose={handleClose}>
186
- <h2 className="mb-4">The Legend of Zelda</h2>
311
+ <Modal {...args} isOpen={isOpen} onRequestClose={handleClose} footer={footer}>
187
312
  <p className="mb-4">
188
313
  In the mystical realm of Hyrule, where ancient magic flows through verdant fields
189
314
  and towering mountains, a timeless tale unfolds across countless generations. The
@@ -201,12 +326,6 @@ export const Default: Story = {
201
326
  continues to inspire hope and courage in all who call Hyrule home, as they battle
202
327
  against the forces of evil that would plunge their world into darkness.
203
328
  </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
329
  </Modal>
211
330
  <Button ariaLabel="open modal" onClick={handleOpen}>
212
331
  Open modal
@@ -218,14 +337,24 @@ export const Default: Story = {
218
337
  },
219
338
  };
220
339
 
221
- export const ConfirmationModal: Story = {
340
+ export const ConfirmationModalStory: StoryObj<typeof ConfirmationModal> = {
222
341
  args: {
223
342
  className: '',
224
343
  onRequestClose: () => {
225
344
  console.log('closed');
226
345
  },
346
+ title: 'Disable User',
347
+ confirmationButtonText: 'Delete',
348
+ confirmationButtonVariant: 'destructive',
349
+ cancelButtonText: 'Cancel',
227
350
  shouldCloseOnOverlayClick: true,
351
+ subtitle: '',
352
+ icon: 'trash',
353
+ status: 'error',
228
354
  shouldCloseOnEsc: true,
355
+ parentSelector: () => document.body,
356
+ maxWidthInPixels: 600,
357
+ hasDontShowAgainCheckbox: false,
229
358
  },
230
359
 
231
360
  render: (args) => {
@@ -243,38 +372,69 @@ export const ConfirmationModal: Story = {
243
372
  <Container>
244
373
  <Row>
245
374
  <Col sm={4}>
246
- <Modal {...args} isOpen={isOpen} onRequestClose={handleClose}>
247
- <h2 className="mb-4">Would you like to continue?</h2>
248
- <p className="mb-4">
249
- If you would like to proceed, please click the confirm button. Otherwise, click the
250
- cancel button.
375
+ <ConfirmationModal {...args} isOpen={isOpen} onRequestClose={handleClose}>
376
+ <p>
377
+ Deleting user will no longer allow this person to have access to this organization.
378
+ You can always invite them again if you change your mind.
251
379
  </p>
252
- <hr />
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>
380
+ </ConfirmationModal>
381
+ <Button ariaLabel="open modal" onClick={handleOpen}>
382
+ Open modal
383
+ </Button>
384
+ </Col>
385
+ </Row>
386
+ </Container>
387
+ );
388
+ },
389
+ };
390
+
391
+ export const ConfirmationModalStoryWithCheckbox: StoryObj<typeof ConfirmationModal> = {
392
+ args: {
393
+ className: '',
394
+ onRequestClose: () => {
395
+ console.log('closed');
396
+ },
397
+ title: 'Disable User',
398
+ confirmationButtonText: 'Delete',
399
+ confirmationButtonVariant: 'destructive',
400
+ cancelButtonText: 'Cancel',
401
+ shouldCloseOnOverlayClick: true,
402
+ icon: 'trash',
403
+ status: 'error',
404
+ shouldCloseOnEsc: true,
405
+ parentSelector: () => document.body,
406
+ maxWidthInPixels: 600,
407
+ hasDontShowAgainCheckbox: true,
408
+ },
409
+
410
+ render: (args) => {
411
+ const [isOpen, setIsOpen] = useState<boolean>(false);
412
+
413
+ const handleOpen = () => {
414
+ setIsOpen(true);
415
+ };
416
+
417
+ const handleClose = () => {
418
+ setIsOpen(false);
419
+ };
420
+
421
+ return (
422
+ <Container>
423
+ <Row>
424
+ <Col sm={4}>
425
+ <ConfirmationModal
426
+ {...args}
427
+ isOpen={isOpen}
428
+ onConfirmRequest={({ dontShowAgain }) => {
429
+ console.log('confirmed', dontShowAgain);
430
+ }}
431
+ onRequestClose={handleClose}
432
+ >
433
+ <p>
434
+ Deleting user will no longer allow this person to have access to this organization.
435
+ You can always invite them again if you change your mind.
436
+ </p>
437
+ </ConfirmationModal>
278
438
  <Button ariaLabel="open modal" onClick={handleOpen}>
279
439
  Open modal
280
440
  </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
  );