@react-ui-org/react-ui 0.57.0 → 0.58.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 (80) hide show
  1. package/.nvmrc +1 -1
  2. package/dist/react-ui.css +14 -14
  3. package/dist/react-ui.development.css +225 -14
  4. package/dist/react-ui.development.js +102 -62
  5. package/dist/react-ui.js +1 -1
  6. package/package.json +12 -1
  7. package/src/components/Alert/Alert.jsx +3 -5
  8. package/src/components/Alert/Alert.module.scss +3 -3
  9. package/src/components/Alert/README.md +22 -10
  10. package/src/components/Badge/Badge.jsx +1 -1
  11. package/src/components/Button/Button.jsx +1 -1
  12. package/src/components/ButtonGroup/ButtonGroup.jsx +1 -1
  13. package/src/components/Card/Card.jsx +1 -1
  14. package/src/components/Card/Card.module.scss +6 -5
  15. package/src/components/Card/CardBody.jsx +1 -1
  16. package/src/components/Card/CardFooter.jsx +1 -1
  17. package/src/components/Card/README.md +22 -0
  18. package/src/components/CheckboxField/CheckboxField.jsx +9 -3
  19. package/src/components/CheckboxField/README.md +110 -5
  20. package/src/components/FileInputField/FileInputField.jsx +1 -1
  21. package/src/components/FormLayout/FormLayout.jsx +1 -1
  22. package/src/components/FormLayout/FormLayoutCustomField.jsx +1 -1
  23. package/src/components/Grid/Grid.jsx +1 -1
  24. package/src/components/Grid/GridSpan.jsx +1 -1
  25. package/src/components/InputGroup/InputGroup.jsx +2 -2
  26. package/src/components/InputGroup/InputGroup.module.scss +9 -5
  27. package/src/components/Modal/Modal.jsx +1 -1
  28. package/src/components/Modal/ModalBody.jsx +1 -1
  29. package/src/components/Modal/ModalCloseButton.jsx +3 -5
  30. package/src/components/Modal/ModalContent.jsx +1 -1
  31. package/src/components/Modal/ModalFooter.jsx +1 -1
  32. package/src/components/Modal/ModalHeader.jsx +1 -1
  33. package/src/components/Modal/ModalTitle.jsx +1 -1
  34. package/src/components/Modal/README.md +18 -18
  35. package/src/components/Paper/Paper.jsx +1 -1
  36. package/src/components/Popover/Popover.jsx +58 -13
  37. package/src/components/Popover/Popover.module.scss +37 -9
  38. package/src/components/Popover/PopoverWrapper.jsx +1 -1
  39. package/src/components/Popover/README.md +60 -3
  40. package/src/components/Popover/_helpers/cleanPlacementStyle.js +20 -0
  41. package/src/components/Radio/README.md +103 -0
  42. package/src/components/Radio/Radio.jsx +9 -3
  43. package/src/components/Radio/Radio.module.scss +4 -0
  44. package/src/components/ScrollView/ScrollView.jsx +3 -5
  45. package/src/components/SelectField/README.md +103 -0
  46. package/src/components/SelectField/SelectField.jsx +9 -3
  47. package/src/components/Table/Table.jsx +1 -1
  48. package/src/components/Tabs/Tabs.jsx +1 -1
  49. package/src/components/Tabs/TabsItem.jsx +1 -1
  50. package/src/components/Text/Text.jsx +1 -1
  51. package/src/components/TextArea/TextArea.jsx +1 -1
  52. package/src/components/TextField/README.md +14 -2
  53. package/src/components/TextField/TextField.jsx +1 -1
  54. package/src/components/TextLink/README.md +10 -3
  55. package/src/components/TextLink/TextLink.jsx +1 -1
  56. package/src/components/TextLink/_theme.scss +3 -3
  57. package/src/components/Toggle/README.md +83 -1
  58. package/src/components/Toggle/Toggle.jsx +9 -3
  59. package/src/components/Toolbar/Toolbar.jsx +1 -1
  60. package/src/components/Toolbar/ToolbarGroup.jsx +1 -1
  61. package/src/components/Toolbar/ToolbarItem.jsx +1 -1
  62. package/src/components/_helpers/resolveContextOrProp.js +6 -3
  63. package/src/index.js +3 -2
  64. package/src/providers/globalProps/GlobalPropsContext.jsx +5 -0
  65. package/src/providers/globalProps/GlobalPropsProvider.jsx +33 -0
  66. package/src/providers/globalProps/index.js +3 -0
  67. package/src/{provider → providers/globalProps}/withGlobalProps.jsx +16 -16
  68. package/src/providers/translations/TranslationsContext.jsx +6 -0
  69. package/src/providers/translations/TranslationsProvider.jsx +33 -0
  70. package/src/providers/translations/index.js +2 -0
  71. package/src/styles/elements/_links.scss +7 -2
  72. package/src/styles/theme/_form-fields.scss +19 -0
  73. package/src/styles/theme/_links.scss +4 -3
  74. package/src/styles/tools/_collections.scss +79 -5
  75. package/src/styles/tools/form-fields/_foundation.scss +6 -4
  76. package/src/styles/tools/form-fields/_variants.scss +5 -1
  77. package/src/theme.scss +2 -1
  78. package/src/provider/RUIContext.jsx +0 -9
  79. package/src/provider/RUIProvider.jsx +0 -42
  80. package/src/provider/index.js +0 -3
