@react-ui-org/react-ui 0.47.0 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
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