@react-ui-org/react-ui 0.57.0 → 0.59.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 (118) hide show
  1. package/.nvmrc +1 -1
  2. package/README.md +2 -11
  3. package/dist/react-ui.css +19 -19
  4. package/dist/react-ui.development.css +1351 -963
  5. package/dist/react-ui.development.js +187 -87
  6. package/dist/react-ui.js +1 -1
  7. package/package.json +16 -5
  8. package/src/components/Alert/Alert.jsx +7 -9
  9. package/src/components/Alert/Alert.module.scss +3 -3
  10. package/src/components/Alert/README.md +18 -32
  11. package/src/components/Alert/_settings.scss +1 -2
  12. package/src/components/Badge/Badge.jsx +3 -3
  13. package/src/components/Button/Button.jsx +3 -3
  14. package/src/components/ButtonGroup/ButtonGroup.jsx +3 -3
  15. package/src/components/Card/Card.jsx +7 -7
  16. package/src/components/Card/Card.module.scss +8 -7
  17. package/src/components/Card/CardBody.jsx +2 -2
  18. package/src/components/Card/CardFooter.jsx +2 -2
  19. package/src/components/Card/README.md +20 -17
  20. package/src/components/Card/_settings.scss +1 -2
  21. package/src/components/Card/_theme.scss +1 -0
  22. package/src/components/CheckboxField/CheckboxField.jsx +11 -5
  23. package/src/components/CheckboxField/README.md +110 -5
  24. package/src/components/FileInputField/FileInputField.jsx +148 -22
  25. package/src/components/FileInputField/FileInputField.module.scss +87 -1
  26. package/src/components/FileInputField/README.md +83 -2
  27. package/src/components/FileInputField/_settings.scss +15 -0
  28. package/src/components/FormLayout/FormLayout.jsx +3 -3
  29. package/src/components/FormLayout/FormLayoutCustomField.jsx +3 -3
  30. package/src/components/FormLayout/README.md +1 -0
  31. package/src/components/Grid/Grid.jsx +2 -2
  32. package/src/components/Grid/Grid.module.scss +2 -2
  33. package/src/components/Grid/GridSpan.jsx +2 -2
  34. package/src/components/InputGroup/InputGroup.jsx +4 -4
  35. package/src/components/InputGroup/InputGroup.module.scss +12 -8
  36. package/src/components/InputGroup/README.md +1 -1
  37. package/src/components/Modal/Modal.jsx +118 -46
  38. package/src/components/Modal/Modal.module.scss +34 -18
  39. package/src/components/Modal/ModalBody.jsx +3 -3
  40. package/src/components/Modal/ModalBody.module.scss +18 -0
  41. package/src/components/Modal/ModalCloseButton.jsx +4 -6
  42. package/src/components/Modal/ModalContent.jsx +2 -2
  43. package/src/components/Modal/ModalFooter.jsx +3 -3
  44. package/src/components/Modal/ModalFooter.module.scss +6 -2
  45. package/src/components/Modal/ModalHeader.jsx +3 -3
  46. package/src/components/Modal/ModalHeader.module.scss +8 -1
  47. package/src/components/Modal/ModalTitle.jsx +2 -2
  48. package/src/components/Modal/README.md +407 -187
  49. package/src/components/Modal/_animations.scss +9 -0
  50. package/src/components/Modal/_helpers/dialogOnCancelHandler.js +28 -0
  51. package/src/components/Modal/_helpers/dialogOnClickHandler.js +46 -0
  52. package/src/components/Modal/_helpers/dialogOnCloseHandler.js +28 -0
  53. package/src/components/Modal/_helpers/dialogOnKeyDownHandler.js +62 -0
  54. package/src/components/Modal/_helpers/getPositionClassName.js +1 -1
  55. package/src/components/Modal/_hooks/useModalFocus.js +24 -91
  56. package/src/components/Modal/_settings.scss +4 -3
  57. package/src/components/Modal/_theme.scss +1 -0
  58. package/src/components/Paper/Paper.jsx +3 -3
  59. package/src/components/Popover/Popover.jsx +60 -15
  60. package/src/components/Popover/Popover.module.scss +37 -9
  61. package/src/components/Popover/PopoverWrapper.jsx +2 -2
  62. package/src/components/Popover/README.md +60 -3
  63. package/src/components/Popover/_helpers/cleanPlacementStyle.js +20 -0
  64. package/src/components/Radio/README.md +103 -0
  65. package/src/components/Radio/Radio.jsx +11 -5
  66. package/src/components/Radio/Radio.module.scss +4 -0
  67. package/src/components/ScrollView/ScrollView.jsx +5 -7
  68. package/src/components/SelectField/README.md +103 -0
  69. package/src/components/SelectField/SelectField.jsx +11 -5
  70. package/src/components/Table/Table.jsx +2 -2
  71. package/src/components/Tabs/Tabs.jsx +2 -2
  72. package/src/components/Tabs/TabsItem.jsx +3 -3
  73. package/src/components/Text/Text.jsx +3 -3
  74. package/src/components/TextArea/TextArea.jsx +3 -3
  75. package/src/components/TextField/README.md +14 -2
  76. package/src/components/TextField/TextField.jsx +3 -3
  77. package/src/components/TextLink/README.md +10 -3
  78. package/src/components/TextLink/TextLink.jsx +2 -2
  79. package/src/components/TextLink/_theme.scss +3 -3
  80. package/src/components/Toggle/README.md +83 -1
  81. package/src/components/Toggle/Toggle.jsx +11 -5
  82. package/src/components/Toolbar/Toolbar.jsx +3 -3
  83. package/src/components/Toolbar/ToolbarGroup.jsx +3 -3
  84. package/src/components/Toolbar/ToolbarItem.jsx +3 -3
  85. package/src/components/_helpers/resolveContextOrProp.js +6 -3
  86. package/src/helpers/classNames/README.md +65 -0
  87. package/src/helpers/classNames/classNames.js +11 -0
  88. package/src/helpers/classNames/index.js +1 -0
  89. package/src/helpers/transferProps/README.md +46 -0
  90. package/src/helpers/transferProps/index.js +1 -0
  91. package/src/index.js +6 -5
  92. package/src/providers/globalProps/GlobalPropsContext.jsx +5 -0
  93. package/src/providers/globalProps/GlobalPropsProvider.jsx +33 -0
  94. package/src/providers/globalProps/index.js +3 -0
  95. package/src/{provider → providers/globalProps}/withGlobalProps.jsx +16 -16
  96. package/src/providers/translations/TranslationsContext.jsx +6 -0
  97. package/src/providers/translations/TranslationsProvider.jsx +33 -0
  98. package/src/providers/translations/index.js +2 -0
  99. package/src/styles/elements/_links.scss +2 -9
  100. package/src/styles/generic/_focus.scss +1 -1
  101. package/src/styles/theme/_form-fields.scss +19 -0
  102. package/src/styles/theme/_links.scss +4 -3
  103. package/src/styles/tools/_accessibility.scss +3 -5
  104. package/src/styles/tools/_collections.scss +62 -5
  105. package/src/styles/tools/_links.scss +17 -0
  106. package/src/styles/tools/form-fields/_box-field-elements.scss +21 -9
  107. package/src/styles/tools/form-fields/_box-field-layout.scss +2 -2
  108. package/src/styles/tools/form-fields/_box-field-sizes.scss +6 -10
  109. package/src/styles/tools/form-fields/_foundation.scss +6 -4
  110. package/src/styles/tools/form-fields/_variants.scss +12 -8
  111. package/src/theme.scss +53 -2
  112. package/src/translations/en.js +5 -0
  113. package/src/provider/RUIContext.jsx +0 -9
  114. package/src/provider/RUIProvider.jsx +0 -42
  115. package/src/provider/index.js +0 -3
  116. package/src/styles/settings/_z-indexes.scss +0 -2
  117. package/src/utils/classNames.js +0 -8
  118. /package/src/{utils → helpers/transferProps}/transferProps.js +0 -0
