@react-ui-org/react-ui 0.50.2 → 0.52.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. package/dist/lib.development.js +157 -49
  2. package/dist/lib.js +1 -1
  3. package/package.json +1 -1
  4. package/src/lib/components/Alert/Alert.jsx +1 -3
  5. package/src/lib/components/Alert/Alert.scss +1 -9
  6. package/src/lib/components/Alert/README.mdx +0 -20
  7. package/src/lib/components/Alert/_settings.scss +1 -1
  8. package/src/lib/components/Alert/_theme.scss +0 -10
  9. package/src/lib/components/Badge/Badge.jsx +1 -3
  10. package/src/lib/components/Badge/Badge.scss +25 -44
  11. package/src/lib/components/Badge/README.mdx +6 -14
  12. package/src/lib/components/Button/Button.jsx +20 -10
  13. package/src/lib/components/Button/README.mdx +8 -3
  14. package/src/lib/components/Button/_base.scss +21 -12
  15. package/src/lib/components/Button/_priorities.scss +13 -18
  16. package/src/lib/components/Button/_settings.scss +1 -1
  17. package/src/lib/components/Button/_theme.scss +0 -10
  18. package/src/lib/components/ButtonGroup/ButtonGroup.jsx +5 -3
  19. package/src/lib/components/ButtonGroup/ButtonGroup.scss +26 -1
  20. package/src/lib/components/ButtonGroup/README.mdx +85 -59
  21. package/src/lib/components/ButtonGroup/_theme.scss +13 -0
  22. package/src/lib/components/Card/Card.jsx +1 -3
  23. package/src/lib/components/Card/Card.scss +0 -9
  24. package/src/lib/components/Card/README.mdx +0 -16
  25. package/src/lib/components/Card/_theme.scss +0 -10
  26. package/src/lib/components/FormLayout/README.mdx +22 -8
  27. package/src/lib/components/Grid/_helpers/generateResponsiveCustomProperties.js +1 -1
  28. package/src/lib/components/InputGroup/InputGroup.jsx +170 -0
  29. package/src/lib/components/InputGroup/InputGroup.scss +92 -0
  30. package/src/lib/components/InputGroup/InputGroupContext.js +3 -0
  31. package/src/lib/components/InputGroup/README.mdx +278 -0
  32. package/src/lib/components/InputGroup/_theme.scss +2 -0
  33. package/src/lib/components/InputGroup/index.js +2 -0
  34. package/src/lib/components/Modal/Modal.jsx +58 -97
  35. package/src/lib/components/Modal/ModalCloseButton.scss +2 -2
  36. package/src/lib/components/Modal/README.mdx +392 -128
  37. package/src/lib/components/Modal/_helpers/getPositionClassName.js +7 -0
  38. package/src/lib/components/Modal/_helpers/getSizeClassName.js +19 -0
  39. package/src/lib/components/Modal/_hooks/useModalFocus.js +126 -0
  40. package/src/lib/components/Modal/_hooks/useModalScrollPrevention.js +35 -0
  41. package/src/lib/components/Modal/_settings.scss +2 -2
  42. package/src/lib/components/Popover/README.mdx +7 -4
  43. package/src/lib/components/Radio/README.mdx +9 -1
  44. package/src/lib/components/Radio/Radio.jsx +39 -31
  45. package/src/lib/components/Radio/Radio.scss +11 -1
  46. package/src/lib/components/ScrollView/README.mdx +2 -2
  47. package/src/lib/components/SelectField/SelectField.jsx +21 -8
  48. package/src/lib/components/SelectField/SelectField.scss +5 -0
  49. package/src/lib/components/Table/_components/TableCell.scss +6 -5
  50. package/src/lib/components/Table/_components/TableHeaderCell/TableHeaderCell.jsx +8 -5
  51. package/src/lib/components/Table/_settings.scss +5 -6
  52. package/src/lib/components/Text/README.mdx +14 -8
  53. package/src/lib/components/TextField/TextField.jsx +21 -8
  54. package/src/lib/components/TextField/TextField.scss +5 -0
  55. package/src/lib/components/TextLink/README.mdx +8 -6
  56. package/src/lib/components/TextLink/TextLink.scss +5 -0
  57. package/src/lib/components/TextLink/_theme.scss +2 -0
  58. package/src/lib/components/Toolbar/README.mdx +19 -11
  59. package/src/lib/components/_helpers/getRootColorClassName.js +4 -0
  60. package/src/lib/index.js +1 -0
  61. package/src/lib/styles/elements/_code.scss +1 -3
  62. package/src/lib/styles/elements/_page.scss +1 -0
  63. package/src/lib/styles/elements/_rulers.scss +1 -3
  64. package/src/lib/styles/elements/_small.scss +1 -1
  65. package/src/lib/styles/settings/_form-fields.scss +1 -1
  66. package/src/lib/styles/settings/_utilities.scss +46 -14
  67. package/src/lib/styles/theme/_accessibility.scss +4 -4
  68. package/src/lib/styles/theme/_borders.scss +3 -2
  69. package/src/lib/styles/theme/_code.scss +2 -2
  70. package/src/lib/styles/theme/_links.scss +6 -4
  71. package/src/lib/styles/theme/_lists.scss +1 -1
  72. package/src/lib/styles/theme/_page.scss +2 -2
  73. package/src/lib/styles/theme/_spacing.scss +11 -11
  74. package/src/lib/styles/theme/_typography.scss +19 -18
  75. package/src/lib/styles/theme-constants/_colors.scss +23 -23
  76. package/src/lib/styles/tools/_spacing.scss +1 -1
  77. package/src/lib/styles/tools/form-fields/_box-field-elements.scss +19 -2
  78. package/src/lib/styles/tools/form-fields/_box-field-sizes.scss +11 -8
  79. package/src/lib/styles/tools/form-fields/_foundation.scss +7 -0
  80. package/src/lib/theme.scss +650 -567
  81. package/src/lib/styles/theme/_colors.scss +0 -65
  82. /package/src/lib/components/{Button/helpers → _helpers}/getRootPriorityClassName.js +0 -0
