@react-ui-org/react-ui 0.47.0 → 0.49.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.
Files changed (142) hide show
  1. package/dist/lib.development.js +465 -93
  2. package/dist/lib.js +1 -1
  3. package/package.json +1 -1
  4. package/src/lib/components/Alert/Alert.jsx +3 -0
  5. package/src/lib/components/Alert/Alert.scss +10 -10
  6. package/src/lib/components/Alert/README.mdx +18 -2
  7. package/src/lib/components/Alert/index.js +1 -1
  8. package/src/lib/components/Badge/Badge.jsx +4 -8
  9. package/src/lib/components/Badge/Badge.scss +21 -21
  10. package/src/lib/components/Badge/README.mdx +15 -1
  11. package/src/lib/components/Badge/index.js +1 -1
  12. package/src/lib/components/Button/Button.jsx +23 -34
  13. package/src/lib/components/Button/README.mdx +21 -7
  14. package/src/lib/components/Button/_base.scss +20 -20
  15. package/src/lib/components/Button/_priorities.scss +35 -35
  16. package/src/lib/components/Button/helpers/getRootLabelVisibilityClassName.js +7 -7
  17. package/src/lib/components/Button/helpers/getRootPriorityClassName.js +3 -3
  18. package/src/lib/components/Button/index.js +1 -1
  19. package/src/lib/components/ButtonGroup/ButtonGroup.jsx +2 -8
  20. package/src/lib/components/ButtonGroup/README.mdx +18 -2
  21. package/src/lib/components/Card/Card.jsx +6 -10
  22. package/src/lib/components/Card/Card.scss +13 -13
  23. package/src/lib/components/Card/CardBody.jsx +6 -10
  24. package/src/lib/components/Card/CardFooter.jsx +6 -7
  25. package/src/lib/components/Card/README.mdx +21 -5
  26. package/src/lib/components/CheckboxField/CheckboxField.jsx +17 -44
  27. package/src/lib/components/CheckboxField/README.mdx +18 -6
  28. package/src/lib/components/CheckboxField/index.js +1 -1
  29. package/src/lib/components/FileInputField/FileInputField.jsx +20 -29
  30. package/src/lib/components/FileInputField/FileInputField.scss +3 -3
  31. package/src/lib/components/FileInputField/README.mdx +30 -28
  32. package/src/lib/components/FileInputField/index.js +1 -1
  33. package/src/lib/components/FormLayout/FormLayout.jsx +5 -9
  34. package/src/lib/components/FormLayout/FormLayout.scss +3 -3
  35. package/src/lib/components/FormLayout/FormLayoutCustomField.jsx +4 -1
  36. package/src/lib/components/FormLayout/FormLayoutCustomField.scss +8 -8
  37. package/src/lib/components/FormLayout/README.mdx +28 -13
  38. package/src/lib/components/Grid/Grid.jsx +31 -35
  39. package/src/lib/components/Grid/Grid.scss +10 -15
  40. package/src/lib/components/Grid/GridSpan.jsx +5 -11
  41. package/src/lib/components/Grid/README.mdx +48 -36
  42. package/src/lib/components/Grid/_helpers/generateResponsiveCustomProperties.js +11 -3
  43. package/src/lib/components/Grid/_settings.scss +18 -0
  44. package/src/lib/components/Grid/_tools.scss +5 -5
  45. package/src/lib/components/Modal/Modal.jsx +147 -254
  46. package/src/lib/components/Modal/Modal.scss +7 -55
  47. package/src/lib/components/Modal/ModalBody.jsx +60 -0
  48. package/src/lib/components/Modal/ModalBody.scss +18 -0
  49. package/src/lib/components/Modal/ModalCloseButton.jsx +45 -0
  50. package/src/lib/components/Modal/ModalCloseButton.scss +18 -0
  51. package/src/lib/components/Modal/ModalContent.jsx +39 -0
  52. package/src/lib/components/Modal/ModalContent.scss +5 -0
  53. package/src/lib/components/Modal/ModalFooter.jsx +42 -0
  54. package/src/lib/components/Modal/ModalFooter.scss +35 -0
  55. package/src/lib/components/Modal/ModalHeader.jsx +44 -0
  56. package/src/lib/components/Modal/ModalHeader.scss +30 -0
  57. package/src/lib/components/Modal/ModalTitle.jsx +44 -0
  58. package/src/lib/components/Modal/ModalTitle.scss +10 -0
  59. package/src/lib/components/Modal/README.mdx +865 -195
  60. package/src/lib/components/Modal/_helpers/getJustifyClassName.js +19 -0
  61. package/src/lib/components/Modal/_helpers/getScrollingClassName.js +11 -0
  62. package/src/lib/components/Modal/_settings.scss +1 -5
  63. package/src/lib/components/Modal/_theme.scss +6 -0
  64. package/src/lib/components/Modal/index.js +7 -1
  65. package/src/lib/components/Paper/Paper.jsx +5 -9
  66. package/src/lib/components/Paper/Paper.scss +2 -2
  67. package/src/lib/components/Paper/README.mdx +15 -1
  68. package/src/lib/components/Paper/index.js +1 -1
  69. package/src/lib/components/Popover/Popover.jsx +14 -30
  70. package/src/lib/components/Popover/Popover.scss +7 -6
  71. package/src/lib/components/Popover/PopoverWrapper.jsx +5 -12
  72. package/src/lib/components/Popover/PopoverWrapper.scss +3 -0
  73. package/src/lib/components/Popover/README.mdx +32 -11
  74. package/src/lib/components/Popover/_theme.scss +1 -1
  75. package/src/lib/components/Radio/README.mdx +13 -6
  76. package/src/lib/components/Radio/Radio.jsx +39 -29
  77. package/src/lib/components/Radio/Radio.scss +3 -3
  78. package/src/lib/components/Radio/index.js +1 -1
  79. package/src/lib/components/ScrollView/README.mdx +165 -84
  80. package/src/lib/components/ScrollView/ScrollView.jsx +115 -117
  81. package/src/lib/components/ScrollView/ScrollView.scss +18 -16
  82. package/src/lib/components/ScrollView/index.js +1 -1
  83. package/src/lib/components/SelectField/README.mdx +83 -7
  84. package/src/lib/components/SelectField/SelectField.jsx +86 -61
  85. package/src/lib/components/SelectField/SelectField.scss +8 -8
  86. package/src/lib/components/SelectField/_components/Option/Option.jsx +46 -0
  87. package/src/lib/components/SelectField/_components/Option/index.js +1 -0
  88. package/src/lib/components/SelectField/index.js +1 -1
  89. package/src/lib/components/Table/README.mdx +25 -9
  90. package/src/lib/components/Table/Table.jsx +43 -101
  91. package/src/lib/components/Table/Table.scss +0 -24
  92. package/src/lib/components/Table/_components/TableBodyCell/TableBodyCell.jsx +46 -0
  93. package/src/lib/components/Table/_components/TableBodyCell/index.js +1 -0
  94. package/src/lib/components/Table/_components/TableCell.scss +25 -0
  95. package/src/lib/components/Table/_components/TableHeaderCell/TableHeaderCell.jsx +71 -0
  96. package/src/lib/components/Table/_components/TableHeaderCell/index.js +1 -0
  97. package/src/lib/components/Table/index.js +1 -1
  98. package/src/lib/components/Tabs/README.mdx +21 -3
  99. package/src/lib/components/Tabs/Tabs.jsx +6 -1
  100. package/src/lib/components/Tabs/TabsItem.jsx +3 -0
  101. package/src/lib/components/Tabs/TabsItem.scss +1 -2
  102. package/src/lib/components/Text/README.mdx +25 -7
  103. package/src/lib/components/Text/Text.jsx +3 -7
  104. package/src/lib/components/Text/Text.scss +6 -6
  105. package/src/lib/components/Text/_helpers/getRootClampClassName.js +2 -2
  106. package/src/lib/components/Text/_helpers/getRootHyphensClassName.js +2 -2
  107. package/src/lib/components/Text/_helpers/getRootWordWrappingClassName.js +2 -2
  108. package/src/lib/components/Text/index.js +1 -1
  109. package/src/lib/components/TextArea/README.mdx +34 -31
  110. package/src/lib/components/TextArea/TextArea.jsx +23 -63
  111. package/src/lib/components/TextArea/TextArea.scss +8 -8
  112. package/src/lib/components/TextArea/index.js +1 -1
  113. package/src/lib/components/TextField/README.mdx +56 -54
  114. package/src/lib/components/TextField/TextField.jsx +25 -52
  115. package/src/lib/components/TextField/TextField.scss +9 -9
  116. package/src/lib/components/TextField/index.js +1 -1
  117. package/src/lib/components/TextLink/README.mdx +13 -6
  118. package/src/lib/components/TextLink/TextLink.jsx +0 -10
  119. package/src/lib/components/TextLink/index.js +1 -1
  120. package/src/lib/components/Toggle/README.mdx +18 -6
  121. package/src/lib/components/Toggle/Toggle.jsx +18 -44
  122. package/src/lib/components/Toggle/index.js +1 -1
  123. package/src/lib/components/Toolbar/README.mdx +21 -6
  124. package/src/lib/components/Toolbar/Toolbar.jsx +9 -43
  125. package/src/lib/components/Toolbar/Toolbar.scss +24 -12
  126. package/src/lib/components/Toolbar/ToolbarGroup.jsx +7 -26
  127. package/src/lib/components/Toolbar/ToolbarItem.jsx +3 -7
  128. package/src/lib/components/Toolbar/_helpers/getAlignClassName.js +19 -0
  129. package/src/lib/components/Toolbar/_helpers/getJustifyClassName.js +16 -0
  130. package/src/lib/components/_helpers/getRootColorClassName.js +10 -10
  131. package/src/lib/components/_helpers/getRootSizeClassName.js +3 -3
  132. package/src/lib/components/_helpers/transferProps.js +1 -1
  133. package/src/lib/index.js +24 -16
  134. package/src/lib/provider/withGlobalProps.jsx +20 -3
  135. package/src/lib/styles/tools/form-fields/_box-field-layout.scss +15 -15
  136. package/src/lib/styles/tools/form-fields/_inline-field-elements.scss +1 -1
  137. package/src/lib/styles/tools/form-fields/_inline-field-layout.scss +9 -9
  138. package/src/lib/theme.scss +18 -26
  139. package/src/lib/translations/en.js +1 -1
  140. package/src/lib/components/Grid/_theme.scss +0 -11
  141. package/src/lib/components/ScrollView/_theme.scss +0 -2
  142. package/src/lib/components/withForwardedRef.jsx +0 -11