@@ -52,6 +52,10 @@
52
52
  @include foundation.label-required();
53
53
  }
54
54
 
55
+ .isRootRequired .optionLabel {
56
+ @include foundation.label-required($show-require-sign: false);
57
+ }
58
+
55
59
  // States
56
60
  .isRootStateInvalid {
57
61
  @include variants.validation(invalid);
@@ -6,10 +6,8 @@ import React, {
6
6
  useRef,
7
7
  useState,
8
8
  } from 'react';
9
- import {
10
- RUIContext,
11
- withGlobalProps,
12
- } from '../../provider';
9
+ import { TranslationsContext } from '../../providers/translations';
10
+ import { withGlobalProps } from '../../providers/globalProps';
13
11
  import { classNames } from '../../utils/classNames';
14
12
  import { transferProps } from '../../utils/transferProps';
15
13
  import { getElementsPositionDifference } from './_helpers/getElementsPositionDifference';
@@ -48,7 +46,7 @@ export const ScrollView = React.forwardRef((props, ref) => {
48
46
  ...restProps
49
47
  } = props;
50
48
 
51
- const { translations } = useContext(RUIContext);
49
+ const translations = useContext(TranslationsContext);
52
50
 
53
51
  const [isAutoScrollInProgress, setIsAutoScrollInProgress] = useState(false);
54
52
  const [isScrolledAtStart, setIsScrolledAtStart] = useState(false);
@@ -592,6 +592,109 @@ React.createElement(() => {
592
592
  })
593
593
  ```
594
594
 
595
+ ### Required State
596
+
597
+ The required state indicates that the input is mandatory.
598
+
599
+ ```docoff-react-preview
600
+ React.createElement(() => {
601
+ const [fruit, setFruit] = React.useState('apple');
602
+ return (
603
+ <SelectField
604
+ label="Your favourite fruit"
605
+ onChange={(e) => setFruit(e.target.value)}
606
+ options={[
607
+ {
608
+ label: 'Apple',
609
+ value: 'apple',
610
+ },
611
+ {
612
+ label: 'Banana',
613
+ value: 'banana',
614
+ },
615
+ {
616
+ label: 'Grapefruit',
617
+ value: 'grapefruit',
618
+ },
619
+ ]}
620
+ value={fruit}
621
+ required
622
+ />
623
+ );
624
+ });
625
+ ```
626
+
627
+ #### Styling the Required State
628
+
629
+ All form fields in React UI can be
630
+ [styled](/docs/customize/theming/forms/#required-state)
631
+ to indicate the required state.
632
+
633
+ However, you may find yourself in a situation where a form field is valid in
634
+ both selected and unselected states, for example to turn on or off a feature.
635
+ If your project uses the label color as the primary means to indicate the
636
+ required state of input fields and the usual asterisk `*` is omitted, you may
637
+ want to keep the label color consistent for both states to avoid confusion.
638
+
639
+ For this edge case, there is the `renderAsRequired` prop:
640
+
641
+ ```docoff-react-preview
642
+ React.createElement(() => {
643
+ const [fruit, setFruit] = React.useState('apple');
644
+ const options = [
645
+ {
646
+ label: 'Apple',
647
+ value: 'apple',
648
+ },
649
+ {
650
+ label: 'Banana',
651
+ value: 'banana',
652
+ },
653
+ {
654
+ label: 'Grapefruit',
655
+ value: 'grapefruit',
656
+ },
657
+ ];
658
+ return (
659
+ <React.Fragment>
660
+ <style>
661
+ {`
662
+ .example {
663
+ display: flex;
664
+ flex-wrap: wrap;
665
+ gap: 1rem 0.5rem;
666
+ }
667
+
668
+ .example--themed-form-fields {
669
+ --rui-FormField__label__color: var(--rui-color-text-secondary);
670
+ --rui-FormField--required__label__color: var(--rui-color-text-primary);
671
+ --rui-FormField--required__sign: '';
672
+ }
673
+ `}
674
+ </style>
675
+ <div class="example example--themed-form-fields">
676
+ <SelectField
677
+ label="This field is optional"
678
+ onChange={(e) => setFruit(e.target.value)}
679
+ options={options}
680
+ value={fruit}
681
+ />
682
+ <SelectField
683
+ label="This field is optional but looks like required"
684
+ onChange={(e) => setFruit(e.target.value)}
685
+ options={options}
686
+ value={fruit}
687
+ renderAsRequired
688
+ />
689
+ </div>
690
+ </React.Fragment>
691
+ );
692
+ });
693
+ ```
694
+
695
+ It renders the field as if it was required, but doesn't add the `required`
696
+ attribute to the actual input.
697
+
595
698
  ### Disabled State
596
699
 
597
700
  It's possible to disable just some options or the whole input.
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
@@ -21,6 +21,7 @@ export const SelectField = React.forwardRef((props, ref) => {
21
21
  label,
22
22
  layout,
23
23
  options,
24
+ renderAsRequired,
24
25
  required,
25
26
  size,
26
27
  validationState,
@@ -43,7 +44,7 @@ export const SelectField = React.forwardRef((props, ref) => {
43
44
  ? styles.isRootLayoutHorizontal
44
45
  : styles.isRootLayoutVertical,
45
46
  inputGroupContext && styles.isRootGrouped,
46
- required && styles.isRootRequired,
47
+ (renderAsRequired || required) && styles.isRootRequired,
47
48
  getRootSizeClassName(
48
49
  resolveContextOrProp(inputGroupContext && inputGroupContext.size, size),
49
50
  styles,
@@ -136,6 +137,7 @@ SelectField.defaultProps = {
136
137
  id: undefined,
137
138
  isLabelVisible: true,
138
139
  layout: 'vertical',
140
+ renderAsRequired: false,
139
141
  required: false,
140
142
  size: 'medium',
141
143
  validationState: null,
@@ -227,7 +229,11 @@ SelectField.propTypes = {
227
229
  })),
228
230
  ]).isRequired,
229
231
  /**
230
- * If `true`, the input will be required.
232
+ * If `true`, the input will be rendered as if it was required.
233
+ */
234
+ renderAsRequired: PropTypes.bool,
235
+ /**
236
+ * If `true`, the input will be made and rendered as required, regardless of the `renderAsRequired` prop.
231
237
  */
232
238
  required: PropTypes.bool,
233
239
  /**
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { transferProps } from '../../utils/transferProps';
5
5
  import { TableHeaderCell } from './_components/TableHeaderCell';
6
6
  import { TableBodyCell } from './_components/TableBodyCell';
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { transferProps } from '../../utils/transferProps';
5
5
  import styles from './Tabs.module.scss';
6
6
 
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import styles from './TabsItem.module.scss';
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
@@ -388,7 +388,13 @@ have.
388
388
  label="User name"
389
389
  onChange={() => {}}
390
390
  validationState="warning"
391
- validationText="Account with this name already exists, pick a different one."
391
+ validationText={(
392
+ <>
393
+ Account with this name already exists, pick a different one.
394
+ {' '}
395
+ <TextLink href="#" label="Forgot your password?" />
396
+ </>
397
+ )}
392
398
  value="joe"
393
399
  required
394
400
  />
@@ -411,7 +417,13 @@ have.
411
417
  label="User name"
412
418
  onChange={() => {}}
413
419
  validationState="warning"
414
- validationText="Account with this name already exists, pick a different one."
420
+ validationText={(
421
+ <>
422
+ Account with this name already exists, pick a different one.
423
+ {' '}
424
+ <TextLink href="#" label="Forgot your password?" />
425
+ </>
426
+ )}
415
427
  variant="filled"
416
428
  value="joe"
417
429
  required
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
@@ -64,13 +64,20 @@ helps to improve its accessibility.
64
64
 
65
65
  ## Theming
66
66
 
67
+ ℹ️ The TextLink component is context-aware and can inherit text color from its
68
+ parent component. This applies for components using
69
+ [Feedback color collection](/docs/foundation/collections#colors) and for
70
+ components in any of the supported
71
+ [validation states](/docs/foundation/colors#validation-states).
72
+ In such cases, the custom properties marked with an asterisk (\*) are ignored.
73
+
67
74
  | Custom Property | Description |
68
75
  |-------------------------------------------|-------------------------------------|
69
- | `--rui-TextLink__color` | Text color |
76
+ | `--rui-TextLink__color` \* | Text color |
70
77
  | `--rui-TextLink__text-decoration` | Text decoration, e.g. underline |
71
- | `--rui-TextLink--hover__color` | Text color on hover |
78
+ | `--rui-TextLink--hover__color` \* | Text color on hover |
72
79
  | `--rui-TextLink--hover__text-decoration` | Text decoration on hover |
73
- | `--rui-TextLink--active__color` | Text color in the active state |
80
+ | `--rui-TextLink--active__color` \* | Text color in the active state |
74
81
  | `--rui-TextLink--active__text-decoration` | Text decoration in the active state |
75
82
 
76
83
  [a-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { transferProps } from '../../utils/transferProps';
5
5
  import styles from './TextLink.module.scss';
6
6
 
@@ -1,6 +1,6 @@
1
- $color: var(--rui-TextLink__color);
1
+ $color: var(--rui-local-link-color, var(--rui-TextLink__color));
2
2
  $text-decoration: var(--rui-TextLink__text-decoration);
3
- $hover-color: var(--rui-TextLink--hover__color);
3
+ $hover-color: var(--rui-local-link-color-hover, var(--rui-TextLink--hover__color));
4
4
  $hover-text-decoration: var(--rui-TextLink--hover__text-decoration);
5
- $active-color: var(--rui-TextLink--active__color);
5
+ $active-color: var(--rui-local-link-color-active, var(--rui-TextLink--active__color));
6
6
  $active-text-decoration: var(--rui-TextLink--active__text-decoration);
@@ -148,13 +148,95 @@ React.createElement(() => {
148
148
  label="Listen in studio quality"
149
149
  onChange={() => setStudioQuality(!studioQuality)}
150
150
  validationState="invalid"
151
- validationText="Please upgrade your plan to make this option available."
151
+ validationText={(
152
+ <>
153
+ Please
154
+ {' '}
155
+ <TextLink href="#" label="upgrade your plan" />
156
+ {' '}
157
+ to make this option available.
158
+ </>
159
+ )}
152
160
  />
153
161
  </>
154
162
  );
155
163
  });
156
164
  ```
157
165
 
166
+ ### Required State
167
+
168
+ The required state indicates that the input is mandatory.
169
+
170
+ ```docoff-react-preview
171
+ React.createElement(() => {
172
+ const [studioQuality, setStudioQuality] = React.useState(true);
173
+ return (
174
+ <Toggle
175
+ checked={studioQuality}
176
+ label="Listen in studio quality"
177
+ onChange={() => setStudioQuality(!studioQuality)}
178
+ required
179
+ />
180
+ );
181
+ });
182
+ ```
183
+
184
+ #### Styling the Required State
185
+
186
+ All form fields in React UI can be
187
+ [styled](/docs/customize/theming/forms/#required-state)
188
+ to indicate the required state.
189
+
190
+ However, you may find yourself in a situation where a form field is valid in
191
+ both checked and unchecked states, for example to turn on or off a feature.
192
+ If your project uses the label color as the primary means to indicate the
193
+ required state of input fields and the usual asterisk `*` is omitted, you may
194
+ want to keep the label color consistent for both states to avoid confusion.
195
+
196
+ For this edge case, there is the `renderAsRequired` prop:
197
+
198
+ ```docoff-react-preview
199
+ React.createElement(() => {
200
+ const [optional, setOptional] = React.useState(false);
201
+ const [renderAsRequired, setRenderAsRequired] = React.useState(false);
202
+ return (
203
+ <React.Fragment>
204
+ <style>
205
+ {`
206
+ .example {
207
+ display: flex;
208
+ flex-wrap: wrap;
209
+ gap: 1rem 0.5rem;
210
+ }
211
+
212
+ .example--themed-form-fields {
213
+ --rui-FormField__label__color: var(--rui-color-text-secondary);
214
+ --rui-FormField--required__label__color: var(--rui-color-text-primary);
215
+ --rui-FormField--required__sign: '';
216
+ }
217
+ `}
218
+ </style>
219
+ <div class="example example--themed-form-fields">
220
+ <Toggle
221
+ checked={optional}
222
+ label="This field is optional"
223
+ onChange={() => setOptional(!optional)}
224
+ />
225
+ <Toggle
226
+ checked={renderAsRequired}
227
+ label="This field is optional but looks like required"
228
+ onChange={() => setRenderAsRequired(!renderAsRequired)}
229
+ renderAsRequired
230
+ />
231
+ </div>
232
+ </React.Fragment>
233
+ );
234
+ });
235
+ ```
236
+
237
+ It renders the field as if it was required, but doesn't add the `required`
238
+ attribute to the actual input.
239
+
158
240
  ### Disabled State
159
241
 
160
242
  Disabled state makes the input unavailable.
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useContext } from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
@@ -15,6 +15,7 @@ export const Toggle = React.forwardRef((props, ref) => {
15
15
  isLabelVisible,
16
16
  label,
17
17
  labelPosition,
18
+ renderAsRequired,
18
19
  required,
19
20
  validationState,
20
21
  validationText,
@@ -31,7 +32,7 @@ export const Toggle = React.forwardRef((props, ref) => {
31
32
  context && context.layout === 'horizontal' ? styles.isRootLayoutHorizontal : styles.isRootLayoutVertical,
32
33
  labelPosition === 'before' && styles.hasRootLabelBefore,
33
34
  disabled && styles.isRootDisabled,
34
- required && styles.isRootRequired,
35
+ (required || renderAsRequired) && styles.isRootRequired,
35
36
  getRootValidationStateClassName(validationState, styles),
36
37
  )}
37
38
  htmlFor={id}
@@ -84,6 +85,7 @@ Toggle.defaultProps = {
84
85
  id: undefined,
85
86
  isLabelVisible: true,
86
87
  labelPosition: 'after',
88
+ renderAsRequired: false,
87
89
  required: false,
88
90
  validationState: null,
89
91
  validationText: null,
@@ -120,7 +122,11 @@ Toggle.propTypes = {
120
122
  */
121
123
  labelPosition: PropTypes.oneOf(['before', 'after']),
122
124
  /**
123
- * If `true`, the input will be required.
125
+ * If `true`, the input will be rendered as if it was required.
126
+ */
127
+ renderAsRequired: PropTypes.bool,
128
+ /**
129
+ * If `true`, the input will be made and rendered as required, regardless of the `renderAsRequired` prop.
124
130
  */
125
131
  required: PropTypes.bool,
126
132
  /**
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
@@ -1,6 +1,6 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
- import { withGlobalProps } from '../../provider';
3
+ import { withGlobalProps } from '../../providers/globalProps';
4
4
  import { classNames } from '../../utils/classNames';
5
5
  import { transferProps } from '../../utils/transferProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import { classNames } from '../../utils/classNames';
4
4
  import { transferProps } from '../../utils/transferProps';
5
- import { withGlobalProps } from '../../provider';
5
+ import { withGlobalProps } from '../../providers/globalProps';
6
6
  import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
7
7
  import styles from './Toolbar.module.scss';
8
8
 
@@ -1,7 +1,10 @@
1
1
  export const resolveContextOrProp = (contextValue, propValue) => {
2
- if (contextValue != null) {
3
- return contextValue;
2
+ // We need to test:
3
+ // * `false` - for when the `contextValue` is boolean
4
+ // * `null` - for when the `contextValue` is non-boolean
5
+ if (contextValue === false || contextValue === null) {
6
+ return propValue;
4
7
  }
5
8
 
6
- return propValue;
9
+ return contextValue;
7
10
  };
package/src/index.js CHANGED
@@ -57,8 +57,9 @@ export {
57
57
  ToolbarItem,
58
58
  } from './components/Toolbar';
59
59
 
60
- // Provider
61
- export { RUIProvider } from './provider';
60
+ // Providers
61
+ export { GlobalPropsProvider } from './providers/globalProps';
62
+ export { TranslationsProvider } from './providers/translations';
62
63
 
63
64
  // Utils
64
65
  export { classNames } from './utils/classNames';
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+
3
+ const GlobalPropsContext = React.createContext({});
4
+
5
+ export default GlobalPropsContext;
@@ -0,0 +1,33 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, {
3
+ useContext,
4
+ } from 'react';
5
+ import { mergeDeep } from '../../utils/mergeDeep';
6
+ import GlobalPropsContext from './GlobalPropsContext';
7
+
8
+ const GlobalPropsProvider = ({
9
+ children,
10
+ globalProps,
11
+ }) => {
12
+ const contextGlobalProps = useContext(GlobalPropsContext);
13
+
14
+ return (
15
+ <GlobalPropsContext.Provider
16
+ value={mergeDeep(contextGlobalProps, globalProps)}
17
+ >
18
+ {children}
19
+ </GlobalPropsContext.Provider>
20
+ );
21
+ };
22
+
23
+ GlobalPropsProvider.defaultProps = {
24
+ children: null,
25
+ globalProps: {},
26
+ };
27
+
28
+ GlobalPropsProvider.propTypes = {
29
+ children: PropTypes.node,
30
+ globalProps: PropTypes.shape({}),
31
+ };
32
+
33
+ export default GlobalPropsProvider;
@@ -0,0 +1,3 @@
1
+ export { default as GlobalPropsContext } from './GlobalPropsContext';
2
+ export { default as GlobalPropsProvider } from './GlobalPropsProvider';
3
+ export { default as withGlobalProps } from './withGlobalProps';
@@ -1,26 +1,24 @@
1
1
  import PropTypes from 'prop-types';
2
- import React from 'react';
3
- import RUIContext from './RUIContext';
2
+ import React, {
3
+ useContext,
4
+ } from 'react';
5
+ import GlobalPropsContext from './GlobalPropsContext';
4
6
 
5
7
  export default (Component, componentName) => {
6
8
  const WithGlobalPropsComponent = ({
7
9
  forwardedRef,
8
10
  ...rest
9
- }) => (
10
- <RUIContext.Consumer>
11
- {({ globalProps }) => {
12
- const contextGlobalProps = globalProps ? globalProps[componentName] : null;
11
+ }) => {
12
+ const contextGlobalProps = useContext(GlobalPropsContext);
13
13
 
14
- return (
15
- <Component
16
- {...contextGlobalProps}
17
- {...rest}
18
- ref={forwardedRef}
19
- />
20
- );
21
- }}
22
- </RUIContext.Consumer>
23
- );
14
+ return (
15
+ <Component
16
+ {...contextGlobalProps[componentName] || {}}
17
+ {...rest}
18
+ ref={forwardedRef}
19
+ />
20
+ );
21
+ };
24
22
 
25
23
  WithGlobalPropsComponent.defaultProps = {
26
24
  forwardedRef: undefined,
@@ -29,6 +27,8 @@ export default (Component, componentName) => {
29
27
  WithGlobalPropsComponent.propTypes = {
30
28
  forwardedRef: PropTypes.oneOfType([
31
29
  PropTypes.func,
30
+
31
+ // The props can be of any type and here we need to support them all
32
32
  // eslint-disable-next-line react/forbid-prop-types
33
33
  PropTypes.shape({ current: PropTypes.any }),
34
34
  ]),
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import defaultTranslations from '../../translations/en';
3
+
4
+ const RUIContext = React.createContext(defaultTranslations);
5
+
6
+ export default RUIContext;
@@ -0,0 +1,33 @@
1
+ import PropTypes from 'prop-types';
2
+ import React, {
3
+ useContext,
4
+ } from 'react';
5
+ import { mergeDeep } from '../../utils/mergeDeep';
6
+ import TranslationsContext from './TranslationsContext';
7
+
8
+ const TranslationsProvider = ({
9
+ children,
10
+ translations,
11
+ }) => {
12
+ const contextTranslations = useContext(TranslationsContext);
13
+
14
+ return (
15
+ <TranslationsContext.Provider
16
+ value={mergeDeep(contextTranslations, translations)}
17
+ >
18
+ {children}
19
+ </TranslationsContext.Provider>
20
+ );
21
+ };
22
+
23
+ TranslationsProvider.defaultProps = {
24
+ children: null,
25
+ translations: {},
26
+ };
27
+
28
+ TranslationsProvider.propTypes = {
29
+ children: PropTypes.node,
30
+ translations: PropTypes.shape({}),
31
+ };
32
+
33
+ export default TranslationsProvider;
@@ -0,0 +1,2 @@
1
+ export { default as TranslationsContext } from './TranslationsContext';
2
+ export { default as TranslationsProvider } from './TranslationsProvider';