@@ -0,0 +1,7 @@
1
+ export const getPositionClassName = (modalPosition, styles) => {
2
+ if (modalPosition === 'top') {
3
+ return styles.isRootPositionTop;
4
+ }
5
+
6
+ return styles.isRootPositionCenter;
7
+ };
@@ -0,0 +1,19 @@
1
+ export const getSizeClassName = (modalSize, styles) => {
2
+ if (modalSize === 'small') {
3
+ return styles.isRootSizeSmall;
4
+ }
5
+
6
+ if (modalSize === 'medium') {
7
+ return styles.isRootSizeMedium;
8
+ }
9
+
10
+ if (modalSize === 'large') {
11
+ return styles.isRootSizeLarge;
12
+ }
13
+
14
+ if (modalSize === 'fullscreen') {
15
+ return styles.isRootSizeFullscreen;
16
+ }
17
+
18
+ return styles.isRootSizeAuto;
19
+ };
@@ -0,0 +1,126 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export const useModalFocus = (
4
+ autoFocus,
5
+ childrenWrapperRef,
6
+ primaryButtonRef,
7
+ closeButtonRef,
8
+ ) => {
9
+ useEffect(
10
+ () => {
11
+ // Following code finds all focusable elements and among them first not disabled form
12
+ // field element (input, textarea or select) or primary button and focuses it. This is
13
+ // necessary to have focus on one of those elements to be able to submit the form
14
+ // by pressing Enter key. If there are neither, it tries to focus any other focusable
15
+ // elements. In case there are none or `autoFocus` is disabled, childrenWrapperElement
16
+ // (Modal itself) is focused.
17
+
18
+ const childrenWrapperElement = childrenWrapperRef.current;
19
+
20
+ if (childrenWrapperElement == null) {
21
+ return () => {};
22
+ }
23
+
24
+ const childrenFocusableElements = Array.from(
25
+ childrenWrapperElement.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),
26
+ );
27
+
28
+ const firstFocusableElement = childrenFocusableElements[0];
29
+ const lastFocusableElement = childrenFocusableElements[childrenFocusableElements.length - 1];
30
+
31
+ const resolveFocusBeforeListener = () => {
32
+ if (!autoFocus || childrenFocusableElements.length === 0) {
33
+ childrenWrapperElement.tabIndex = -1;
34
+ childrenWrapperElement.focus();
35
+ return;
36
+ }
37
+
38
+ const firstFormFieldEl = childrenFocusableElements.find(
39
+ (element) => ['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName) && !element.disabled,
40
+ );
41
+
42
+ if (firstFormFieldEl) {
43
+ firstFormFieldEl.focus();
44
+ return;
45
+ }
46
+
47
+ if (primaryButtonRef?.current != null) {
48
+ primaryButtonRef.current.focus();
49
+ return;
50
+ }
51
+
52
+ firstFocusableElement.focus();
53
+ };
54
+
55
+ const keyPressHandler = (e) => {
56
+ if (e.key === 'Escape' && closeButtonRef?.current != null) {
57
+ closeButtonRef.current.click();
58
+ return;
59
+ }
60
+
61
+ if (
62
+ e.key === 'Enter'
63
+ && e.target.nodeName !== 'BUTTON'
64
+ && e.target.nodeName !== 'TEXTAREA'
65
+ && e.target.nodeName !== 'A'
66
+ && primaryButtonRef?.current != null
67
+ ) {
68
+ primaryButtonRef.current.click();
69
+ return;
70
+ }
71
+
72
+ // Following code traps focus inside Modal
73
+
74
+ if (e.key !== 'Tab') {
75
+ return;
76
+ }
77
+
78
+ if (childrenFocusableElements.length === 0) {
79
+ childrenWrapperElement.focus();
80
+ e.preventDefault();
81
+ return;
82
+ }
83
+
84
+ if (
85
+ ![
86
+ ...childrenFocusableElements,
87
+ childrenWrapperElement,
88
+ ]
89
+ .includes(window.document.activeElement)
90
+ ) {
91
+ firstFocusableElement.focus();
92
+ e.preventDefault();
93
+ return;
94
+ }
95
+
96
+ if (!e.shiftKey && window.document.activeElement === lastFocusableElement) {
97
+ firstFocusableElement.focus();
98
+ e.preventDefault();
99
+ return;
100
+ }
101
+
102
+ if (e.shiftKey
103
+ && (
104
+ window.document.activeElement === firstFocusableElement
105
+ || window.document.activeElement === childrenWrapperElement
106
+ )
107
+ ) {
108
+ lastFocusableElement.focus();
109
+ e.preventDefault();
110
+ }
111
+ };
112
+
113
+ resolveFocusBeforeListener();
114
+
115
+ window.document.addEventListener('keydown', keyPressHandler, false);
116
+
117
+ return () => window.document.removeEventListener('keydown', keyPressHandler, false);
118
+ },
119
+ [
120
+ autoFocus,
121
+ childrenWrapperRef,
122
+ primaryButtonRef,
123
+ closeButtonRef,
124
+ ],
125
+ );
126
+ };
@@ -0,0 +1,35 @@
1
+ import { useLayoutEffect } from 'react';
2
+
3
+ export const useModalScrollPrevention = (preventScrollUnderneath) => {
4
+ useLayoutEffect(
5
+ () => {
6
+ if (preventScrollUnderneath === 'off') {
7
+ return () => {};
8
+ }
9
+
10
+ if (preventScrollUnderneath === 'default') {
11
+ const scrollbarWidth = Math.abs(window.innerWidth - window.document.documentElement.clientWidth);
12
+ const prevOverflow = window.document.body.style.overflow;
13
+ const prevPaddingRight = window.document.body.style.paddingRight;
14
+
15
+ window.document.body.style.overflow = 'hidden';
16
+
17
+ if (Number.isNaN(parseInt(prevPaddingRight, 10))) {
18
+ window.document.body.style.paddingRight = `${scrollbarWidth}px`;
19
+ } else {
20
+ window.document.body.style.paddingRight = `calc(${prevPaddingRight} + ${scrollbarWidth}px)`;
21
+ }
22
+
23
+ return () => {
24
+ window.document.body.style.overflow = prevOverflow;
25
+ window.document.body.style.paddingRight = prevPaddingRight;
26
+ };
27
+ }
28
+
29
+ preventScrollUnderneath?.start();
30
+
31
+ return preventScrollUnderneath?.reset;
32
+ },
33
+ [preventScrollUnderneath],
34
+ );
35
+ };
@@ -3,7 +3,7 @@
3
3
  @use "../../styles/theme/borders";