@@ -6,21 +6,44 @@ route: /components/modal
6
6
 
7
7
  # Modal
8
8
 
9
- Modal allows to prompt users to take or complete an action.
9
+ Modal allows prompting users to take or complete an action.
10
10
 
11
11
  import {
12
12
  Playground,
13
13
  Props,
14
14
  } from 'docz'
15
- import Button from '../Button'
16
- import { Modal } from './Modal'
15
+ import {
16
+ Button,
17
+ FormLayout,
18
+ Modal,
19
+ ModalBody,
20
+ ModalCloseButton,
21
+ ModalContent,
22
+ ModalFooter,
23
+ ModalHeader,
24
+ ModalTitle,
25
+ ScrollView,
26
+ TextField,
27
+ Toolbar,
28
+ ToolbarGroup,
29
+ ToolbarItem,
30
+ } from '../..'
31
+ import Icon from '../../../docs/_components/Icon'
17
32
 
18
33
  ## Basic Usage
19
34
 
20
35
  To implement the Modal component, you need to import it first:
21
36
 
22
37
  ```js
23
- import { Modal } from '@react-ui-org/react-ui';
38
+ import {
39
+ Modal,
40
+ ModalHeader,
41
+ ModalBody,
42
+ ModalCloseButton,
43
+ ModalContent,
44
+ ModalFooter,
45
+ ModalTitle,
46
+ } from '@react-ui-org/react-ui';
24
47
  ```
25
48
 
26
49
  And use it:
@@ -28,6 +51,8 @@ And use it:
28
51
  <Playground>