@@ -30,7 +30,7 @@ React.createElement(() => {
30
30
  React UI docs. You may not need it in your application.
31
31
  */}
32
32
  return (
33
- <RUIProvider globalProps={{
33
+ <GlobalPropsProvider globalProps={{
34
34
  Modal: { preventScrollUnderneath: window.document.documentElement }
35
35
  }}>
36
36
  <Button
@@ -72,7 +72,7 @@ React.createElement(() => {
72
72
  </Modal>
73
73
  )}
74
74
  </div>
75
- </RUIProvider>
75
+ </GlobalPropsProvider>
76
76
  );
77
77
  });
78
78
  ```
@@ -92,11 +92,24 @@ See [API](#api) for all available options.
92
92
  - **Modal actions** should correspond to the modal purpose, too. E.g. “Delete”
93
93
  tells better what happens rather than “OK”.
94
94
 
95
- - Modal **automatically focuses the first non-disabled form field** by default
96
- which allows users to confirm the modal by hitting the enter key. When no
97
- field is found then the primary button (in the footer) is focused. To turn
95
+ - While native `<dialog>` (that is used under the hood) can be present in DOM,
96
+ modal is a more feature-rich component that provides more control over the
97
+ modal behavior and shall be **removed from DOM when closed**.
98
+
99
+ - Modal **automatically focuses the first non-disabled form field** by default.
100
+ When no field is found then the primary button (in the footer) is focused. To turn
98
101
  this feature off, set the `autofocus` prop to `false`.
99
102
 
103
+ - Modal **submits the form when the user presses the `Enter` key** . A click is
104
+ programmatically triggered on the primary button in this case. To turn this
105
+ feature off, set the `allowPrimaryActionOnEnterKey` prop to `false`.
106
+
107
+ - Modal **closes when the user presses the `Escape` key**. A click is
108
+ programmatically triggered on the close button in this case. To turn this
109
+ feature off, set the `allowCloseOnEscapeKey` prop to `false`. Modal can be
110
+ also **closed by clicking on the backdrop**. To turn this feature off,
111
+ set the `allowCloseOnBackdropClick` prop to `false`.
112
+
100
113
  - **Avoid stacking** of modals. While it may technically work, the modal is just
101
114
  not designed for that.
102
115
 
@@ -114,143 +127,8 @@ Modal is decomposed into the following components:
114
127
  - [ModalFooter](#modalfooter)
115
128
 
116
129
  Using different combinations, you can compose different kinds of modals,
117
- e.g. dialog modal, blocking modal, scrollable modal, etc.
118
-
119
- ```docoff-react-preview
120
- React.createElement(() => {
121
- const [modalOpen, setModalOpen] = React.useState(null);
122
- const modalPrimaryButtonRef = React.useRef();
123
- const modalCloseButtonRef = React.useRef();
124
- {/*
125
- The `preventScrollUnderneath` feature is necessary for Modals to work in
126
- React UI docs. You may not need it in your application.
127
- */}
128
- return (
129
- <RUIProvider globalProps={{
130
- Modal: { preventScrollUnderneath: window.document.documentElement }
131
- }}>
132
- <Button
133
- label="Launch blocking modal without title"
134
- onClick={() => {
135
- setModalOpen(1);
136
- setTimeout(() => setModalOpen(null), 2500);
137
- }}
138
- />
139
- <Button
140
- label="Launch blocking modal with title"
141
- onClick={() => {
142
- setModalOpen(2);
143
- setTimeout(() => setModalOpen(null), 3500);
144
- }}
145
- />
146
- <Button
147
- label="Launch modal as dialog"
148
- onClick={() => setModalOpen(3)}
149
- />
150
- <Button
151
- label="Launch modal as form"
152
- onClick={() => setModalOpen(4)}
153
- />
154
- <div>
155
- {modalOpen === 1 && (
156
- <Modal>
157
- <ModalBody>
158
- <ModalContent>
159
- <p className="text-center">
160
- Application is being loaded.
161
- <span className="d-inline-flex align-items-center animation-spin-counterclockwise">
162
- <rui-icon icon="loading" />
163
- </span>
164
- </p>
165
- </ModalContent>
166
- </ModalBody>
167
- </Modal>
168
- )}
169
- {modalOpen === 2 && (
170
- <Modal>
171
- <ModalHeader>
172
- <ModalTitle>Action finished</ModalTitle>
173
- </ModalHeader>
174
- <ModalBody>
175
- <ModalContent>
176
- <p>
177
- Action has been successfully finished.
178
- You will be redirected within a few seconds.
179
- </p>
180
- </ModalContent>
181
- </ModalBody>
182
- </Modal>
183
- )}
184
- {modalOpen === 3 && (
185
- <Modal
186
- closeButtonRef={modalCloseButtonRef}
187
- primaryButtonRef={modalPrimaryButtonRef}
188
- >
189
- <ModalHeader>
190
- <ModalTitle>Delete the user?</ModalTitle>
191
- <ModalCloseButton onClick={() => setModalOpen(false)} />
192
- </ModalHeader>
193
- <ModalBody>
194
- <ModalContent>
195
- <p>
196
- Do you really want to delete the user <code>admin</code>?
197
- This cannot be undone.
198
- </p>
199
- </ModalContent>
200
- </ModalBody>
201
- <ModalFooter>
202
- <Button
203
- color="danger"
204
- label="Delete"
205
- onClick={() => setModalOpen(false)}
206
- ref={modalPrimaryButtonRef}
207
- />
208
- <Button
209
- label="Close"
210
- onClick={() => setModalOpen(false)}
211
- priority="outline"
212
- ref={modalCloseButtonRef}
213
- />
214
- </ModalFooter>
215
- </Modal>
216
- )}
217
- {modalOpen === 4 && (
218
- <Modal
219
- closeButtonRef={modalCloseButtonRef}
220
- primaryButtonRef={modalPrimaryButtonRef}
221
- >
222
- <ModalHeader>
223
- <ModalTitle>Add new user</ModalTitle>
224
- <ModalCloseButton onClick={() => setModalOpen(false)} />
225
- </ModalHeader>
226
- <ModalBody>
227
- <ModalContent>
228
- <FormLayout fieldLayout="horizontal">
229
- <TextField label="Username" />
230
- <TextField label="Password" type="password" />
231
- </FormLayout>
232
- </ModalContent>
233
- </ModalBody>
234
- <ModalFooter>
235
- <Button
236
- label="Save"
237
- onClick={() => setModalOpen(false)}
238
- ref={modalPrimaryButtonRef}
239
- />
240
- <Button
241
- label="Close"
242
- onClick={() => setModalOpen(false)}
243
- priority="outline"
244
- ref={modalCloseButtonRef}
245
- />
246
- </ModalFooter>
247
- </Modal>
248
- )}
249
- </div>
250
- </RUIProvider>
251
- );
252
- });
253
- ```
130
+ e.g. dialog modal, [modal with form](#forms), [blocking modal](#interaction-blocking),
131
+ [scrollable modal](#scrolling-long-content), etc.
254
132
 
255
133
  ### ModalHeader
256
134
 
@@ -281,7 +159,7 @@ React.createElement(() => {
281
159
  React UI docs. You may not need it in your application.
282
160
  */}
283
161
  return (
284
- <RUIProvider globalProps={{
162
+ <GlobalPropsProvider globalProps={{
285
163
  Modal: { preventScrollUnderneath: window.document.documentElement }
286
164
  }}>
287
165
  <Button
@@ -374,7 +252,7 @@ React.createElement(() => {
374
252
  </Modal>
375
253
  )}
376
254
  </div>
377
- </RUIProvider>
255
+ </GlobalPropsProvider>
378
256
  );
379
257
  });
380
258
  ```
@@ -415,7 +293,7 @@ React.createElement(() => {
415
293
  React UI docs. You may not need it in your application.
416
294
  */}
417
295
  return (
418
- <RUIProvider globalProps={{
296
+ <GlobalPropsProvider globalProps={{
419
297
  Modal: { preventScrollUnderneath: window.document.documentElement }
420
298
  }}>
421
299
  <Button
@@ -517,7 +395,7 @@ React.createElement(() => {
517
395
  </Modal>
518
396
  )}
519
397
  </div>
520
- </RUIProvider>
398
+ </GlobalPropsProvider>
521
399
  );
522
400
  });
523
401
  ```
@@ -538,7 +416,7 @@ React.createElement(() => {
538
416
  React UI docs. You may not need it in your application.
539
417
  */}
540
418
  return (
541
- <RUIProvider globalProps={{
419
+ <GlobalPropsProvider globalProps={{
542
420
  Modal: { preventScrollUnderneath: window.document.documentElement }
543
421
  }}>
544
422
  <Button
@@ -605,7 +483,7 @@ React.createElement(() => {
605
483
  </Modal>
606
484
  )}
607
485
  </div>
608
- </RUIProvider>
486
+ </GlobalPropsProvider>
609
487
  );
610
488
  });
611
489
  ```
@@ -622,7 +500,7 @@ React.createElement(() => {
622
500
  React UI docs. You may not need it in your application.
623
501
  */}
624
502
  return (
625
- <RUIProvider globalProps={{
503
+ <GlobalPropsProvider globalProps={{
626
504
  Modal: { preventScrollUnderneath: window.document.documentElement }
627
505
  }}>
628
506
  <Button
@@ -665,7 +543,7 @@ React.createElement(() => {
665
543
  </Modal>
666
544
  )}
667
545
  </div>
668
- </RUIProvider>
546
+ </GlobalPropsProvider>
669
547
  );
670
548
  });
671
549
  ```
@@ -690,7 +568,7 @@ React.createElement(() => {
690
568
  React UI docs. You may not need it in your application.
691
569
  */}
692
570
  return (
693
- <RUIProvider globalProps={{
571
+ <GlobalPropsProvider globalProps={{
694
572
  Modal: { preventScrollUnderneath: window.document.documentElement }
695
573
  }}>
696
574
  <Button
@@ -734,7 +612,7 @@ React.createElement(() => {
734
612
  </Modal>
735
613
  )}
736
614
  </div>
737
- </RUIProvider>
615
+ </GlobalPropsProvider>
738
616
  );
739
617
  });
740
618
  ```
@@ -754,7 +632,7 @@ React.createElement(() => {
754
632
  React UI docs. You may not need it in your application.
755
633
  */}
756
634
  return (
757
- <RUIProvider globalProps={{
635
+ <GlobalPropsProvider globalProps={{
758
636
  Modal: { preventScrollUnderneath: window.document.documentElement }
759
637
  }}>
760
638
  <Button
@@ -807,21 +685,111 @@ React.createElement(() => {
807
685
  </Modal>
808
686
  )}
809
687
  </div>
810
- </RUIProvider>
688
+ </GlobalPropsProvider>
811
689
  );
812
690
  });
813
691
  ```
814
692
 
815
- ## Keyboard Control
693
+ ## Color Variants
694
+
695
+ Modal can be colored using the `color` prop. The `color` prop implements the
696
+ [Feedback color collection](/docs/foundation/collections#colors)
697
+ and is applied to the border of the modal and the modal footer.
698
+
699
+ ```docoff-react-preview
700
+ React.createElement(() => {
701
+ const [modalOpen, setModalOpen] = React.useState(false);
702
+ const [modalColor, setModalColor] = React.useState('success');
703
+ const modalCloseButtonRef = React.useRef();
704
+ {/*
705
+ The `preventScrollUnderneath` feature is necessary for Modals to work in
706
+ React UI docs. You may not need it in your application.
707
+ */}
708
+ return (
709
+ <GlobalPropsProvider globalProps={{
710
+ Modal: { preventScrollUnderneath: window.document.documentElement }
711
+ }}>
712
+ <Button
713
+ label="Launch modal with color options"
714
+ onClick={() => setModalOpen(true)}
715
+ />
716
+ <div>
717
+ {modalOpen && (
718
+ <Modal
719
+ closeButtonRef={modalCloseButtonRef}
720
+ color={modalColor}
721
+ >
722
+ <ModalHeader>
723
+ <ModalTitle>Modal color</ModalTitle>
724
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
725
+ </ModalHeader>
726
+ <ModalBody>
727
+ <ModalContent>
728
+ <Radio
729
+ label="Modal color"
730
+ onChange={(e) => setModalColor(e.target.value)}
731
+ options={[
732
+ {
733
+ label: 'success',
734
+ value: 'success',
735
+ },
736
+ {
737
+ label: 'warning',
738
+ value: 'warning',
739
+ },
740
+ {
741
+ label: 'danger',
742
+ value: 'danger',
743
+ },
744
+ {
745
+ label: 'info',
746
+ value: 'info',
747
+ },
748
+ {
749
+ label: 'help',
750
+ value: 'help',
751
+ },
752
+ {
753
+ label: 'note',
754
+ value: 'note',
755
+ },
756
+ ]}
757
+ value={modalColor}
758
+ />
759
+ </ModalContent>
760
+ </ModalBody>
761
+ <ModalFooter>
762
+ <Button
763
+ color={modalColor}
764
+ label="Close"
765
+ onClick={() => setModalOpen(false)}
766
+ ref={modalCloseButtonRef}
767
+ />
768
+ </ModalFooter>
769
+ </Modal>
770
+ )}
771
+ </div>
772
+ </GlobalPropsProvider>
773
+ );
774
+ });
775
+ ```
776
+
777
+ ## Mouse and Keyboard Control
816
778
 
817
779
  Modal can be controlled either by mouse or keyboard. To enhance user
818
- experience, primary action can be fired by pressing `Enter` key and the modal
819
- can be closed by pressing the `Escape` key.
780
+ experience, primary action can be fired by pressing `Enter` key and
781
+ the modal can be closed by pressing the `Escape` key. Modal can be
782
+ also closed by clicking on the backdrop.
820
783
 
821
784
  To enable it, you just need to pass a reference to the buttons using
822
- `primaryButtonRef` and `closeButtonRef` props on Modal. The advantage of passing
823
- a reference to the button is that if the button is disabled, the key press will
824
- not fire the event.
785
+ `primaryButtonRef` and `closeButtonRef` props on Modal. The advantage
786
+ of passing a reference to the button is that if the button is disabled,
787
+ the key press or the mouse click will not fire the event.
788
+
789
+ As `primaryButtonRef` and `closeButtonRef` are used for more than just
790
+ actions mentioned above, you can explicitly disable the default behavior
791
+ by changing `allowCloseOnBackdropClick`, `allowCloseOnEscapeKey` or
792
+ `allowPrimaryActionOnEnterKey` to `false`.
825
793
 
826
794
  👉 We strongly recommend using this feature together with Autofocus for a better
827
795
  user experience.
@@ -839,7 +807,241 @@ is focused.
839
807
  Autofocus is enabled by default, so if you want to control the focus of
840
808
  elements manually, set the `autoFocus` prop on Modal to `false`.
841
809
 
842
- ## Scrolling Long Content
810
+ ## Use Cases
811
+
812
+ ### Interaction blocking
813
+
814
+ Modal can be used to block user interaction while an action is being
815
+ performed.
816
+
817
+ ```docoff-react-preview
818
+ React.createElement(() => {
819
+ const [modalOpen, setModalOpen] = React.useState(null);
820
+ const modalPrimaryButtonRef = React.useRef();
821
+ const modalCloseButtonRef = React.useRef();
822
+ {/*
823
+ The `preventScrollUnderneath` feature is necessary for Modals to work in
824
+ React UI docs. You may not need it in your application.
825
+ */}
826
+ return (
827
+ <GlobalPropsProvider globalProps={{
828
+ Modal: { preventScrollUnderneath: window.document.documentElement }
829
+ }}>
830
+ <Button
831
+ label="Launch blocking modal without title"
832
+ onClick={() => {
833
+ setModalOpen(1);
834
+ setTimeout(() => setModalOpen(null), 2500);
835
+ }}
836
+ />
837
+ <Button
838
+ label="Launch blocking modal with title"
839
+ onClick={() => {
840
+ setModalOpen(2);
841
+ setTimeout(() => setModalOpen(null), 3500);
842
+ }}
843
+ />
844
+ <div>
845
+ {modalOpen === 1 && (
846
+ <Modal>
847
+ <ModalBody>
848
+ <ModalContent>
849
+ <p className="text-center">
850
+ Application is being loaded.
851
+ <span className="d-inline-flex align-items-center animation-spin-counterclockwise">
852
+ <rui-icon icon="loading" />
853
+ </span>
854
+ </p>
855
+ </ModalContent>
856
+ </ModalBody>
857
+ </Modal>
858
+ )}
859
+ {modalOpen === 2 && (
860
+ <Modal>
861
+ <ModalHeader>
862
+ <ModalTitle>Action finished</ModalTitle>
863
+ </ModalHeader>
864
+ <ModalBody>
865
+ <ModalContent>
866
+ <p>
867
+ Action has been successfully finished.
868
+ You will be redirected within a few seconds.
869
+ </p>
870
+ </ModalContent>
871
+ </ModalBody>
872
+ </Modal>
873
+ )}
874
+ </div>
875
+ </GlobalPropsProvider>
876
+ );
877
+ });
878
+ ```
879
+
880
+ ### Forms
881
+
882
+ Modal can be used to display forms. It is recommended to use
883
+ [FormLayout](/components/FormLayout) component to layout form fields.
884
+
885
+ While we support only [controlled components][controlled-components],
886
+ and we encourage you to use them, it is possible to use native form and its
887
+ functionality inside the modal. This might be useful when you need to use
888
+ native form features like validation, submission, etc.
889
+
890
+ To do so, you need to set `allowPrimaryActionOnEnterKey` to `false` and remove
891
+ `onClick` from the primary button. Then, you need to set `form` attribute on the
892
+ primary button to the `id` of the form to connect it with the form.
893
+
894
+ ```docoff-react-preview
895
+ React.createElement(() => {
896
+ const [modalOpen, setModalOpen] = React.useState(null);
897
+ const modalPrimaryButtonRef = React.useRef();
898
+ const modalCloseButtonRef = React.useRef();
899
+ {/*
900
+ The `preventScrollUnderneath` feature is necessary for Modals to work in
901
+ React UI docs. You may not need it in your application.
902
+ */}
903
+ return (
904
+ <GlobalPropsProvider globalProps={{
905
+ Modal: { preventScrollUnderneath: window.document.documentElement }
906
+ }}>
907
+ <Button
908
+ label="Launch modal as form"
909
+ onClick={() => setModalOpen(1)}
910
+ />
911
+ <Button
912
+ label="Launch modal as native form"
913
+ onClick={() => setModalOpen(2)}
914
+ />
915
+ <div>
916
+ {modalOpen === 1 && (
917
+ <Modal
918
+ closeButtonRef={modalCloseButtonRef}
919
+ primaryButtonRef={modalPrimaryButtonRef}
920
+ >
921
+ <ModalHeader>
922
+ <ModalTitle>Add new user</ModalTitle>
923
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
924
+ </ModalHeader>
925
+ <ModalBody>
926
+ <ModalContent>
927
+ <FormLayout fieldLayout="horizontal" labelWidth="limited">
928
+ <Toggle
929
+ label="Enabled"
930
+ />
931
+ <TextField label="Username" required />
932
+ <TextField label="Password" type="password" />
933
+ <CheckboxField label="Force password on login" />
934
+ <Radio
935
+ label="Type of collaboration"
936
+ options={[
937
+ { label: 'Internal', value: 'internal'},
938
+ { label: 'External', value: 'external'},
939
+ ]}
940
+ />
941
+ <SelectField
942
+ label="Role"
943
+ options={[
944
+ { label: 'Programmer', value: 'programmer' },
945
+ { label: 'Team leader', value: 'team-leader' },
946
+ { label: 'Project manager', value: 'project-manager' },
947
+ ]}
948
+ />
949
+ <FileInputField label="Photo" onFilesChanged={() => {}} />
950
+ <TextArea
951
+ label="Additional info"
952
+ helpText={<p>Enter key is used for new line,<br />so <strong>Enter won't submit the form</strong>.</p>}
953
+ />
954
+ </FormLayout>
955
+ </ModalContent>
956
+ </ModalBody>
957
+ <ModalFooter>
958
+ <Button
959
+ label="Save"
960
+ onClick={() => setModalOpen(false)}
961
+ ref={modalPrimaryButtonRef}
962
+ />
963
+ <Button
964
+ label="Close"
965
+ onClick={() => setModalOpen(false)}
966
+ priority="outline"
967
+ ref={modalCloseButtonRef}
968
+ />
969
+ </ModalFooter>
970
+ </Modal>
971
+ )}
972
+ {modalOpen === 2 && (
973
+ <Modal
974
+ allowPrimaryActionOnEnterKey={false}
975
+ closeButtonRef={modalCloseButtonRef}
976
+ onCancel={(e) => {
977
+ console.log('cancel', e);
978
+ }}
979
+ onClose={(e) => {
980
+ console.log('close', e);
981
+ }}
982
+ primaryButtonRef={modalPrimaryButtonRef}
983
+ >
984
+ <ModalHeader>
985
+ <ModalTitle>Add new user using native form</ModalTitle>
986
+ <ModalCloseButton onClick={() => setModalOpen(false)} />
987
+ </ModalHeader>
988
+ <ModalBody>
989
+ <ModalContent>
990
+ <form method="dialog" id="native-form">
991
+ <FormLayout fieldLayout="horizontal" labelWidth="limited">
992
+ <Toggle
993
+ label="Enabled"
994
+ />
995
+ <TextField label="Username" required />
996
+ <TextField label="Password" type="password" />
997
+ <CheckboxField label="Force password on login" />
998
+ <Radio
999
+ label="Type of collaboration"
1000
+ options={[
1001
+ { label: 'Internal', value: 'internal'},
1002
+ { label: 'External', value: 'external'},
1003
+ ]}
1004
+ />
1005
+ <SelectField
1006
+ label="Role"
1007
+ options={[
1008
+ { label: 'Programmer', value: 'programmer' },
1009
+ { label: 'Team leader', value: 'team-leader' },
1010
+ { label: 'Project manager', value: 'project-manager' },
1011
+ ]}
1012
+ />
1013
+ <FileInputField label="Photo" />
1014
+ <TextArea
1015
+ label="Additional info"
1016
+ helpText={<p>Enter key is used for new line,<br />so <strong>Enter won't submit the form</strong>.</p>}
1017
+ />
1018
+ </FormLayout>
1019
+ </form>
1020
+ </ModalContent>
1021
+ </ModalBody>
1022
+ <ModalFooter>
1023
+ <Button
1024
+ form="native-form"
1025
+ label="Save"
1026
+ ref={modalPrimaryButtonRef}
1027
+ type="submit"
1028
+ />
1029
+ <Button
1030
+ label="Close"
1031
+ onClick={() => setModalOpen(false)}
1032
+ priority="outline"
1033
+ ref={modalCloseButtonRef}
1034
+ />
1035
+ </ModalFooter>
1036
+ </Modal>
1037
+ )}
1038
+ </div>
1039
+ </GlobalPropsProvider>
1040
+ );
1041
+ });
1042
+ ```
1043
+
1044
+ ### Scrolling Long Content
843
1045
 
844
1046
  When modals become too long for the user's viewport or device, they scroll
845
1047
  independent of the page itself. This can be done in three ways using the
@@ -936,7 +1138,7 @@ React.createElement(() => {
936
1138
  React UI docs. You may not need it in your application.
937
1139
  */}
938
1140
  return (
939
- <RUIProvider globalProps={{
1141
+ <GlobalPropsProvider globalProps={{
940
1142
  Modal: { preventScrollUnderneath: window.document.documentElement }
941
1143
  }}>
942
1144
  <Button
@@ -999,12 +1201,12 @@ React.createElement(() => {
999
1201
  </Modal>
1000
1202
  )}
1001
1203
  </div>
1002
- </RUIProvider>
1204
+ </GlobalPropsProvider>
1003
1205
  );
1004
1206
  });
1005
1207
  ```
1006
1208
 
1007
- ### Long Content and Autofocus
1209
+ #### Long Content and Autofocus
1008
1210
 
1009
1211
  👉 If you wrap ModalContent with ScrollView, you may want to turn `autoFocus`
1010
1212
  off to prevent the modal from scrolling to the end immediately after being
@@ -1019,8 +1221,7 @@ can specify **any HTML attribute you like.** All attributes that don't
1019
1221
  interfere with the API of the React component and that aren't filtered out by
1020
1222
  [`transferProps`](/docs/js-helpers/transferProps) helper are forwarded to:
1021
1223
 
1022
- - the `<div>` HTML element in case of the `Modal` component. This `<div>` is not
1023
- the root, but its first child which represents the modal window.
1224
+ - the `<dialog>` HTML element in case of the `Modal` component.
1024
1225
  - the root `<div>` HTML element in case of `ModalHeader`, `ModalBody`, `ModalContent`
1025
1226
  and `ModalFooter` components.
1026
1227
  - the heading (e.g. `<h1>`) HTML element in case of the `ModalTitle` component.
@@ -1031,6 +1232,7 @@ accessibility.
1031
1232
 
1032
1233
  👉 For the full list of supported attributes refer to:
1033
1234
 
1235
+ - [`<dialog>` HTML element attributes][dialog-attributes]{:target="_blank"}
1034
1236
  - [`<div>` HTML element attributes][div-attributes]{:target="_blank"}
1035
1237
  - [`<h1>`-`<h6>` HTML element attributes][heading-attributes]{:target="_blank"}
1036
1238
  - [`<button>` HTML element attributes][button-attributes]{:target="_blank"}
@@ -1066,29 +1268,47 @@ accessibility.
1066
1268
 
1067
1269
  ## Theming
1068
1270
 
1069
- | Custom Property | Description |
1070
- |------------------------------------------------------|---------------------------------------------------------------|
1071
- | `--rui-Modal__padding-x` | Inline padding of individual modal components |
1072
- | `--rui-Modal__padding-y` | Block padding of individual modal components |
1073
- | `--rui-Modal__background` | Modal background (including `url()` or gradient) |
1074
- | `--rui-Modal__box-shadow` | Modal box shadow |
1075
- | `--rui-Modal__separator__width` | Width of separator between modal header, body, and footer |
1076
- | `--rui-Modal__separator__color` | Color of separator between modal header, body, and footer |
1077
- | `--rui-Modal__outer-spacing-xs` | Spacing around modal, `xs` screen size |
1078
- | `--rui-Modal__outer-spacing-sm` | Spacing around modal, `sm` screen size and bigger |
1079
- | `--rui-Modal__header__gap` | Modal header gap between children |
1080
- | `--rui-Modal__footer__background` | Modal footer background (including `url()` or gradient) |
1081
- | `--rui-Modal__footer__gap` | Modal footer gap between children |
1082
- | `--rui-Modal__backdrop__background` | Modal backdrop background (including `url()` or gradient) |
1083
- | `--rui-Modal--auto__min-width` | Min width of auto-sized modal (when enough screen estate) |
1084
- | `--rui-Modal--auto__max-width` | Max width of auto-sized modal (when enough screen estate) |
1085
- | `--rui-Modal--small__width` | Width of small modal |
1086
- | `--rui-Modal--medium__width` | Width of medium modal |
1087
- | `--rui-Modal--large__width` | Width of large modal |
1088
- | `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
1089
- | `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
1271
+ | Custom Property | Description |
1272
+ |------------------------------------------------------|-------------------------------------------------------------|
1273
+ | `--rui-Modal__padding-x` | Inline padding of individual modal components |
1274
+ | `--rui-Modal__padding-y` | Block padding of individual modal components |
1275
+ | `--rui-Modal__background` | Modal background (including `url()` or gradient) |
1276
+ | `--rui-Modal__box-shadow` | Modal box shadow |
1277
+ | `--rui-Modal__separator__width` | Width of separator between modal header, body, and footer |
1278
+ | `--rui-Modal__separator__color` | Color of separator between modal header, body, and footer |
1279
+ | `--rui-Modal__outer-spacing-xs` | Spacing around modal, `xs` screen size |
1280
+ | `--rui-Modal__outer-spacing-sm` | Spacing around modal, `sm` screen size and bigger |
1281
+ | `--rui-Modal__header__gap` | Modal header gap between children |
1282
+ | `--rui-Modal__footer__background` | Modal footer background (including `url()` or gradient) |
1283
+ | `--rui-Modal__footer__gap` | Modal footer gap between children |
1284
+ | `--rui-Modal__backdrop__background` | Modal backdrop background (including `url()` or gradient) |
1285
+ | `--rui-Modal--auto__min-width` | Min width of auto-sized modal (when enough screen estate) |
1286
+ | `--rui-Modal--auto__max-width` | Max width of auto-sized modal (when enough screen estate) |
1287
+ | `--rui-Modal--small__width` | Width of small modal |
1288
+ | `--rui-Modal--medium__width` | Width of medium modal |
1289
+ | `--rui-Modal--large__width` | Width of large modal |
1290
+ | `--rui-Modal--fullscreen__width` | Width of fullscreen modal |
1291
+ | `--rui-Modal--fullscreen__height` | Height of fullscreen modal |
1292
+ | `--rui-Modal__animation__duration` | Duration of animation used (when opening modal) |
1293
+
1294
+ ### Theming Variants
1295
+
1296
+ It's possible to adjust the theme of specific color variant. Naming convention
1297
+ looks as follows:
1298
+
1299
+ `--rui-Modal--<COLOR>__<PROPERTY>`
1300
+
1301
+ Where:
1302
+
1303
+ - `<COLOR>` is a value from supported
1304
+ [color collections](/docs/foundation/collections#colors)
1305
+ (check [color variants](#color-variants) and [API](#api) to see which
1306
+ collections are supported),
1307
+ - `<PROPERTY>` is one of `border-color` or `background-color`.
1090
1308
 
1091
1309
  [button-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attributes
1310
+ [controlled-components]: /docs/getting-started/usage#foundation-css
1311
+ [dialog-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#attributes
1092
1312
  [div-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes
1093
1313
  [heading-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements#attributes
1094
1314
  [React common props]: https://react.dev/reference/react-dom/components/common#common-props