4
4
  @use "../../styles/theme/typography";
5
5
 
6
- $border-radius: borders.$radius;
6
+ $border-radius: borders.$radius-2;
7
7
  $z-index: z-indexes.$modal;
8
8
  $backdrop-z-index: z-indexes.$modal-backdrop;
9
- $title-font-size: map.get(typography.$size-values, 1);
9
+ $title-font-size: map.get(typography.$font-size-values, 2);
@@ -88,19 +88,22 @@ complete list of accepted values.
88
88
  <span id="alignment-options-label">Alignment:</span>
89
89
  </ToolbarItem>
90
90
  <ToolbarItem>
91
- <ButtonGroup aria-labelledby="alignment-options-label">
91
+ <ButtonGroup aria-labelledby="alignment-options-label" priority="outline">
92
92
  <Button
93
- color={align === '-start' ? 'dark' : 'primary'}
93
+ aria-pressed={align === '-start'}
94
+ color={align === '-start' ? 'selected' : 'secondary'}
94
95
  label="start"
95
96
  onClick={() => setAlign('-start')}
96
97
  />
97
98
  <Button
98
- color={align === '' ? 'dark' : 'primary'}
99
+ aria-pressed={align === ''}
100
+ color={align === '' ? 'selected' : 'secondary'}
99
101
  label="center"
100
102
  onClick={() => setAlign('')}
101
103
  />
102
104
  <Button
103
- color={align === '-end' ? 'dark' : 'primary'}
105
+ aria-pressed={align === '-end'}
106
+ color={align === '-end' ? 'selected' : 'secondary'}
104
107
  label="end"
105
108
  onClick={() => setAlign('-end')}
106
109
  />
@@ -77,7 +77,12 @@ See [API](#api) for all available options.
77
77
  - Use **clear, calm error messages** when there's a problem with what they
78
78
  entered.
79
79
 
80
- 📖 [Read more about checkboxes and radios at Nielsen Norman Group.](https://www.nngroup.com/articles/checkboxes-vs-radio-buttons/)
80
+ - In the background, Radio uses the [`fieldset`][fieldset] element. Not only it
81
+ improves the [accessibility] of the group, it also allows you to make use of
82
+ its built-in features like disabling all nested inputs or pairing the group
83
+ with a form outside. Consult [the MDN docs][fieldset] to learn more.
84
+
85
+ 📖 [Read more about checkboxes and radios at Nielsen Norman Group.][nng-radio]
81
86
 
82
87
  ## Invisible Label
83
88
 
@@ -308,5 +313,8 @@ options. On top of that, the following options are available for Radio.
308
313
  | `--rui-FormField--check__input--radio__border-radius` | Input corner radius |
309
314
  | `--rui-FormField--check__input--radio--checked__background-image` | Checked input background image (inline, URL, …) |
310
315
 
316
+ [nng-radio]: https://www.nngroup.com/articles/checkboxes-vs-radio-buttons/
317
+ [fieldset]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset
318
+ [accessibility]: https://www.w3.org/WAI/tutorials/forms/grouping/
311
319
  [React synthetic events]: https://reactjs.org/docs/events.html
312
320
  [radio]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio#additional_attributes
@@ -25,7 +25,8 @@ export const Radio = ({
25
25
  const context = useContext(FormLayoutContext);
26
26
 
27
27
  return (
28
- <div
28
+ <fieldset
29
+ {...transferProps(restProps)}
29
30
  className={classNames(
30
31
  styles.root,
31
32
  context && styles.isRootInFormLayout,
@@ -36,53 +37,59 @@ export const Radio = ({
36
37
  required && styles.isRootRequired,
37
38
  getRootValidationStateClassName(validationState, styles),
38
39
  )}
40
+ disabled={disabled}
39
41
  id={id}
40
42
  >
43
+ <legend
44
+ className={styles.legend}
45
+ id={id && `${id}__label`}
46
+ >
47
+ {label}
48
+ </legend>
41
49
  <div
50
+ aria-hidden
42
51
  className={classNames(
43
52
  styles.label,
44
53
  !isLabelVisible && styles.isLabelHidden,
45
54
  )}
46
- id={id && `${id}__labelText`}
55
+ id={id && `${id}__displayLabel`}
47
56
  >
48
57
  {label}
49
58
  </div>
50
59
  <div className={styles.field}>
51
- <ul className={styles.list}>
60
+ <div className={styles.options}>
52
61
  {
53
62
  options.map((option) => {
54
63
  const key = option.key ?? option.value;
55
64
  return (
56
- <li key={key}>
57
- <label
58
- className={styles.option}
59
- htmlFor={id && `${id}__item__${key}`}
60
- id={id && `${id}__item__${key}__label`}
65
+ <label
66
+ className={styles.option}
67
+ htmlFor={id && `${id}__item__${key}`}
68
+ id={id && `${id}__item__${key}__label`}
69
+ key={key}
70
+ >
71
+ <input
72
+ className={styles.input}
73
+ checked={restProps.onChange
74
+ ? (value === option.value) || false
75
+ : undefined}
76
+ disabled={disabled || option.disabled}
77
+ id={id && `${id}__item__${key}`}
78
+ name={id}
79
+ type="radio"
80
+ value={option.value}
81
+ />
82
+ <span
83
+ className={styles.optionLabel}
84
+ id={id && `${id}__item__${key}__labelText`}
61
85
  >
62
- <input
63
- {...transferProps(restProps)}
64
- className={styles.input}
65
- checked={restProps.onChange
66
- ? (value === option.value) || false
67
- : undefined}
68
- disabled={disabled || option.disabled}
69
- id={id && `${id}__item__${key}`}
70
- name={id}
71
- type="radio"
72
- value={option.value}
73
- />
74
- <span
75
- className={styles.optionLabel}
76
- id={id && `${id}__item__${key}__labelText`}
77
- >
78
- { option.label }
79
- </span>
80
- </label>
81
- </li>
86
+ { option.label }
87
+ </span>
88
+ </label>
82
89
  );
83
90
  })
84
91
  }
85
- </ul>
92
+ </div>
86
93
  {helpText && (
87
94
  <div
88
95
  className={styles.helpText}
@@ -100,7 +107,7 @@ export const Radio = ({
100
107
  </div>
101
108
  )}
102
109
  </div>
103
- </div>
110
+ </fieldset>
104
111
  );
105
112
  };
106
113
 
@@ -129,7 +136,8 @@ Radio.propTypes = {
129
136
  * ID of the root HTML element.
130
137
  *
131
138
  * Also serves as base for ids of nested elements:
132
- * * `<ID>__labelText`
139
+ * * `<ID>__label`
140
+ * * `<ID>__displayLabel`
133
141
  * * `<ID>__helpText`
134
142
  * * `<ID>__validationText`
135
143
  *
@@ -1,3 +1,6 @@
1
+ // 1. Legends are tricky to style, let's use a `div` instead.
2
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset#styling_with_css
3
+
1
4
  @use "../../styles/tools/form-fields/box-field-elements";
2
5
  @use "../../styles/tools/form-fields/box-field-layout";
3
6
  @use "../../styles/tools/form-fields/foundation";
@@ -11,15 +14,22 @@
11
14
  // Foundation
12
15
  .root {
13
16
  @include foundation.root();
17
+ @include foundation.fieldset();
14
18
  @include variants.visual(check);
15
19
  }
16
20
 
21
+ // 1.
22
+ .legend {
23
+ @include accessibility.hide-text();
24
+ }
25
+
26
+ // 1.
17
27
  .label,
18
28
  .optionLabel {
19
29
  @include foundation.label();
20
30
  }
21
31
 
22
- .list {
32
+ .options {
23
33
  @include reset.list();
24
34
  }
25
35
 
@@ -297,8 +297,8 @@ by smaller or bigger portions.
297
297
  arrows
298
298
  arrowsScrollStep={300}
299
299
  direction="horizontal"
300
- nextArrowElement={(<span class="typography-size-3">➡️</span>)}
301
- prevArrowElement={(<span class="typography-size-3">⬅️</span>)}
300
+ nextArrowElement={(<span class="font-size-3">➡️</span>)}
301
+ prevArrowElement={(<span class="font-size-3">⬅️</span>)}
302
302
  scrollbar={false}
303
303
  >
304
304
  <Placeholder>
@@ -7,6 +7,7 @@ import { getRootValidationStateClassName } from '../_helpers/getRootValidationSt
7
7
  import { resolveContextOrProp } from '../_helpers/resolveContextOrProp';
8
8
  import { transferProps } from '../_helpers/transferProps';
9
9
  import { FormLayoutContext } from '../FormLayout';
10
+ import { InputGroupContext } from '../InputGroup/InputGroupContext';
10
11
  import { Option } from './_components/Option';
11
12
  import styles from './SelectField.scss';
12
13
 
@@ -28,20 +29,25 @@ export const SelectField = React.forwardRef((props, ref) => {
28
29
  ...restProps
29
30
  } = props;
30
31
 
31
- const context = useContext(FormLayoutContext);
32
+ const formLayoutContext = useContext(FormLayoutContext);
33
+ const inputGroupContext = useContext(InputGroupContext);
32
34
 
33
35
  return (
34
36
  <label
35
37
  className={classNames(
36
38
  styles.root,
37
39
  fullWidth && styles.isRootFullWidth,
38
- context && styles.isRootInFormLayout,
39
- resolveContextOrProp(context && context.layout, layout) === 'horizontal'
40
+ formLayoutContext && styles.isRootInFormLayout,
41
+ resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled) && styles.isRootDisabled,
42
+ resolveContextOrProp(formLayoutContext && formLayoutContext.layout, layout) === 'horizontal'
40
43
  ? styles.isRootLayoutHorizontal
41
44
  : styles.isRootLayoutVertical,
42
- disabled && styles.isRootDisabled,
45
+ inputGroupContext && styles.isRootGrouped,
43
46
  required && styles.isRootRequired,
44
- getRootSizeClassName(size, styles),
47
+ getRootSizeClassName(
48
+ resolveContextOrProp(inputGroupContext && inputGroupContext.size, size),
49
+ styles,
50
+ ),
45
51
  getRootValidationStateClassName(validationState, styles),
46
52
  variant === 'filled' ? styles.isRootVariantFilled : styles.isRootVariantOutline,
47
53
  )}
@@ -51,7 +57,7 @@ export const SelectField = React.forwardRef((props, ref) => {
51
57
  <div
52
58
  className={classNames(
53
59
  styles.label,
54
- !isLabelVisible && styles.isLabelHidden,
60
+ (!isLabelVisible || inputGroupContext) && styles.isLabelHidden,
55
61
  )}
56
62
  id={id && `${id}__labelText`}
57
63
  >
@@ -62,7 +68,7 @@ export const SelectField = React.forwardRef((props, ref) => {
62
68
  <select
63
69
  {...transferProps(restProps)}
64
70
  className={styles.input}
65
- disabled={disabled}
71
+ disabled={resolveContextOrProp(inputGroupContext && inputGroupContext.disabled, disabled)}
66
72
  id={id}
67
73
  ref={ref}
68
74
  required={required}
@@ -110,7 +116,7 @@ export const SelectField = React.forwardRef((props, ref) => {
110
116
  {helpText}
111
117
  </div>
112
118
  )}
113
- {validationText && (
119
+ {(validationText && !inputGroupContext) && (
114
120
  <div
115
121
  className={styles.validationText}
116
122
  id={id && `${id}__validationText`}
@@ -169,6 +175,8 @@ SelectField.propTypes = {
169
175
  /**
170
176
  * If `false`, the label will be visually hidden (but remains accessible by assistive
171
177
  * technologies).
178
+ *
179
+ * Automatically set to `false` when the component is rendered within `InputGroup` component.
172
180
  */
173
181
  isLabelVisible: PropTypes.bool,
174
182
  /**
@@ -224,6 +232,8 @@ SelectField.propTypes = {
224
232
  required: PropTypes.bool,
225
233
  /**
226
234
  * Size of the field.
235
+ *
236
+ * Ignored if the component is rendered within `InputGroup` component as the value is inherited in such case.
227
237
  */
228
238
  size: PropTypes.oneOf(['small', 'medium', 'large']),
229
239
  /**
@@ -232,6 +242,9 @@ SelectField.propTypes = {
232
242
  validationState: PropTypes.oneOf(['invalid', 'valid', 'warning']),
233
243
  /**
234
244
  * Validation message to be displayed.
245
+ *
246
+ * Validation text is never rendered when the component is placed into `InputGroup`. Instead, the `InputGroup`
247
+ * component itself renders all validation texts of its nested components.
235
248
  */
236
249
  validationText: PropTypes.node,
237
250
  /**
@@ -102,3 +102,8 @@
102
102
  .isRootSizeLarge {
103
103
  @include box-field-sizes.size(large);
104
104
  }
105
+
106
+ // Groups
107
+ .isRootGrouped {
108
+ @include box-field-elements.in-group-layout();
109
+ }
@@ -14,12 +14,13 @@
14
14
  border-bottom-width: 2px;
15
15
  }
16
16
 
17
+ .tableHeadCellLayout {
18
+ display: flex;
19
+ gap: settings.$cell-padding-x;
20
+ align-items: center;
21
+ }
22
+
17
23
  .isTableCellSortingActive,
18
24
  .isTableHeadCellSortingActive {
19
25
  background-color: settings.$sorted-background-color;
20
26
  }
21
-
22
- .sortButton {
23
- display: inline-block;
24
- margin-right: settings.$cell-padding-x;
25
- }
@@ -16,23 +16,26 @@ export const TableHeaderCell = ({
16
16
  className={isSortingActive ? styles.isTableHeadCellSortingActive : styles.tableHeadCell}
17
17
  id={id}
18
18
  >
19
- {sort && column.isSortable && (
20
- <div className={styles.sortButton}>
19
+ <span className={styles.tableHeadCellLayout}>
20
+ {sort && column.isSortable && (
21
21
  <Button
22
+ aria-pressed={isSortingActive}
22
23
  beforeLabel={
23
24
  sortDirection === 'asc'
24
25
  ? sort.ascendingIcon
25
26
  : sort.descendingIcon
26
27
  }
28
+ color={isSortingActive ? 'selected' : 'secondary'}
27
29
  id={id && `${id}__sortButton`}
28
30
  label={sortDirection}
29
31
  labelVisibility="none"
30
32
  onClick={() => sort.onClick(column.name, sortDirection)}
31
33
  priority="flat"
34
+ size="small"
32
35
  />
33
- </div>
34
- )}
35
- {column.label}
36
+ )}
37
+ {column.label}
38
+ </span>
36
39
  </th>
37
40
  );
38
41
  };
@@ -1,15 +1,14 @@
1
1
  @use "sass:map";
2
2
  @use "../../styles/theme/borders";
3
- @use "../../styles/theme/colors";
4
3
  @use "../../styles/theme/typography";
5
4
  @use "../../styles/tools/spacing";
6
5
 
7
6
  $cell-padding-x: spacing.of(3);
8
7
  $cell-padding-y: spacing.of(1);
9
8
  $border-width: borders.$width;
10
- $border-color: map.get(colors.$grays, gray-100);
11
- $background-color: colors.$white;
12
- $head-background-color: colors.$white;
9
+ $border-color: var(--rui-color-border-secondary);
10
+ $background-color: var(--rui-color-background-basic);
11
+ $head-background-color: var(--rui-color-background-basic);
13
12
  $head-font-weight: map.get(typography.$font-weight-values, bold);
14
- $hover-background-color: map.get(colors.$grays, gray-50);
15
- $sorted-background-color: map.get(colors.$grays, gray-50);
13
+ $hover-background-color: var(--rui-color-background-interactive-hover);
14
+ $sorted-background-color: var(--rui-color-background-selected);
@@ -156,19 +156,22 @@ will override automatic break point selection in `auto` mode when present.
156
156
  <span id="word-wrapping-options-label">Word wrapping:</span>
157
157
  </ToolbarItem>
158
158
  <ToolbarItem>
159
- <ButtonGroup aria-labelledby="word-wrapping-options-label">
159
+ <ButtonGroup aria-labelledby="word-wrapping-options-label" priority="outline">
160
160
  <Button
161
- color={wordWrapping === 'normal' ? 'dark' : 'primary'}
161
+ aria-pressed={wordWrapping === 'normal'}
162
+ color={wordWrapping === 'normal' ? 'selected' : 'secondary'}
162
163
  label="normal"
163
164
  onClick={() => setWordWrapping('normal')}
164
165
  />
165
166
  <Button
166
- color={wordWrapping === 'long-words' ? 'dark' : 'primary'}
167
+ aria-pressed={wordWrapping === 'long-words'}
168
+ color={wordWrapping === 'long-words' ? 'selected' : 'secondary'}
167
169
  label="long-words"
168
170
  onClick={() => setWordWrapping('long-words')}
169
171
  />
170
172
  <Button
171
- color={wordWrapping === 'anywhere' ? 'dark' : 'primary'}
173
+ aria-pressed={wordWrapping === 'anywhere'}
174
+ color={wordWrapping === 'anywhere' ? 'selected' : 'secondary'}
172
175
  label="anywhere"
173
176
  onClick={() => setWordWrapping('anywhere')}
174
177
  />
@@ -180,19 +183,22 @@ will override automatic break point selection in `auto` mode when present.
180
183
  <span id="hyphens-options-label">Hyphens:</span>
181
184
  </ToolbarItem>
182
185
  <ToolbarItem>
183
- <ButtonGroup aria-labelledby="hyphens-options-label">
186
+ <ButtonGroup aria-labelledby="hyphens-options-label" priority="outline">
184
187
  <Button
185
- color={hyphens === 'none' ? 'dark' : 'primary'}
188
+ aria-pressed={hyphens === 'none'}
189
+ color={hyphens === 'none' ? 'selected' : 'secondary'}
186
190
  label="none"
187
191
  onClick={() => setHyphens('none')}
188
192
  />
189
193
  <Button
190
- color={hyphens === 'auto' ? 'dark' : 'primary'}
194
+ aria-pressed={hyphens === 'auto'}
195
+ color={hyphens === 'auto' ? 'selected' : 'secondary'}
191
196
  label="auto"
192
197
  onClick={() => setHyphens('auto')}
193
198
  />
194
199
  <Button
195
- color={hyphens === 'manual' ? 'dark' : 'primary'}
200
+ aria-pressed={hyphens === 'manual'}
201
+ color={hyphens === 'manual' ? 'selected' : 'secondary'}
196
202
  label="manual"
197
203
  onClick={() => setHyphens('manual')}
198
204
  />