29
52
  {() => {
30
53
  const [modalOpen, setModalOpen] = React.useState(false);
54
+ const modalPrimaryButtonRef = React.useRef();
55
+ const modalCloseButtonRef = React.useRef();
31
56
  return (
32
57
  <>
33
58
  <Button
@@ -38,18 +63,35 @@ And use it:
38
63
  <div>
39
64
  {modalOpen && (
40
65
  <Modal
41
- actions={[{
42
- color: 'danger',
43
- label: 'Delete',
44
- onClick: () => setModalOpen(false),
45
- }]}
46
- onClose={() => setModalOpen(false)}
47
- title="Warning"
66
+ closeButtonRef={modalCloseButtonRef}
67
+ primaryButtonRef={modalPrimaryButtonRef}
48
68
  >
49
- <p>
50
- Do you really want to delete the user <code>admin</code>?
51
- This cannot be undone.
52
- </p>
69
+ <ModalHeader>
70
+ <ModalTitle>Delete the user?</ModalTitle>
71
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
72
+ </ModalHeader>
73
+ <ModalBody>
74
+ <ModalContent>
75
+ <p>
76
+ Do you really want to delete the user <code>admin</code>?
77
+ This cannot be undone.
78
+ </p>
79
+ </ModalContent>
80
+ </ModalBody>
81
+ <ModalFooter>
82
+ <Button
83
+ color="danger"
84
+ label="Delete"
85
+ onClick={() => setModalOpen(false)}
86
+ ref={modalPrimaryButtonRef}
87
+ />
88
+ <Button
89
+ label="Close"
90
+ onClick={() => setModalOpen(false)}
91
+ priority="outline"
92
+ ref={modalCloseButtonRef}
93
+ />
94
+ </ModalFooter>
53
95
  </Modal>
54
96
  )}
55
97
  </div>
@@ -62,25 +104,449 @@ See [API](#api) for all available options.
62
104
 
63
105
  ## General Guidelines
64
106
 
65
- - Use modals to **confirm an action,** display a **blocking alert**, or to
66
- reveal **contextual options or settings** that cannot be displayed inline
67
- with the parent content.
107
+ - Use modals to **confirm an action,** display a **blocking alert**, or reveal
108
+ **contextual options or settings** that cannot be displayed in line with the
109
+ parent content.
68
110
 
69
111
  - **The title** should communicate the **purpose of the modal** rather than a
70
112
  generic text. Eg. “Delete the user?” tells more than “Are you sure?” or
71
113
  “Warning”.
72
114
 
73
- - **Modal actions** should correspond to the modal purpose, too. Eg. “Delete”
115
+ - **Modal actions** should correspond to the modal purpose, too. E.g. “Delete”
74
116
  tells better what happens rather than “OK”.
75
117
 
76
118
  - Modal **automatically focuses the first form field** by default which allows
77
- users to confirm the modal by hitting enter key (requires an action of type
78
- `submit`). When no field is found then the first button in footer is focused.
79
- To turn this feature off, set the `autofocus` prop to `false`.
119
+ users to confirm the modal by hitting the enter key. When no field is found
120
+ then the primary button (in the footer) is focused. To turn this feature off,
121
+ set the `autofocus` prop to `false`.
80
122
 
81
123
  - **Avoid stacking** of modals. While it may technically work, the modal is just
82
124
  not designed for that.
83
125
 
126
+ ## Composition
127
+
128
+ Modal is decomposed into the following components:
129
+
130
+ - [Modal](#api)
131
+ - [ModalHeader](#modalheader-1)
132
+ - [ModalTitle](#modaltitle)
133
+ - [ModalCloseButton](#modalclosebutton)
134
+ - [ModalBody](#modalbody-1)
135
+ - [ModalContent](#modalcontent)
136
+ (may be wrapped with [ScrollView](/components/scroll-view))
137
+ - [ModalFooter](#modalfooter-1)
138
+
139
+ Using different combinations, you can compose different kinds of modals,
140
+ e.g. dialog modal, blocking modal, scrollable modal, etc.
141
+
142
+ <Playground>
143
+ {() => {
144
+ const [modalOpen, setModalOpen] = React.useState(null);
145
+ const modalPrimaryButtonRef = React.useRef();
146
+ const modalCloseButtonRef = React.useRef();
147
+ return (
148
+ <>
149
+ <Button
150
+ label="Launch blocking modal without title"
151
+ onClick={() => {
152
+ setModalOpen(1);
153
+ setTimeout(() => setModalOpen(null), 2500);
154
+ }}
155
+ priority="outline"
156
+ />
157
+ <Button
158
+ label="Launch blocking modal with title"
159
+ onClick={() => {
160
+ setModalOpen(2);
161
+ setTimeout(() => setModalOpen(null), 3500);
162
+ }}
163
+ priority="outline"
164
+ />
165
+ <Button
166
+ label="Launch modal as dialog"
167
+ onClick={() => setModalOpen(3)}
168
+ priority="outline"
169
+ />
170
+ <Button
171
+ label="Launch modal as form"
172
+ onClick={() => setModalOpen(4)}
173
+ priority="outline"
174
+ />
175
+ <div>
176
+ {modalOpen === 1 && (
177
+ <Modal>
178
+ <ModalBody>
179
+ <ModalContent>
180
+ <p className="text-center">
181
+ Application is being loaded.
182
+ <span className="d-inline-flex align-items-center animation-spin-counterclockwise">
183
+ <Icon icon="loading" />
184
+ </span>
185
+ </p>
186
+ </ModalContent>
187
+ </ModalBody>
188
+ </Modal>
189
+ )}
190
+ {modalOpen === 2 && (
191
+ <Modal>
192
+ <ModalHeader>
193
+ <ModalTitle>Action finished</ModalTitle>
194
+ </ModalHeader>
195
+ <ModalBody>
196
+ <ModalContent>
197
+ <p>
198
+ Action has been successfully finished.
199
+ You will be redirected within a few seconds.
200
+ </p>
201
+ </ModalContent>
202
+ </ModalBody>
203
+ </Modal>
204
+ )}
205
+ {modalOpen === 3 && (
206
+ <Modal
207
+ closeButtonRef={modalCloseButtonRef}
208
+ primaryButtonRef={modalPrimaryButtonRef}
209
+ >
210
+ <ModalHeader>
211
+ <ModalTitle>Delete the user?</ModalTitle>
212
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
213
+ </ModalHeader>
214
+ <ModalBody>
215
+ <ModalContent>
216
+ <p>
217
+ Do you really want to delete the user <code>admin</code>?
218
+ This cannot be undone.
219
+ </p>
220
+ </ModalContent>
221
+ </ModalBody>
222
+ <ModalFooter>
223
+ <Button
224
+ color="danger"
225
+ label="Delete"
226
+ onClick={() => setModalOpen(false)}
227
+ ref={modalPrimaryButtonRef}
228
+ />
229
+ <Button
230
+ label="Close"
231
+ onClick={() => setModalOpen(false)}
232
+ priority="outline"
233
+ ref={modalCloseButtonRef}
234
+ />
235
+ </ModalFooter>
236
+ </Modal>
237
+ )}
238
+ {modalOpen === 4 && (
239
+ <Modal
240
+ closeButtonRef={modalCloseButtonRef}
241
+ primaryButtonRef={modalPrimaryButtonRef}
242
+ >
243
+ <ModalHeader>
244
+ <ModalTitle>Add new user</ModalTitle>
245
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
246
+ </ModalHeader>
247
+ <ModalBody>
248
+ <ModalContent>
249
+ <FormLayout fieldLayout="horizontal">
250
+ <TextField label="Username" />
251
+ <TextField label="Password" type="password" />
252
+ </FormLayout>
253
+ </ModalContent>
254
+ </ModalBody>
255
+ <ModalFooter>
256
+ <Button
257
+ label="Save"
258
+ onClick={() => setModalOpen(false)}
259
+ ref={modalPrimaryButtonRef}
260
+ />
261
+ <Button
262
+ label="Close"
263
+ onClick={() => setModalOpen(false)}
264
+ priority="outline"
265
+ ref={modalCloseButtonRef}
266
+ />
267
+ </ModalFooter>
268
+ </Modal>
269
+ )}
270
+ </div>
271
+ </>
272
+ );
273
+ }}
274
+ </Playground>
275
+
276
+ ### ModalHeader
277
+
278
+ ModalHeader is an optional part of the Modal which allows you to display the title
279
+ of the modal and its close button.
280
+
281
+ It is recommended to compose ModalHeader from the following elements. For title,
282
+ use ModalTitle. For the close button, use ModalCloseButton, however it can
283
+ be omitted if a close button is part of ModalFooter.
284
+
285
+ There are two ways how to position elements within the ModalHeader:
286
+
287
+ 1. You can use provided positioning. Place previously mentioned elements into
288
+ the ModalHeader and use `justify` prop to set up the positioning of those
289
+ elements.
290
+ 2. You can customize positioning using another component (e.g.
291
+ [Toolbar](/components/toolbar)). In that case, set `justify` to `stretch` and
292
+ position elements on your own.
293
+
294
+ <Playground>
295
+ {() => {
296
+ const [modalOpen, setModalOpen] = React.useState(false);
297
+ const [variant, setVariant] = React.useState(null);
298
+ const modalPrimaryButtonRef = React.useRef();
299
+ const modalCloseButtonRef = React.useRef();
300
+ return (
301
+ <>
302
+ <Button
303
+ label="Launch with close button"
304
+ onClick={() => {
305
+ setModalOpen(true);
306
+ setVariant(1);
307
+ }}
308
+ priority="outline"
309
+ />
310
+ <Button
311
+ label="Launch without close button"
312
+ onClick={() => {
313
+ setModalOpen(true);
314
+ setVariant(2);
315
+ }}
316
+ priority="outline"
317
+ />
318
+ <Button
319
+ label="Launch without close button and with centered title"
320
+ onClick={() => {
321
+ setModalOpen(true);
322
+ setVariant(3);
323
+ }}
324
+ priority="outline"
325
+ />
326
+ <Button
327
+ label="Launch with custom layout"
328
+ onClick={() => {
329
+ setModalOpen(true);
330
+ setVariant(4);
331
+ }}
332
+ priority="outline"
333
+ />
334
+ <div>
335
+ {modalOpen && (
336
+ <Modal
337
+ closeButtonRef={modalCloseButtonRef}
338
+ primaryButtonRef={modalPrimaryButtonRef}
339
+ >
340
+ {variant === 1 && (
341
+ <ModalHeader>
342
+ <ModalTitle>Delete the user?</ModalTitle>
343
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
344
+ </ModalHeader>
345
+ )}
346
+ {variant === 2 && (
347
+ <ModalHeader>
348
+ <ModalTitle>Delete the user?</ModalTitle>
349
+ </ModalHeader>
350
+ )}
351
+ {variant === 3 && (
352
+ <ModalHeader justify="center">
353
+ <ModalTitle>Delete the user?</ModalTitle>
354
+ </ModalHeader>
355
+ )}
356
+ {variant === 4 && (
357
+ <ModalHeader justify="stretch">
358
+ <Toolbar justify="space-between">
359
+ <ToolbarItem>
360
+ {''}
361
+ </ToolbarItem>
362
+ <ToolbarItem>
363
+ <ModalTitle>Delete the user?</ModalTitle>
364
+ </ToolbarItem>
365
+ <ToolbarItem>
366
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
367
+ </ToolbarItem>
368
+ </Toolbar>
369
+ </ModalHeader>
370
+ )}
371
+ <ModalBody>
372
+ <ModalContent>
373
+ <p>
374
+ Do you really want to delete the user <code>admin</code>?
375
+ This cannot be undone.
376
+ </p>
377
+ </ModalContent>
378
+ </ModalBody>
379
+ <ModalFooter>
380
+ <Button
381
+ color="danger"
382
+ label="Delete"
383
+ onClick={() => setModalOpen(false)}
384
+ ref={modalPrimaryButtonRef}
385
+ />
386
+ <Button
387
+ label="Close"
388
+ onClick={() => setModalOpen(false)}
389
+ priority="outline"
390
+ ref={modalCloseButtonRef}
391
+ />
392
+ </ModalFooter>
393
+ </Modal>
394
+ )}
395
+ </div>
396
+ </>
397
+ );
398
+ }}
399
+ </Playground>
400
+
401
+ ### ModalBody
402
+
403
+ ModalBody is a mandatory part of the Modal which allows you to display the
404
+ content of the Modal.
405
+
406
+ Although the ModalBody allows you to display arbitrary content, you should not
407
+ place content directly into the ModalBody, but wrap it with ModalContent first.
408
+
409
+ In case your content is expected to be long, consider wrapping ModalContent
410
+ with ScrollView. Check [Scrolling Long Content](#scrolling-long-content) section
411
+ below.
412
+
413
+ ### ModalFooter
414
+
415
+ ModalFooter is an optional part of the Modal which allows you to display
416
+ user actions.
417
+
418
+ There are two ways to position buttons within the ModalFooter:
419
+
420
+ 1. You can use provided positioning. Place Button component (or any arbitrary
421
+ element) and use `justify` prop to set up the positioning of those elements.
422
+ 2. You can customize positioning using another component (e.g.
423
+ [Toolbar](/components/toolbar)). In that case, set `justify` to `stretch`
424
+ and position elements on your own.
425
+
426
+ <Playground>
427
+ {() => {
428
+ const [modalOpen, setModalOpen] = React.useState(false);
429
+ const [modalJustify, setModalJustify] = React.useState('center');
430
+ const modalPrimaryButtonRef = React.useRef();
431
+ const modalCloseButtonRef = React.useRef();
432
+ return (
433
+ <>
434
+ <Button
435
+ label="Launch with footer justified to start"
436
+ onClick={() => {
437
+ setModalOpen(true);
438
+ setModalJustify('start');
439
+ }}
440
+ priority="outline"
441
+ />
442
+ <Button
443
+ label="Launch with footer justified to center"
444
+ onClick={() => {
445
+ setModalOpen(true);
446
+ setModalJustify('center');
447
+ }}
448
+ priority="outline"
449
+ />
450
+ <Button
451
+ label="Launch with footer justified to end"
452
+ onClick={() => {
453
+ setModalOpen(true);
454
+ setModalJustify('end');
455
+ }}
456
+ priority="outline"
457
+ />
458
+ <Button
459
+ label="Launch with footer justified with space between"
460
+ onClick={() => {
461
+ setModalOpen(true);
462
+ setModalJustify('space-between');
463
+ }}
464
+ priority="outline"
465
+ />
466
+ <Button
467
+ label="Launch with footer with custom layout"
468
+ onClick={() => {
469
+ setModalOpen(true);
470
+ setModalJustify('stretch');
471
+ }}
472
+ priority="outline"
473
+ />
474
+ <div>
475
+ {modalOpen && (
476
+ <Modal
477
+ closeButtonRef={modalCloseButtonRef}
478
+ primaryButtonRef={modalPrimaryButtonRef}
479
+ >
480
+ <ModalHeader>
481
+ <ModalTitle>Delete the user?</ModalTitle>
482
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
483
+ </ModalHeader>
484
+ <ModalBody>
485
+ <ModalContent>
486
+ <p>
487
+ Do you really want to delete the user <code>admin</code>?
488
+ This cannot be undone.
489
+ </p>
490
+ </ModalContent>
491
+ </ModalBody>
492
+ <ModalFooter justify={modalJustify}>
493
+ {
494
+ modalJustify === 'stretch'
495
+ ? (
496
+ <Toolbar justify="space-between">
497
+ <ToolbarGroup>
498
+ <ToolbarItem>
499
+ <Button
500
+ color="danger"
501
+ label="Delete"
502
+ onClick={() => setModalOpen(false)}
503
+ ref={modalPrimaryButtonRef}
504
+ />
505
+ </ToolbarItem>
506
+ <ToolbarItem>
507
+ <Button
508
+ color="warning"
509
+ label="Archive"
510
+ onClick={() => setModalOpen(false)}
511
+ ref={modalPrimaryButtonRef}
512
+ />
513
+ </ToolbarItem>
514
+ </ToolbarGroup>
515
+ <ToolbarItem>
516
+ <Button
517
+ label="Close"
518
+ onClick={() => setModalOpen(false)}
519
+ priority="outline"
520
+ ref={modalCloseButtonRef}
521
+ />
522
+ </ToolbarItem>
523
+ </Toolbar>
524
+ ) : (
525
+ <>
526
+ <Button
527
+ color="danger"
528
+ label="Delete"
529
+ onClick={() => setModalOpen(false)}
530
+ ref={modalPrimaryButtonRef}
531
+ />
532
+ <Button
533
+ label="Close"
534
+ onClick={() => setModalOpen(false)}
535
+ priority="outline"
536
+ ref={modalCloseButtonRef}
537
+ />
538
+ </>
539
+ )
540
+ }
541
+ </ModalFooter>
542
+ </Modal>
543
+ )}
544
+ </div>
545
+ </>
546
+ );
547
+ }}
548
+ </Playground>
549
+
84
550
  ## Sizes
85
551
 
86
552
  Modal is available in three fixed-width sizes: small, medium, large and fullscreen.
@@ -90,6 +556,8 @@ Modals of any size automatically shrink when they cannot fit the screen width.
90
556
  {() => {
91
557
  const [modalOpen, setModalOpen] = React.useState(false);
92
558
  const [modalSize, setModalSize] = React.useState('small');
559
+ const modalPrimaryButtonRef = React.useRef();
560
+ const modalCloseButtonRef = React.useRef();
93
561
  return (
94
562
  <>
95
563
  <Button
@@ -127,19 +595,36 @@ Modals of any size automatically shrink when they cannot fit the screen width.
127
595
  <div>
128
596
  {modalOpen && (
129
597
  <Modal
130
- actions={[{
131
- color: 'danger',
132
- label: 'Delete',
133
- onClick: () => setModalOpen(false),
134
- }]}
135
- onClose={() => setModalOpen(false)}
598
+ closeButtonRef={modalCloseButtonRef}
599
+ primaryButtonRef={modalPrimaryButtonRef}
136
600
  size={modalSize}
137
- title="Delete the user?"
138
601
  >
139
- <p>
140
- Do you really want to delete the user <code>admin</code>?
141
- This cannot be undone.
142
- </p>
602
+ <ModalHeader>
603
+ <ModalTitle>Delete the user?</ModalTitle>
604
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
605
+ </ModalHeader>
606
+ <ModalBody>
607
+ <ModalContent>
608
+ <p>
609
+ Do you really want to delete the user <code>admin</code>?
610
+ This cannot be undone.
611
+ </p>
612
+ </ModalContent>
613
+ </ModalBody>
614
+ <ModalFooter>
615
+ <Button
616
+ color="danger"
617
+ label="Delete"
618
+ onClick={() => setModalOpen(false)}
619
+ ref={modalPrimaryButtonRef}
620
+ />
621
+ <Button
622
+ label="Close"
623
+ onClick={() => setModalOpen(false)}
624
+ priority="outline"
625
+ ref={modalCloseButtonRef}
626
+ />
627
+ </ModalFooter>
143
628
  </Modal>
144
629
  )}
145
630
  </div>
@@ -148,39 +633,117 @@ Modals of any size automatically shrink when they cannot fit the screen width.
148
633
  }}
149
634
  </Playground>
150
635
 
151
- On top of that, the modal is able to adjust to the width of its content.
636
+ On top of that, the modal can adjust to the width of its content.
637
+
638
+ <Playground>
639
+ {() => {
640
+ const [modalOpen, setModalOpen] = React.useState(false);
641
+ const modalPrimaryButtonRef = React.useRef();
642
+ const modalCloseButtonRef = React.useRef();
643
+ return (
644
+ <>
645
+ <Button
646
+ label="Launch auto-width modal"
647
+ onClick={() => setModalOpen(true)}
648
+ priority="outline"
649
+ />
650
+ <div>
651
+ {modalOpen && (
652
+ <Modal
653
+ closeButtonRef={modalCloseButtonRef}
654
+ primaryButtonRef={modalPrimaryButtonRef}
655
+ size="auto"
656
+ >
657
+ <ModalHeader>
658
+ <ModalTitle>Delete the user?</ModalTitle>
659
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
660
+ </ModalHeader>
661
+ <ModalBody>
662
+ <ModalContent>
663
+ <p>
664
+ Do you really want to delete the user <code>admin</code>?
665
+ This cannot be undone.
666
+ </p>
667
+ </ModalContent>
668
+ </ModalBody>
669
+ <ModalFooter>
670
+ <Button
671
+ color="danger"
672
+ label="Delete"
673
+ onClick={() => setModalOpen(false)}
674
+ ref={modalPrimaryButtonRef}
675
+ />
676
+ <Button
677
+ label="Close"
678
+ onClick={() => setModalOpen(false)}
679
+ priority="outline"
680
+ ref={modalCloseButtonRef}
681
+ />
682
+ </ModalFooter>
683
+ </Modal>
684
+ )}
685
+ </div>
686
+ </>
687
+ );
688
+ }}
689
+ </Playground>
152
690
 
153
691
  👉 Please note the auto width may not function correctly in combination with
154
- other auto layout mechanisms, e.g. the auto-width
692
+ other auto-layout mechanisms, e.g. the auto-width
155
693
  [FormLayout](/components/form-layout#label-width). It's just too much
156
- magic that doesn't work together yet 🎩.
694
+ magic that doesn't work together (yet?) 🎩.
695
+
696
+ 👉 Beware of horizontal FormLayout inside `small` modals. While automatic
697
+ overflow handling comes to the rescue in this kind of scenario, you will be
698
+ better off with the combination of auto-sized modal and horizontal FormLayout
699
+ with a fixed label width (i.e. any other than `auto`, see the previous note).
157
700
 
158
701
  <Playground>
159
702
  {() => {
160
703
  const [modalOpen, setModalOpen] = React.useState(false);
704
+ const modalPrimaryButtonRef = React.useRef();
705
+ const modalCloseButtonRef = React.useRef();
161
706
  return (
162
707
  <>
163
708
  <Button
164
- label="Launch auto-width modal"
709
+ label="Launch auto-with modal with a form"
165
710
  onClick={() => setModalOpen(true)}
166
711
  priority="outline"
167
712
  />
168
713
  <div>
169
714
  {modalOpen && (
170
715
  <Modal
171
- actions={[{
172
- color: 'danger',
173
- label: 'Delete',
174
- onClick: () => setModalOpen(false),
175
- }]}
176
- onClose={() => setModalOpen(false)}
716
+ closeButtonRef={modalCloseButtonRef}
717
+ primaryButtonRef={modalPrimaryButtonRef}
177
718
  size="auto"
178
- title="Delete the user?"
179
719
  >
180
- <p>
181
- Do you really want to delete the user <code>admin</code>?
182
- This cannot be undone.
183
- </p>
720
+ <ModalHeader>
721
+ <ModalTitle>Form inside modal</ModalTitle>
722
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
723
+ </ModalHeader>
724
+ <ModalBody>
725
+ <ModalContent>
726
+ <FormLayout fieldLayout="horizontal">
727
+ <TextField label="A form element" />
728
+ <TextField label="Another form element" />
729
+ <TextField label="Yet another one" />
730
+ </FormLayout>
731
+ </ModalContent>
732
+ </ModalBody>
733
+ <ModalFooter>
734
+ <Button
735
+ color="primary"
736
+ label="Save"
737
+ onClick={() => setModalOpen(false)}
738
+ ref={modalPrimaryButtonRef}
739
+ />
740
+ <Button
741
+ label="Cancel"
742
+ onClick={() => setModalOpen(false)}
743
+ priority="outline"
744
+ ref={modalCloseButtonRef}
745
+ />
746
+ </ModalFooter>
184
747
  </Modal>
185
748
  )}
186
749
  </div>
@@ -191,12 +754,14 @@ magic that doesn't work together yet 🎩.
191
754
 
192
755
  ## Position
193
756
 
194
- Modal can be aligned either to top or center of the screen.
757
+ Modal can be aligned either to the top or center of the screen.
195
758
 
196
759
  <Playground>
197
760
  {() => {
198
761
  const [modalOpen, setModalOpen] = React.useState(false);
199
762
  const [modalPosition, setModalPosition] = React.useState('center');
763
+ const modalPrimaryButtonRef = React.useRef();
764
+ const modalCloseButtonRef = React.useRef();
200
765
  return (
201
766
  <>
202
767
  <Button
@@ -218,19 +783,36 @@ Modal can be aligned either to top or center of the screen.
218
783
  <div>
219
784
  {modalOpen && (
220
785
  <Modal
221
- actions={[{
222
- onClick: () => setModalOpen(false),
223
- label: 'Delete',
224
- color: 'danger',
225
- }]}
226
- onClose={() => setModalOpen(false)}
786
+ closeButtonRef={modalCloseButtonRef}
227
787
  position={modalPosition}
228
- title="Delete the user?"
788
+ primaryButtonRef={modalPrimaryButtonRef}
229
789
  >
230
- <p>
231
- Do you really want to delete the user <code>admin</code>?
232
- This cannot be undone.
233
- </p>
790
+ <ModalHeader>
791
+ <ModalTitle>Delete the user?</ModalTitle>
792
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
793
+ </ModalHeader>
794
+ <ModalBody>
795
+ <ModalContent>
796
+ <p>
797
+ Do you really want to delete the user <code>admin</code>?
798
+ This cannot be undone.
799
+ </p>
800
+ </ModalContent>
801
+ </ModalBody>
802
+ <ModalFooter>
803
+ <Button
804
+ color="danger"
805
+ label="Delete"
806
+ onClick={() => setModalOpen(false)}
807
+ ref={modalPrimaryButtonRef}
808
+ />
809
+ <Button
810
+ label="Close"
811
+ onClick={() => setModalOpen(false)}
812
+ priority="outline"
813
+ ref={modalCloseButtonRef}
814
+ />
815
+ </ModalFooter>
234
816
  </Modal>
235
817
  )}
236
818
  </div>
@@ -239,33 +821,146 @@ Modal can be aligned either to top or center of the screen.
239
821
  }}
240
822
  </Playground>
241
823
 
824
+ ## Keyboard Control
825
+
826
+ Modal can be controlled either by mouse or keyboard. To enhance user
827
+ experience, primary action can be fired by pressing `Enter` key and the modal
828
+ can be closed by pressing the `Escape` key.
829
+
830
+ To enable it, you just need to pass a reference to the buttons using
831
+ `primaryButtonRef` and `closeButtonRef` props on Modal. The advantage of passing
832
+ a reference to the button is that if the button is disabled, the key press will
833
+ not fire the event.
834
+
835
+ 👉 We strongly recommend using this feature together with Autofocus for a better
836
+ user experience.
837
+
838
+ ## Autofocus
839
+
840
+ Autofocus is implemented to enhance the user experience by automatically
841
+ focussing an element within the modal.
842
+
843
+ How does it work? It tries to find `input`, `textarea`, and `select` elements
844
+ inside of Modal and moves focus into the first found. If none is found and the
845
+ `primaryButtonRef` prop on Modal is set, then the primary button is focused.
846
+
847
+ Autofocus is enabled by default, so if you want to control the focus of
848
+ elements manually, set the `autoFocus` prop on Modal to `false`.
849
+
242
850
  ## Scrolling Long Content
243
851
 
244
852
  When modals become too long for the user's viewport or device, they scroll
245
- independent of the page itself.
853
+ independent of the page itself. This can be done in three ways using the
854
+ `scrolling` option of the ModalBody component:
246
855
 
247
- The inner implementation of body scrolling uses a partially
248
- [customizable](#api) instance of the [ScrollView](/components/scroll-view)
249
- component.
856
+ - `auto` (default) ModalBody is responsible for scrolling,
857
+ - `custom` you must provide a custom component to handle scrolling,
858
+ typically an instance of [ScrollView](/components/scroll-view) wrapping
859
+ ModalContent,
860
+ - `none` — entire Modal is responsible for scrolling.
250
861
 
251
862
  <Playground>
252
863
  {() => {
253
864
  const [modalOpen, setModalOpen] = React.useState(false);
254
- const [modalScrollView, setModalScrollView] = React.useState(true);
865
+ const [modalScrolling, setModalScrolling] = React.useState('auto');
866
+ const modalCloseButtonRef = React.useRef();
867
+ const modalPrimaryButtonRef = React.useRef();
868
+ const modalContent = (
869
+ <ModalContent>
870
+ <p>
871
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
872
+ commodo ligula eget dolor. Aenean massa.
873
+ </p>
874
+ <p>
875
+ Cum sociis natoque penatibus et magnis dis parturient montes,
876
+ nascetur ridiculus mus. Donec quam felis, ultricies nec,
877
+ pellentesque eu, pretium quis, sem.
878
+ </p>
879
+ <p>
880
+ Nulla consequat massa quis enim. Donec pede justo, fringilla
881
+ vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
882
+ ut, imperdiet a, venenatis vitae, justo.
883
+ </p>
884
+ <p>
885
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
886
+ commodo ligula eget dolor. Aenean massa.
887
+ </p>
888
+ <p>
889
+ Cum sociis natoque penatibus et magnis dis parturient montes,
890
+ nascetur ridiculus mus. Donec quam felis, ultricies nec,
891
+ pellentesque eu, pretium quis, sem.
892
+ </p>
893
+ <p>
894
+ Nulla consequat massa quis enim. Donec pede justo, fringilla
895
+ vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
896
+ ut, imperdiet a, venenatis vitae, justo.
897
+ </p>
898
+ <p>
899
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
900
+ commodo ligula eget dolor. Aenean massa.
901
+ </p>
902
+ <p>
903
+ Cum sociis natoque penatibus et magnis dis parturient montes,
904
+ nascetur ridiculus mus. Donec quam felis, ultricies nec,
905
+ pellentesque eu, pretium quis, sem.
906
+ </p>
907
+ <p>
908
+ Nulla consequat massa quis enim. Donec pede justo, fringilla
909
+ vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
910
+ ut, imperdiet a, venenatis vitae, justo.
911
+ </p>
912
+ <p>
913
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
914
+ commodo ligula eget dolor. Aenean massa.
915
+ </p>
916
+ <p>
917
+ Cum sociis natoque penatibus et magnis dis parturient montes,
918
+ nascetur ridiculus mus. Donec quam felis, ultricies nec,
919
+ pellentesque eu, pretium quis, sem.
920
+ </p>
921
+ <p>
922
+ Nulla consequat massa quis enim. Donec pede justo, fringilla
923
+ vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
924
+ ut, imperdiet a, venenatis vitae, justo.
925
+ </p>
926
+ <p>
927
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
928
+ commodo ligula eget dolor. Aenean massa.
929
+ </p>
930
+ <p>
931
+ Cum sociis natoque penatibus et magnis dis parturient montes,
932
+ nascetur ridiculus mus. Donec quam felis, ultricies nec,
933
+ pellentesque eu, pretium quis, sem.
934
+ </p>
935
+ <p>
936
+ Nulla consequat massa quis enim. Donec pede justo, fringilla
937
+ vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
938
+ ut, imperdiet a, venenatis vitae, justo.
939
+ </p>
940
+ </ModalContent>
941
+ )
255
942
  return (
256
943
  <>
257
944
  <Button
258
945
  label="Launch modal with scrolling body"
259
946
  onClick={() => {
260
- setModalScrollView(true);
947
+ setModalScrolling('auto');
261
948
  setModalOpen(true);
262
949
  }}
263
950
  priority="outline"
264
951
  />
265
952
  <Button
266
- label="Launch scrolling modal"
953
+ label="Launch modal with ScrollView"
267
954
  onClick={() => {
268
- setModalScrollView(false);
955
+ setModalScrolling('custom');
956
+ setModalOpen(true);
957
+ }}
958
+ priority="outline"
959
+ />
960
+ <Button
961
+ label="Launch modal with non-scrolling body"
962
+ onClick={() => {
963
+ setModalScrolling('none');
269
964
  setModalOpen(true);
270
965
  }}
271
966
  priority="outline"
@@ -273,86 +968,39 @@ component.
273
968
  <div>
274
969
  {modalOpen && (
275
970
  <Modal
276
- actions={[{
277
- label: 'OK',
278
- onClick: () => setModalOpen(false),
279
- }]}
280
971
  autoFocus={false}
281
- onClose={() => setModalOpen(false)}
282
- scrollView={modalScrollView ? undefined : null}
972
+ closeButtonRef={modalCloseButtonRef}
973
+ primaryButtonRef={modalPrimaryButtonRef}
283
974
  size="small"
284
- title="Modal with long content"
285
975
  >
286
- <p>
287
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
288
- commodo ligula eget dolor. Aenean massa.
289
- </p>
290
- <p>
291
- Cum sociis natoque penatibus et magnis dis parturient montes,
292
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
293
- pellentesque eu, pretium quis, sem.
294
- </p>
295
- <p>
296
- Nulla consequat massa quis enim. Donec pede justo, fringilla
297
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
298
- ut, imperdiet a, venenatis vitae, justo.
299
- </p>
300
- <p>
301
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
302
- commodo ligula eget dolor. Aenean massa.
303
- </p>
304
- <p>
305
- Cum sociis natoque penatibus et magnis dis parturient montes,
306
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
307
- pellentesque eu, pretium quis, sem.
308
- </p>
309
- <p>
310
- Nulla consequat massa quis enim. Donec pede justo, fringilla
311
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
312
- ut, imperdiet a, venenatis vitae, justo.
313
- </p>
314
- <p>
315
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
316
- commodo ligula eget dolor. Aenean massa.
317
- </p>
318
- <p>
319
- Cum sociis natoque penatibus et magnis dis parturient montes,
320
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
321
- pellentesque eu, pretium quis, sem.
322
- </p>
323
- <p>
324
- Nulla consequat massa quis enim. Donec pede justo, fringilla
325
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
326
- ut, imperdiet a, venenatis vitae, justo.
327
- </p>
328
- <p>
329
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
330
- commodo ligula eget dolor. Aenean massa.
331
- </p>
332
- <p>
333
- Cum sociis natoque penatibus et magnis dis parturient montes,
334
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
335
- pellentesque eu, pretium quis, sem.
336
- </p>
337
- <p>
338
- Nulla consequat massa quis enim. Donec pede justo, fringilla
339
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
340
- ut, imperdiet a, venenatis vitae, justo.
341
- </p>
342
- <p>
343
- Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean
344
- commodo ligula eget dolor. Aenean massa.
345
- </p>
346
- <p>
347
- Cum sociis natoque penatibus et magnis dis parturient montes,
348
- nascetur ridiculus mus. Donec quam felis, ultricies nec,
349
- pellentesque eu, pretium quis, sem.
350
- </p>
351
- <p>
352
- Nulla consequat massa quis enim. Donec pede justo, fringilla
353
- vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus
354
- ut, imperdiet a, venenatis vitae, justo.
355
- </p>
976
+ <ModalHeader>
977
+ <ModalTitle>Modal with long content</ModalTitle>
978
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
979
+ </ModalHeader>
980
+ <ModalBody scrolling={modalScrolling}>
981
+ {
982
+ modalScrolling === 'custom'
983
+ ? (
984
+ <ScrollView>
985
+ {modalContent}
986
+ </ScrollView>
987
+ )
988
+ : modalContent
989
+ }
990
+ </ModalBody>
991
+ <ModalFooter>
992
+ <Button
993
+ label="OK"
994
+ onClick={() => setModalOpen(false)}
995
+ ref={modalPrimaryButtonRef}
996
+ />
997
+ <Button
998
+ label="Close"
999
+ onClick={() => setModalOpen(false)}
1000
+ priority="outline"
1001
+ ref={modalCloseButtonRef}
1002
+ />
1003
+ </ModalFooter>
356
1004
  </Modal>
357
1005
  )}
358
1006
  </div>
@@ -363,63 +1011,85 @@ component.
363
1011
 
364
1012
  ### Long Content and Autofocus
365
1013
 
366
- 👉 If you set the `scrollMode` to `modal`, you may want to turn `autoFocus` off
367
- to prevent the modal from scrolling to the end immediately after being opened.
1014
+ 👉 If you wrap ModalContent with ScrollView, you may want to turn `autoFocus`
1015
+ off to prevent the modal from scrolling to the end immediately after being
1016
+ opened.
368
1017
 
369
- ## Blocking Modals
1018
+ <!-- markdownlint-disable MD024 -->
370
1019
 
371
- There are situations when you need to interrupt user's flow and lock entire UI
372
- to prevent interaction. That's where blocking modals may come handy.
1020
+ ## Forwarding HTML Attributes
373
1021
 
374
- <Playground>
375
- {() => {
376
- const [modalOpen, setModalOpen] = React.useState(false);
377
- return (
378
- <>
379
- <Button
380
- label="Launch blocking modal"
381
- onClick={() => setModalOpen(true)}
382
- priority="outline"
383
- />
384
- <div>
385
- {modalOpen && (
386
- <Modal title="Update is in progress" size="auto">
387
- <p>
388
- <span role="img" aria-label="Warning">⚠️</span>
389
- Please wait a few seconds until the firmware update is finished.
390
- Don&apos;t turn off the device!
391
- </p>
392
- <Button
393
- label="Close the demo"
394
- onClick={() => setModalOpen(false)}
395
- priority="flat"
396
- />
397
- </Modal>
398
- )}
399
- </div>
400
- </>
401
- );
402
- }}
403
- </Playground>
1022
+ In addition to the options below in the [component's API](#api) section, you
1023
+ can specify [React synthetic events] or **any HTML attribute you like.** All
1024
+ attributes that don't interfere with the API are forwarded to the:
1025
+
1026
+ - `<div>` HTML element in case of the `Modal` component. This `<div>` is not the
1027
+ root, but its first child which represents the modal window.
1028
+ - root `<div>` HTML element in case of `ModalHeader`, `ModalBody`, `ModalContent`
1029
+ and `ModalFooter` components.
1030
+ - heading HTML element, which level can be specified through `level` option, in
1031
+ case of the `ModalTitle` component.
1032
+ - native HTML `<button>` in case of the `ModalCloseButton` component.
1033
+
1034
+ This enables making the component interactive and helps to improve its
1035
+ accessibility.
1036
+
1037
+ 👉 Refer to the MDN reference for the full list of supported attributes of the
1038
+ [div], [heading] and [button] element.
404
1039
 
405
1040
  ## API
406
1041
 
407
1042
  <Props table of={Modal} />
408
1043
 
1044
+ ### ModalHeader
1045
+
1046
+ <Props table of={ModalHeader} />
1047
+
1048
+ ### ModalTitle
1049
+
1050
+ <Props table of={ModalTitle} />
1051
+
1052
+ ### ModalCloseButton
1053
+
1054
+ <Props table of={ModalCloseButton} />
1055
+
1056
+ ### ModalBody
1057
+
1058
+ <Props table of={ModalBody} />
1059
+
1060
+ ### ModalContent
1061
+
1062
+ <Props table of={ModalContent} />
1063
+
1064
+ ### ModalFooter
1065
+
1066
+ <Props table of={ModalFooter} />
1067
+
409
1068
  ## Theming
410
1069
 
411
- | Custom Property | Description |
412
- |------------------------------------------------------|--------------------------------------------------------------|
413
- | `--rui-Modal__background` | Modal background (including `url()` or gradient) |
414
- | `--rui-Modal__box-shadow` | Modal box shadow |
415
- | `--rui-Modal__outer-spacing-xs` | Spacing around modal, `xs` screen size |
416
- | `--rui-Modal__outer-spacing-sm` | Spacing around modal, `sm` screen size and bigger |
417
- | `--rui-Modal__footer__background` | Modal footer background (including `url()` or gradient) |
418
- | `--rui-Modal__backdrop__background` | Modal backdrop background (including `url()` or gradient) |
419
- | `--rui-Modal--auto__min-width` | Minimum width of auto-sized modal |
420
- | `--rui-Modal--auto__max-width` | Maximum width of auto-sized modal |
421
- | `--rui-Modal--small__width` | Width of small modal |
422
- | `--rui-Modal--medium__width` | Width of medium modal |
423
- | `--rui-Modal--large__width` | Width of large modal |
424
- | `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
425
- | `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
1070
+ | Custom Property | Description |
1071
+ |------------------------------------------------------|---------------------------------------------------------------|
1072
+ | `--rui-Modal__padding-x` | Inline padding of individual modal components |
1073
+ | `--rui-Modal__padding-y` | Block padding of individual modal components |
1074
+ | `--rui-Modal__background` | Modal background (including `url()` or gradient) |
1075
+ | `--rui-Modal__box-shadow` | Modal box shadow |
1076
+ | `--rui-Modal__separator__width` | Width of separator between modal header, body, and footer |
1077
+ | `--rui-Modal__separator__color` | Color of separator between modal header, body, and footer |
1078
+ | `--rui-Modal__outer-spacing-xs` | Spacing around modal, `xs` screen size |
1079
+ | `--rui-Modal__outer-spacing-sm` | Spacing around modal, `sm` screen size and bigger |
1080
+ | `--rui-Modal__header__gap` | Modal header gap between children |
1081
+ | `--rui-Modal__footer__background` | Modal footer background (including `url()` or gradient) |
1082
+ | `--rui-Modal__footer__gap` | Modal footer gap between children |
1083
+ | `--rui-Modal__backdrop__background` | Modal backdrop background (including `url()` or gradient) |
1084
+ | `--rui-Modal--auto__min-width` | Min width of auto-sized modal (when enough screen estate) |
1085
+ | `--rui-Modal--auto__max-width` | Max width of auto-sized modal (when enough screen estate) |
1086
+ | `--rui-Modal--small__width` | Width of small modal |
1087
+ | `--rui-Modal--medium__width` | Width of medium modal |
1088
+ | `--rui-Modal--large__width` | Width of large modal |
1089
+ | `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
1090
+ | `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
1091
+
1092
+ [React synthetic events]: https://reactjs.org/docs/events.html
1093
+ [div]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes
1094
+ [heading]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#attributes
1095
+ [button]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes