carbon-react 117.1.1 → 117.1.2

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 (41) hide show
  1. package/esm/__internal__/checkable-input/checkable-input.component.js +6 -4
  2. package/esm/__internal__/checkable-input/hidden-checkable-input.component.d.ts +4 -0
  3. package/esm/__internal__/checkable-input/hidden-checkable-input.component.js +19 -2
  4. package/esm/__internal__/input/input.component.d.ts +4 -0
  5. package/esm/__internal__/input/input.component.js +13 -0
  6. package/esm/__internal__/input-icon-toggle/input-icon-toggle.component.js +1 -1
  7. package/esm/__internal__/label/label.component.js +1 -2
  8. package/esm/__internal__/validation-message/validation-message.component.d.ts +3 -1
  9. package/esm/__internal__/validation-message/validation-message.component.js +3 -0
  10. package/esm/__internal__/validations/validation-icon.component.js +5 -3
  11. package/esm/components/checkbox/checkbox.component.js +2 -0
  12. package/esm/components/decimal/decimal.component.js +2 -0
  13. package/esm/components/grouped-character/grouped-character.component.js +2 -0
  14. package/esm/components/number/number.component.js +2 -0
  15. package/esm/components/radio-button/radio-button.component.js +2 -0
  16. package/esm/components/switch/switch.component.js +2 -0
  17. package/esm/components/textarea/textarea.component.js +11 -7
  18. package/esm/components/textbox/textbox.component.js +12 -8
  19. package/esm/hooks/__internal__/useInputAccessibility/useInputAccessibility.d.ts +3 -2
  20. package/esm/hooks/__internal__/useInputAccessibility/useInputAccessibility.js +10 -3
  21. package/lib/__internal__/checkable-input/checkable-input.component.js +6 -4
  22. package/lib/__internal__/checkable-input/hidden-checkable-input.component.d.ts +4 -0
  23. package/lib/__internal__/checkable-input/hidden-checkable-input.component.js +19 -2
  24. package/lib/__internal__/input/input.component.d.ts +4 -0
  25. package/lib/__internal__/input/input.component.js +13 -0
  26. package/lib/__internal__/input-icon-toggle/input-icon-toggle.component.js +1 -1
  27. package/lib/__internal__/label/label.component.js +1 -2
  28. package/lib/__internal__/validation-message/validation-message.component.d.ts +3 -1
  29. package/lib/__internal__/validation-message/validation-message.component.js +3 -0
  30. package/lib/__internal__/validations/validation-icon.component.js +5 -2
  31. package/lib/components/checkbox/checkbox.component.js +2 -0
  32. package/lib/components/decimal/decimal.component.js +2 -0
  33. package/lib/components/grouped-character/grouped-character.component.js +2 -0
  34. package/lib/components/number/number.component.js +2 -0
  35. package/lib/components/radio-button/radio-button.component.js +2 -0
  36. package/lib/components/switch/switch.component.js +2 -0
  37. package/lib/components/textarea/textarea.component.js +11 -7
  38. package/lib/components/textbox/textbox.component.js +12 -8
  39. package/lib/hooks/__internal__/useInputAccessibility/useInputAccessibility.d.ts +3 -2
  40. package/lib/hooks/__internal__/useInputAccessibility/useInputAccessibility.js +10 -3
  41. package/package.json +1 -1
@@ -44,7 +44,7 @@ const CheckableInput = /*#__PURE__*/React.forwardRef(({
44
44
  const {
45
45
  labelId,
46
46
  fieldHelpId,
47
- validationIconId,
47
+ validationId,
48
48
  ariaDescribedBy
49
49
  } = useInputAccessibility({
50
50
  id,
@@ -70,17 +70,16 @@ const CheckableInput = /*#__PURE__*/React.forwardRef(({
70
70
  labelId,
71
71
  labelInline,
72
72
  labelSpacing,
73
- name: id,
74
73
  reverse,
75
74
  warning,
76
- validationIconId,
75
+ validationIconId: validationId,
77
76
  // We don't want an asterisk on each radio control, only the legend
78
77
  // However, we still want the input element to receive the required prop
79
78
  isRequired: isRadio ? undefined : required,
80
79
  useValidationIcon: validationOnLabel
81
80
  };
82
81
  const inputProps = {
83
- "aria-describedby": ariaDescribedBy,
82
+ ariaDescribedBy,
84
83
  "aria-labelledby": ariaLabelledBy,
85
84
  "aria-invalid": !!error,
86
85
  autoFocus,
@@ -95,6 +94,7 @@ const CheckableInput = /*#__PURE__*/React.forwardRef(({
95
94
  onFocus,
96
95
  required,
97
96
  ref,
97
+ validationIconId: validationId,
98
98
  ...props
99
99
  };
100
100
  return /*#__PURE__*/React.createElement(StyledCheckableInputWrapper, {
@@ -161,6 +161,7 @@ CheckableInput.propTypes = {
161
161
  "aria-valuemin": PropTypes.number,
162
162
  "aria-valuenow": PropTypes.number,
163
163
  "aria-valuetext": PropTypes.string,
164
+ "ariaDescribedBy": PropTypes.string,
164
165
  "ariaLabelledBy": PropTypes.string,
165
166
  "autoCapitalize": PropTypes.string,
166
167
  "autoComplete": PropTypes.string,
@@ -460,6 +461,7 @@ CheckableInput.propTypes = {
460
461
  "type": PropTypes.string.isRequired,
461
462
  "typeof": PropTypes.string,
462
463
  "unselectable": PropTypes.oneOf(["off", "on"]),
464
+ "validationIconId": PropTypes.string,
463
465
  "validationOnLabel": PropTypes.bool,
464
466
  "value": PropTypes.string,
465
467
  "vocab": PropTypes.string,
@@ -1,5 +1,7 @@
1
1
  import React from "react";
2
2
  export interface CommonHiddenCheckableInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "size" | "type"> {
3
+ /** The id of the element that describe the input. */
4
+ ariaDescribedBy?: string;
3
5
  /** Prop to specify the aria-labelledby attribute of the input */
4
6
  ariaLabelledBy?: string;
5
7
  /** If true the Component will be focused when page load */
@@ -18,6 +20,8 @@ export interface CommonHiddenCheckableInputProps extends Omit<React.InputHTMLAtt
18
20
  onMouseLeave?: (ev: React.MouseEvent<HTMLInputElement>) => void;
19
21
  /** OnMouseEnter event handler */
20
22
  onMouseEnter?: (ev: React.MouseEvent<HTMLInputElement>) => void;
23
+ /** Id of the validation icon */
24
+ validationIconId?: string;
21
25
  /** Value of the input */
22
26
  value?: string;
23
27
  }
@@ -5,6 +5,7 @@ import PropTypes from "prop-types";
5
5
  import HiddenCheckableInputStyle from "./hidden-checkable-input.style";
6
6
  import { InputContext, InputGroupContext } from "../input-behaviour";
7
7
  const HiddenCheckableInput = /*#__PURE__*/React.forwardRef(({
8
+ ariaDescribedBy,
8
9
  ariaLabelledBy,
9
10
  name,
10
11
  checked,
@@ -13,19 +14,24 @@ const HiddenCheckableInput = /*#__PURE__*/React.forwardRef(({
13
14
  onChange,
14
15
  autoFocus,
15
16
  role,
17
+ validationIconId,
16
18
  ...props
17
19
  }, ref) => {
18
20
  const {
19
21
  onBlur,
20
22
  onFocus,
21
23
  onMouseEnter,
22
- onMouseLeave
24
+ onMouseLeave,
25
+ hasFocus,
26
+ hasMouseOver
23
27
  } = useContext(InputContext);
24
28
  const {
25
29
  onBlur: onBlurGroup,
26
30
  onFocus: onFocusGroup,
27
31
  onMouseEnter: onMouseEnterGroup,
28
- onMouseLeave: onMouseLeaveGroup
32
+ onMouseLeave: onMouseLeaveGroup,
33
+ hasFocus: hasGroupFocus,
34
+ hasMouseOver: hasGroupMouseOver
29
35
  } = useContext(InputGroupContext);
30
36
 
31
37
  const handleFocus = ev => {
@@ -52,7 +58,16 @@ const HiddenCheckableInput = /*#__PURE__*/React.forwardRef(({
52
58
  if (onMouseLeaveGroup) onMouseLeaveGroup();
53
59
  };
54
60
 
61
+ const hasValidationPart = (hasFocus || hasGroupFocus || hasMouseOver || hasGroupMouseOver) && validationIconId;
62
+ const descriptionList = ariaDescribedBy ? [ariaDescribedBy] : [];
63
+
64
+ if (hasValidationPart) {
65
+ descriptionList.push(validationIconId);
66
+ }
67
+
68
+ const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
55
69
  return /*#__PURE__*/React.createElement(HiddenCheckableInputStyle, _extends({
70
+ "aria-describedby": combinedDescription,
56
71
  "aria-labelledby": ariaLabelledBy,
57
72
  autoFocus: autoFocus,
58
73
  "aria-checked": checked,
@@ -123,6 +138,7 @@ HiddenCheckableInput.propTypes = {
123
138
  "aria-valuemin": PropTypes.number,
124
139
  "aria-valuenow": PropTypes.number,
125
140
  "aria-valuetext": PropTypes.string,
141
+ "ariaDescribedBy": PropTypes.string,
126
142
  "ariaLabelledBy": PropTypes.string,
127
143
  "autoCapitalize": PropTypes.string,
128
144
  "autoComplete": PropTypes.string,
@@ -359,6 +375,7 @@ HiddenCheckableInput.propTypes = {
359
375
  "type": PropTypes.string.isRequired,
360
376
  "typeof": PropTypes.string,
361
377
  "unselectable": PropTypes.oneOf(["off", "on"]),
378
+ "validationIconId": PropTypes.string,
362
379
  "value": PropTypes.string,
363
380
  "vocab": PropTypes.string,
364
381
  "width": PropTypes.oneOfType([PropTypes.number, PropTypes.string])
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
2
  export interface CommonInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
3
3
  align?: "right" | "left";
4
+ /** The id of the element that describe the input. */
5
+ ariaDescribedBy?: string;
4
6
  /** Override the variant component */
5
7
  as?: React.ElementType;
6
8
  /** If true the Component will be focused when rendered */
@@ -29,6 +31,8 @@ export interface CommonInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
29
31
  readOnly?: boolean;
30
32
  /** Flag to configure component as mandatory */
31
33
  required?: boolean;
34
+ /** Id of the validation icon */
35
+ validationIconId?: string;
32
36
  }
33
37
  export interface InputProps extends CommonInputProps {
34
38
  /** The visible width of the text control, in average character widths */
@@ -30,6 +30,7 @@ function selectTextOnFocus(input) {
30
30
  const Input = /*#__PURE__*/React.forwardRef(({
31
31
  align,
32
32
  "aria-labelledby": ariaLabelledBy,
33
+ ariaDescribedBy,
33
34
  placeholder,
34
35
  disabled,
35
36
  readOnly,
@@ -45,6 +46,7 @@ const Input = /*#__PURE__*/React.forwardRef(({
45
46
  type = "text",
46
47
  id,
47
48
  name,
49
+ validationIconId,
48
50
  ...rest
49
51
  }, ref) => {
50
52
  const context = useContext(InputContext);
@@ -145,7 +147,16 @@ const Input = /*#__PURE__*/React.forwardRef(({
145
147
  handleDeferred(ev);
146
148
  };
147
149
 
150
+ const hasValidationPart = (context.hasFocus || groupContext.hasFocus || context.hasMouseOver || groupContext.hasMouseOver) && validationIconId;
151
+ const descriptionList = ariaDescribedBy ? [ariaDescribedBy] : [];
152
+
153
+ if (hasValidationPart) {
154
+ descriptionList.push(validationIconId);
155
+ }
156
+
157
+ const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
148
158
  return /*#__PURE__*/React.createElement(StyledInput, _extends({}, rest, {
159
+ "aria-describedby": combinedDescription,
149
160
  "aria-labelledby": ariaLabelledBy,
150
161
  align: align,
151
162
  placeholder: placeholder,
@@ -216,6 +227,7 @@ Input.propTypes = {
216
227
  "aria-valuemin": PropTypes.number,
217
228
  "aria-valuenow": PropTypes.number,
218
229
  "aria-valuetext": PropTypes.string,
230
+ "ariaDescribedBy": PropTypes.string,
219
231
  "as": PropTypes.elementType,
220
232
  "autoCapitalize": PropTypes.string,
221
233
  "autoComplete": PropTypes.string,
@@ -508,6 +520,7 @@ Input.propTypes = {
508
520
  "type": PropTypes.string,
509
521
  "typeof": PropTypes.string,
510
522
  "unselectable": PropTypes.oneOf(["off", "on"]),
523
+ "validationIconId": PropTypes.string,
511
524
  "value": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.number, PropTypes.string]),
512
525
  "vocab": PropTypes.string,
513
526
  "width": PropTypes.oneOfType([PropTypes.number, PropTypes.string])
@@ -47,7 +47,7 @@ const InputIconToggle = ({
47
47
  onBlur: onBlur,
48
48
  isPartOfInput: true,
49
49
  tabIndex: iconTabIndex,
50
- iconId: validationIconId,
50
+ tooltipId: validationIconId,
51
51
  tooltipPosition: align === "right" ? "left" : "right"
52
52
  }));
53
53
  }
@@ -85,8 +85,7 @@ const Label = ({
85
85
  inline
86
86
  });
87
87
  return /*#__PURE__*/React.createElement(StyledIconWrapper, null, /*#__PURE__*/React.createElement(ValidationIcon, {
88
- iconId: validationIconId,
89
- tooltipId: tooltipId,
88
+ tooltipId: validationIconId,
90
89
  error: error,
91
90
  warning: warning,
92
91
  info: info,
@@ -3,9 +3,11 @@ export interface ValidationMessageProps {
3
3
  /** Indicate that error has occurred
4
4
  Pass string to display hint with error */
5
5
  error?: boolean | string;
6
+ /** Id of the component, to be used for accessibility purposes */
7
+ validationId?: string;
6
8
  /** Indicate that warning has occurred
7
9
  Pass string to display hint with warning */
8
10
  warning?: boolean | string;
9
11
  }
10
- declare const ValidationMessage: ({ error, warning }: ValidationMessageProps) => JSX.Element | null;
12
+ declare const ValidationMessage: ({ error, validationId, warning, }: ValidationMessageProps) => JSX.Element | null;
11
13
  export default ValidationMessage;
@@ -4,17 +4,20 @@ import StyledValidationMessage from "./validation-message.style";
4
4
 
5
5
  const ValidationMessage = ({
6
6
  error,
7
+ validationId,
7
8
  warning
8
9
  }) => {
9
10
  const validation = error || warning;
10
11
  const isStringValidation = typeof validation === "string";
11
12
  return isStringValidation ? /*#__PURE__*/React.createElement(StyledValidationMessage, {
13
+ id: validationId,
12
14
  isWarning: !!(!error && warning)
13
15
  }, validation) : null;
14
16
  };
15
17
 
16
18
  ValidationMessage.propTypes = {
17
19
  "error": PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
20
+ "validationId": PropTypes.string,
18
21
  "warning": PropTypes.oneOfType([PropTypes.string, PropTypes.bool])
19
22
  };
20
23
  export default ValidationMessage;
@@ -1,8 +1,9 @@
1
1
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
2
 
3
- import React, { useContext, useState } from "react";
3
+ import React, { useContext, useRef, useState } from "react";
4
4
  import PropTypes from "prop-types";
5
5
  import invariant from "invariant";
6
+ import guid from "../utils/helpers/guid";
6
7
  import Icon from "../../components/icon";
7
8
  import ValidationIconStyle from "./validation-icon.style";
8
9
  import { InputContext, InputGroupContext } from "../input-behaviour";
@@ -35,6 +36,7 @@ const ValidationIcon = ({
35
36
  tooltipFlipOverrides,
36
37
  ...rest
37
38
  }) => {
39
+ const validationTooltipId = useRef(tooltipId || guid());
38
40
  const flipBehaviourCheck = Array.isArray(tooltipFlipOverrides) && tooltipFlipOverrides.every(override => ["bottom", "left", "right", "top"].includes(override));
39
41
 
40
42
  if (tooltipFlipOverrides) {
@@ -76,10 +78,11 @@ const ValidationIcon = ({
76
78
  },
77
79
  isPartOfInput: isPartOfInput
78
80
  }, filterStyledSystemMarginProps(rest)), /*#__PURE__*/React.createElement(Icon, {
81
+ "aria-describedby": validationTooltipId.current,
79
82
  key: `${validationType}-icon`,
80
83
  type: validationType,
81
84
  tabIndex: tabIndex,
82
- tooltipId: tooltipId,
85
+ tooltipId: validationTooltipId.current,
83
86
  tooltipMessage: validationMessage,
84
87
  tooltipPosition: tooltipPosition,
85
88
  tooltipVisible: hasFocus || hasMouseOver || groupHasFocus || groupHasMouseOver || triggeredByIcon,
@@ -87,7 +90,6 @@ const ValidationIcon = ({
87
90
  isPartOfInput: isPartOfInput,
88
91
  inputSize: size,
89
92
  id: iconId,
90
- ariaLabel: validationMessage,
91
93
  focusable: tabIndex !== -1
92
94
  }));
93
95
  };
@@ -165,6 +165,7 @@ Checkbox.propTypes = {
165
165
  "aria-valuemin": PropTypes.number,
166
166
  "aria-valuenow": PropTypes.number,
167
167
  "aria-valuetext": PropTypes.string,
168
+ "ariaDescribedBy": PropTypes.string,
168
169
  "ariaLabelledBy": PropTypes.string,
169
170
  "autoCapitalize": PropTypes.string,
170
171
  "autoComplete": PropTypes.string,
@@ -625,6 +626,7 @@ Checkbox.propTypes = {
625
626
  "translate": PropTypes.oneOf(["no", "yes"]),
626
627
  "typeof": PropTypes.string,
627
628
  "unselectable": PropTypes.oneOf(["off", "on"]),
629
+ "validationIconId": PropTypes.string,
628
630
  "validationOnLabel": PropTypes.bool,
629
631
  "value": PropTypes.string,
630
632
  "vocab": PropTypes.string,
@@ -268,6 +268,7 @@ Decimal.propTypes = {
268
268
  "aria-valuemin": PropTypes.number,
269
269
  "aria-valuenow": PropTypes.number,
270
270
  "aria-valuetext": PropTypes.string,
271
+ "ariaDescribedBy": PropTypes.string,
271
272
  "as": PropTypes.elementType,
272
273
  "autoCapitalize": PropTypes.string,
273
274
  "autoComplete": PropTypes.string,
@@ -737,6 +738,7 @@ Decimal.propTypes = {
737
738
  "translate": PropTypes.oneOf(["no", "yes"]),
738
739
  "typeof": PropTypes.string,
739
740
  "unselectable": PropTypes.oneOf(["off", "on"]),
741
+ "validationIconId": PropTypes.string,
740
742
  "validationOnLabel": PropTypes.bool,
741
743
  "value": PropTypes.string,
742
744
  "vocab": PropTypes.string,
@@ -183,6 +183,7 @@ GroupedCharacter.propTypes = {
183
183
  "aria-valuemin": PropTypes.number,
184
184
  "aria-valuenow": PropTypes.number,
185
185
  "aria-valuetext": PropTypes.string,
186
+ "ariaDescribedBy": PropTypes.string,
186
187
  "as": PropTypes.elementType,
187
188
  "autoCapitalize": PropTypes.string,
188
189
  "autoComplete": PropTypes.string,
@@ -656,6 +657,7 @@ GroupedCharacter.propTypes = {
656
657
  "translate": PropTypes.oneOf(["no", "yes"]),
657
658
  "typeof": PropTypes.string,
658
659
  "unselectable": PropTypes.oneOf(["off", "on"]),
660
+ "validationIconId": PropTypes.string,
659
661
  "validationOnLabel": PropTypes.bool,
660
662
  "value": PropTypes.string,
661
663
  "vocab": PropTypes.string,
@@ -108,6 +108,7 @@ Number.propTypes = {
108
108
  "aria-valuemin": PropTypes.number,
109
109
  "aria-valuenow": PropTypes.number,
110
110
  "aria-valuetext": PropTypes.string,
111
+ "ariaDescribedBy": PropTypes.string,
111
112
  "as": PropTypes.elementType,
112
113
  "autoCapitalize": PropTypes.string,
113
114
  "autoComplete": PropTypes.string,
@@ -579,6 +580,7 @@ Number.propTypes = {
579
580
  "translate": PropTypes.oneOf(["no", "yes"]),
580
581
  "typeof": PropTypes.string,
581
582
  "unselectable": PropTypes.oneOf(["off", "on"]),
583
+ "validationIconId": PropTypes.string,
582
584
  "validationOnLabel": PropTypes.bool,
583
585
  "value": PropTypes.string,
584
586
  "vocab": PropTypes.string,
@@ -161,6 +161,7 @@ RadioButton.propTypes = {
161
161
  "aria-valuemin": PropTypes.number,
162
162
  "aria-valuenow": PropTypes.number,
163
163
  "aria-valuetext": PropTypes.string,
164
+ "ariaDescribedBy": PropTypes.string,
164
165
  "ariaLabelledBy": PropTypes.string,
165
166
  "autoCapitalize": PropTypes.string,
166
167
  "autoComplete": PropTypes.string,
@@ -621,6 +622,7 @@ RadioButton.propTypes = {
621
622
  "translate": PropTypes.oneOf(["no", "yes"]),
622
623
  "typeof": PropTypes.string,
623
624
  "unselectable": PropTypes.oneOf(["off", "on"]),
625
+ "validationIconId": PropTypes.string,
624
626
  "validationOnLabel": PropTypes.bool,
625
627
  "value": PropTypes.string.isRequired,
626
628
  "vocab": PropTypes.string,
@@ -169,6 +169,7 @@ Switch.propTypes = {
169
169
  "aria-valuemin": PropTypes.number,
170
170
  "aria-valuenow": PropTypes.number,
171
171
  "aria-valuetext": PropTypes.string,
172
+ "ariaDescribedBy": PropTypes.string,
172
173
  "ariaLabelledBy": PropTypes.string,
173
174
  "autoCapitalize": PropTypes.string,
174
175
  "autoComplete": PropTypes.string,
@@ -630,6 +631,7 @@ Switch.propTypes = {
630
631
  "translate": PropTypes.oneOf(["no", "yes"]),
631
632
  "typeof": PropTypes.string,
632
633
  "unselectable": PropTypes.oneOf(["off", "on"]),
634
+ "validationIconId": PropTypes.string,
633
635
  "validationOnLabel": PropTypes.bool,
634
636
  "value": PropTypes.string,
635
637
  "vocab": PropTypes.string,
@@ -103,11 +103,12 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
103
103
 
104
104
  const {
105
105
  labelId,
106
- validationIconId,
106
+ validationId,
107
107
  fieldHelpId,
108
108
  ariaDescribedBy
109
109
  } = useInputAccessibility({
110
110
  id,
111
+ validationRedesignOptIn,
111
112
  error,
112
113
  warning,
113
114
  info,
@@ -141,13 +142,12 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
141
142
  }
142
143
  };
143
144
  }, [expandable]);
144
- const hasIconInside = !!(inputIcon || validationIconId && !validationOnLabel);
145
+ const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
145
146
  const hintId = useRef(guid());
146
147
  const characterCountHintIdValue = characterCount ? characterCountHintId : undefined;
147
148
  const inputHintIdValue = inputHint ? hintId.current : undefined;
148
149
  const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
149
- const ariaDescribedByValues = [validationRedesignOptIn ? undefined : ariaDescribedBy, hintIdValue];
150
- const ariaDescribedByValue = ariaDescribedByValues.filter(Boolean).join(" ");
150
+ const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
151
151
  const input = /*#__PURE__*/React.createElement(InputPresentation, {
152
152
  size: size,
153
153
  disabled: disabled,
@@ -160,7 +160,7 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
160
160
  }, /*#__PURE__*/React.createElement(Input, _extends({
161
161
  "aria-invalid": !!error,
162
162
  "aria-labelledby": ariaLabelledBy,
163
- "aria-describedby": ariaDescribedByValue,
163
+ ariaDescribedBy: combinedAriaDescribedBy,
164
164
  autoFocus: autoFocus,
165
165
  name: name,
166
166
  value: value,
@@ -174,7 +174,8 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
174
174
  cols: cols,
175
175
  id: id,
176
176
  as: "textarea",
177
- inputRef: inputRef
177
+ inputRef: inputRef,
178
+ validationIconId: validationRedesignOptIn ? undefined : validationId
178
179
  }, rest)), children, /*#__PURE__*/React.createElement(InputIconToggle, {
179
180
  disabled: disabled,
180
181
  readOnly: readOnly,
@@ -183,7 +184,7 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
183
184
  error: error,
184
185
  warning: warning,
185
186
  info: info,
186
- validationIconId: validationRedesignOptIn ? undefined : validationIconId,
187
+ validationIconId: validationRedesignOptIn ? undefined : validationId,
187
188
  useValidationIcon: !(validationRedesignOptIn || validationOnLabel)
188
189
  }));
189
190
  const marginProps = useFormSpacing(rest);
@@ -222,6 +223,7 @@ const Textarea = /*#__PURE__*/React.forwardRef(({
222
223
  position: "relative"
223
224
  }, /*#__PURE__*/React.createElement(ValidationMessage, {
224
225
  error: error,
226
+ validationId: validationId,
225
227
  warning: warning
226
228
  }), (error || warning) && /*#__PURE__*/React.createElement(ErrorBorder, {
227
229
  warning: !!(!error && warning)
@@ -282,6 +284,7 @@ Textarea.propTypes = {
282
284
  "aria-valuemin": PropTypes.number,
283
285
  "aria-valuenow": PropTypes.number,
284
286
  "aria-valuetext": PropTypes.string,
287
+ "ariaDescribedBy": PropTypes.string,
285
288
  "as": PropTypes.elementType,
286
289
  "autoCapitalize": PropTypes.string,
287
290
  "autoComplete": PropTypes.string,
@@ -747,6 +750,7 @@ Textarea.propTypes = {
747
750
  "translate": PropTypes.oneOf(["no", "yes"]),
748
751
  "typeof": PropTypes.string,
749
752
  "unselectable": PropTypes.oneOf(["off", "on"]),
753
+ "validationIconId": PropTypes.string,
750
754
  "validationOnLabel": PropTypes.bool,
751
755
  "value": PropTypes.string,
752
756
  "vocab": PropTypes.string,
@@ -95,11 +95,12 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
95
95
 
96
96
  const {
97
97
  labelId,
98
- validationIconId,
98
+ validationId,
99
99
  fieldHelpId,
100
100
  ariaDescribedBy
101
101
  } = useInputAccessibility({
102
102
  id: uniqueId,
103
+ validationRedesignOptIn,
103
104
  error,
104
105
  warning,
105
106
  info,
@@ -110,9 +111,8 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
110
111
  const characterCountHintIdValue = characterCount ? characterCountHintId : undefined;
111
112
  const inputHintIdValue = inputHint ? hintId.current : undefined;
112
113
  const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
113
- const ariaDescribedByValues = [validationRedesignOptIn ? undefined : ariaDescribedBy, hintIdValue];
114
- const ariaDescribedByValue = ariaDescribedByValues.filter(Boolean).join(" ");
115
- const hasIconInside = !!(inputIcon || validationIconId && !validationOnLabel);
114
+ const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
115
+ const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
116
116
  const input = /*#__PURE__*/React.createElement(InputPresentation, {
117
117
  align: align,
118
118
  disabled: disabled,
@@ -134,7 +134,7 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
134
134
  align: align,
135
135
  "aria-invalid": !!error,
136
136
  "aria-labelledby": ariaLabelledBy,
137
- "aria-describedby": ariaDescribedByValue,
137
+ ariaDescribedBy: combinedAriaDescribedBy,
138
138
  autoFocus: autoFocus,
139
139
  deferTimeout: deferTimeout,
140
140
  disabled: disabled,
@@ -151,7 +151,8 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
151
151
  placeholder: disabled || readOnly ? "" : placeholder,
152
152
  readOnly: readOnly,
153
153
  value: typeof formattedValue === "string" ? formattedValue : value,
154
- maxLength: maxLength
154
+ maxLength: maxLength,
155
+ validationIconId: validationRedesignOptIn ? undefined : validationId
155
156
  }, props)), children, /*#__PURE__*/React.createElement(InputIconToggle, {
156
157
  align: align,
157
158
  disabled: disabled,
@@ -165,7 +166,7 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
165
166
  size: size,
166
167
  useValidationIcon: !(validationRedesignOptIn || validationOnLabel),
167
168
  warning: warning,
168
- validationIconId: validationRedesignOptIn ? undefined : validationIconId
169
+ validationIconId: validationRedesignOptIn ? undefined : validationId
169
170
  }));
170
171
  return /*#__PURE__*/React.createElement(TooltipProvider, {
171
172
  helpAriaLabel: helpAriaLabel,
@@ -193,7 +194,7 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
193
194
  "data-component": dataComponent,
194
195
  "data-role": dataRole,
195
196
  "data-element": dataElement,
196
- validationIconId: validationRedesignOptIn ? undefined : validationIconId,
197
+ validationIconId: validationRedesignOptIn ? undefined : validationId,
197
198
  validationRedesignOptIn: validationRedesignOptIn
198
199
  }, filterStyledSystemMarginProps(props)), characterLimit || inputHint ? /*#__PURE__*/React.createElement(StyledInputHint, {
199
200
  id: hintIdValue,
@@ -202,6 +203,7 @@ const Textbox = /*#__PURE__*/React.forwardRef(({
202
203
  position: "relative"
203
204
  }, /*#__PURE__*/React.createElement(ValidationMessage, {
204
205
  error: error,
206
+ validationId: validationId,
205
207
  warning: warning
206
208
  }), !disableErrorBorder && (error || warning) && /*#__PURE__*/React.createElement(ErrorBorder, {
207
209
  warning: !!(!error && warning)
@@ -262,6 +264,7 @@ Textbox.propTypes = {
262
264
  "aria-valuemin": PropTypes.number,
263
265
  "aria-valuenow": PropTypes.number,
264
266
  "aria-valuetext": PropTypes.string,
267
+ "ariaDescribedBy": PropTypes.string,
265
268
  "as": PropTypes.elementType,
266
269
  "autoCapitalize": PropTypes.string,
267
270
  "autoComplete": PropTypes.string,
@@ -733,6 +736,7 @@ Textbox.propTypes = {
733
736
  "translate": PropTypes.oneOf(["no", "yes"]),
734
737
  "typeof": PropTypes.string,
735
738
  "unselectable": PropTypes.oneOf(["off", "on"]),
739
+ "validationIconId": PropTypes.string,
736
740
  "validationOnLabel": PropTypes.bool,
737
741
  "value": PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.number, PropTypes.string]),
738
742
  "vocab": PropTypes.string,
@@ -1,6 +1,7 @@
1
1
  /// <reference types="react" />
2
- export default function useInputAccessibility({ id, error, warning, info, label, fieldHelp, }: {
2
+ export default function useInputAccessibility({ id, validationRedesignOptIn, error, warning, info, label, fieldHelp, }: {
3
3
  id: string;
4
+ validationRedesignOptIn?: boolean;
4
5
  error?: string | boolean;
5
6
  warning?: string | boolean;
6
7
  info?: string | boolean;
@@ -8,7 +9,7 @@ export default function useInputAccessibility({ id, error, warning, info, label,
8
9
  fieldHelp?: React.ReactNode;
9
10
  }): {
10
11
  labelId?: string;
11
- validationIconId?: string;
12
+ validationId?: string;
12
13
  fieldHelpId?: string;
13
14
  ariaDescribedBy?: string;
14
15
  };
@@ -1,5 +1,6 @@
1
1
  export default function useInputAccessibility({
2
2
  id,
3
+ validationRedesignOptIn,
3
4
  error,
4
5
  warning,
5
6
  info,
@@ -7,12 +8,18 @@ export default function useInputAccessibility({
7
8
  fieldHelp
8
9
  }) {
9
10
  const labelId = label ? `${id}-label` : undefined;
10
- const validationIconId = [error, warning, info].filter(validation => validation && typeof validation === "string").length ? `${id}-validation-icon` : undefined;
11
+ const validationId = [error, warning, info].filter(validation => validation && typeof validation === "string").length ? `${id}-validation` : undefined;
11
12
  const fieldHelpId = fieldHelp ? `${id}-field-help` : undefined;
12
- const ariaDescribedBy = [fieldHelpId, validationIconId].filter(Boolean).join(" ");
13
+ const descriptionList = fieldHelpId ? [fieldHelpId] : [];
14
+
15
+ if (validationRedesignOptIn && validationId) {
16
+ descriptionList.push(validationId);
17
+ }
18
+
19
+ const ariaDescribedBy = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
13
20
  return {
14
21
  labelId,
15
- validationIconId,
22
+ validationId,
16
23
  fieldHelpId,
17
24
  ariaDescribedBy
18
25
  };
@@ -65,7 +65,7 @@ const CheckableInput = /*#__PURE__*/_react.default.forwardRef(({
65
65
  const {
66
66
  labelId,
67
67
  fieldHelpId,
68
- validationIconId,
68
+ validationId,
69
69
  ariaDescribedBy
70
70
  } = (0, _useInputAccessibility.default)({
71
71
  id,
@@ -91,17 +91,16 @@ const CheckableInput = /*#__PURE__*/_react.default.forwardRef(({
91
91
  labelId,
92
92
  labelInline,
93
93
  labelSpacing,
94
- name: id,
95
94
  reverse,
96
95
  warning,
97
- validationIconId,
96
+ validationIconId: validationId,
98
97
  // We don't want an asterisk on each radio control, only the legend
99
98
  // However, we still want the input element to receive the required prop
100
99
  isRequired: isRadio ? undefined : required,
101
100
  useValidationIcon: validationOnLabel
102
101
  };
103
102
  const inputProps = {
104
- "aria-describedby": ariaDescribedBy,
103
+ ariaDescribedBy,
105
104
  "aria-labelledby": ariaLabelledBy,
106
105
  "aria-invalid": !!error,
107
106
  autoFocus,
@@ -116,6 +115,7 @@ const CheckableInput = /*#__PURE__*/_react.default.forwardRef(({
116
115
  onFocus,
117
116
  required,
118
117
  ref,
118
+ validationIconId: validationId,
119
119
  ...props
120
120
  };
121
121
  return /*#__PURE__*/_react.default.createElement(_checkableInput.StyledCheckableInputWrapper, {
@@ -183,6 +183,7 @@ CheckableInput.propTypes = {
183
183
  "aria-valuemin": _propTypes.default.number,
184
184
  "aria-valuenow": _propTypes.default.number,
185
185
  "aria-valuetext": _propTypes.default.string,
186
+ "ariaDescribedBy": _propTypes.default.string,
186
187
  "ariaLabelledBy": _propTypes.default.string,
187
188
  "autoCapitalize": _propTypes.default.string,
188
189
  "autoComplete": _propTypes.default.string,
@@ -482,6 +483,7 @@ CheckableInput.propTypes = {
482
483
  "type": _propTypes.default.string.isRequired,
483
484
  "typeof": _propTypes.default.string,
484
485
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
486
+ "validationIconId": _propTypes.default.string,
485
487
  "validationOnLabel": _propTypes.default.bool,
486
488
  "value": _propTypes.default.string,
487
489
  "vocab": _propTypes.default.string,
@@ -1,5 +1,7 @@
1
1
  import React from "react";
2
2
  export interface CommonHiddenCheckableInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "size" | "type"> {
3
+ /** The id of the element that describe the input. */
4
+ ariaDescribedBy?: string;
3
5
  /** Prop to specify the aria-labelledby attribute of the input */
4
6
  ariaLabelledBy?: string;
5
7
  /** If true the Component will be focused when page load */
@@ -18,6 +20,8 @@ export interface CommonHiddenCheckableInputProps extends Omit<React.InputHTMLAtt
18
20
  onMouseLeave?: (ev: React.MouseEvent<HTMLInputElement>) => void;
19
21
  /** OnMouseEnter event handler */
20
22
  onMouseEnter?: (ev: React.MouseEvent<HTMLInputElement>) => void;
23
+ /** Id of the validation icon */
24
+ validationIconId?: string;
21
25
  /** Value of the input */
22
26
  value?: string;
23
27
  }
@@ -22,6 +22,7 @@ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj;
22
22
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
23
23
 
24
24
  const HiddenCheckableInput = /*#__PURE__*/_react.default.forwardRef(({
25
+ ariaDescribedBy,
25
26
  ariaLabelledBy,
26
27
  name,
27
28
  checked,
@@ -30,19 +31,24 @@ const HiddenCheckableInput = /*#__PURE__*/_react.default.forwardRef(({
30
31
  onChange,
31
32
  autoFocus,
32
33
  role,
34
+ validationIconId,
33
35
  ...props
34
36
  }, ref) => {
35
37
  const {
36
38
  onBlur,
37
39
  onFocus,
38
40
  onMouseEnter,
39
- onMouseLeave
41
+ onMouseLeave,
42
+ hasFocus,
43
+ hasMouseOver
40
44
  } = (0, _react.useContext)(_inputBehaviour.InputContext);
41
45
  const {
42
46
  onBlur: onBlurGroup,
43
47
  onFocus: onFocusGroup,
44
48
  onMouseEnter: onMouseEnterGroup,
45
- onMouseLeave: onMouseLeaveGroup
49
+ onMouseLeave: onMouseLeaveGroup,
50
+ hasFocus: hasGroupFocus,
51
+ hasMouseOver: hasGroupMouseOver
46
52
  } = (0, _react.useContext)(_inputBehaviour.InputGroupContext);
47
53
 
48
54
  const handleFocus = ev => {
@@ -69,7 +75,16 @@ const HiddenCheckableInput = /*#__PURE__*/_react.default.forwardRef(({
69
75
  if (onMouseLeaveGroup) onMouseLeaveGroup();
70
76
  };
71
77
 
78
+ const hasValidationPart = (hasFocus || hasGroupFocus || hasMouseOver || hasGroupMouseOver) && validationIconId;
79
+ const descriptionList = ariaDescribedBy ? [ariaDescribedBy] : [];
80
+
81
+ if (hasValidationPart) {
82
+ descriptionList.push(validationIconId);
83
+ }
84
+
85
+ const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
72
86
  return /*#__PURE__*/_react.default.createElement(_hiddenCheckableInput.default, _extends({
87
+ "aria-describedby": combinedDescription,
73
88
  "aria-labelledby": ariaLabelledBy,
74
89
  autoFocus: autoFocus,
75
90
  "aria-checked": checked,
@@ -141,6 +156,7 @@ HiddenCheckableInput.propTypes = {
141
156
  "aria-valuemin": _propTypes.default.number,
142
157
  "aria-valuenow": _propTypes.default.number,
143
158
  "aria-valuetext": _propTypes.default.string,
159
+ "ariaDescribedBy": _propTypes.default.string,
144
160
  "ariaLabelledBy": _propTypes.default.string,
145
161
  "autoCapitalize": _propTypes.default.string,
146
162
  "autoComplete": _propTypes.default.string,
@@ -377,6 +393,7 @@ HiddenCheckableInput.propTypes = {
377
393
  "type": _propTypes.default.string.isRequired,
378
394
  "typeof": _propTypes.default.string,
379
395
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
396
+ "validationIconId": _propTypes.default.string,
380
397
  "value": _propTypes.default.string,
381
398
  "vocab": _propTypes.default.string,
382
399
  "width": _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
2
  export interface CommonInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
3
3
  align?: "right" | "left";
4
+ /** The id of the element that describe the input. */
5
+ ariaDescribedBy?: string;
4
6
  /** Override the variant component */
5
7
  as?: React.ElementType;
6
8
  /** If true the Component will be focused when rendered */
@@ -29,6 +31,8 @@ export interface CommonInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
29
31
  readOnly?: boolean;
30
32
  /** Flag to configure component as mandatory */
31
33
  required?: boolean;
34
+ /** Id of the validation icon */
35
+ validationIconId?: string;
32
36
  }
33
37
  export interface InputProps extends CommonInputProps {
34
38
  /** The visible width of the text control, in average character widths */
@@ -46,6 +46,7 @@ function selectTextOnFocus(input) {
46
46
  const Input = /*#__PURE__*/_react.default.forwardRef(({
47
47
  align,
48
48
  "aria-labelledby": ariaLabelledBy,
49
+ ariaDescribedBy,
49
50
  placeholder,
50
51
  disabled,
51
52
  readOnly,
@@ -61,6 +62,7 @@ const Input = /*#__PURE__*/_react.default.forwardRef(({
61
62
  type = "text",
62
63
  id,
63
64
  name,
65
+ validationIconId,
64
66
  ...rest
65
67
  }, ref) => {
66
68
  const context = (0, _react.useContext)(_inputBehaviour.InputContext);
@@ -161,7 +163,16 @@ const Input = /*#__PURE__*/_react.default.forwardRef(({
161
163
  handleDeferred(ev);
162
164
  };
163
165
 
166
+ const hasValidationPart = (context.hasFocus || groupContext.hasFocus || context.hasMouseOver || groupContext.hasMouseOver) && validationIconId;
167
+ const descriptionList = ariaDescribedBy ? [ariaDescribedBy] : [];
168
+
169
+ if (hasValidationPart) {
170
+ descriptionList.push(validationIconId);
171
+ }
172
+
173
+ const combinedDescription = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
164
174
  return /*#__PURE__*/_react.default.createElement(_input2.default, _extends({}, rest, {
175
+ "aria-describedby": combinedDescription,
165
176
  "aria-labelledby": ariaLabelledBy,
166
177
  align: align,
167
178
  placeholder: placeholder,
@@ -233,6 +244,7 @@ Input.propTypes = {
233
244
  "aria-valuemin": _propTypes.default.number,
234
245
  "aria-valuenow": _propTypes.default.number,
235
246
  "aria-valuetext": _propTypes.default.string,
247
+ "ariaDescribedBy": _propTypes.default.string,
236
248
  "as": _propTypes.default.elementType,
237
249
  "autoCapitalize": _propTypes.default.string,
238
250
  "autoComplete": _propTypes.default.string,
@@ -525,6 +537,7 @@ Input.propTypes = {
525
537
  "type": _propTypes.default.string,
526
538
  "typeof": _propTypes.default.string,
527
539
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
540
+ "validationIconId": _propTypes.default.string,
528
541
  "value": _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.string), _propTypes.default.number, _propTypes.default.string]),
529
542
  "vocab": _propTypes.default.string,
530
543
  "width": _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string])
@@ -60,7 +60,7 @@ const InputIconToggle = ({
60
60
  onBlur: onBlur,
61
61
  isPartOfInput: true,
62
62
  tabIndex: iconTabIndex,
63
- iconId: validationIconId,
63
+ tooltipId: validationIconId,
64
64
  tooltipPosition: align === "right" ? "left" : "right"
65
65
  }));
66
66
  }
@@ -104,8 +104,7 @@ const Label = ({
104
104
  inline
105
105
  });
106
106
  return /*#__PURE__*/_react.default.createElement(_iconWrapper.default, null, /*#__PURE__*/_react.default.createElement(_validationIcon.default, {
107
- iconId: validationIconId,
108
- tooltipId: tooltipId,
107
+ tooltipId: validationIconId,
109
108
  error: error,
110
109
  warning: warning,
111
110
  info: info,
@@ -3,9 +3,11 @@ export interface ValidationMessageProps {
3
3
  /** Indicate that error has occurred
4
4
  Pass string to display hint with error */
5
5
  error?: boolean | string;
6
+ /** Id of the component, to be used for accessibility purposes */
7
+ validationId?: string;
6
8
  /** Indicate that warning has occurred
7
9
  Pass string to display hint with warning */
8
10
  warning?: boolean | string;
9
11
  }
10
- declare const ValidationMessage: ({ error, warning }: ValidationMessageProps) => JSX.Element | null;
12
+ declare const ValidationMessage: ({ error, validationId, warning, }: ValidationMessageProps) => JSX.Element | null;
11
13
  export default ValidationMessage;
@@ -15,17 +15,20 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
15
15
 
16
16
  const ValidationMessage = ({
17
17
  error,
18
+ validationId,
18
19
  warning
19
20
  }) => {
20
21
  const validation = error || warning;
21
22
  const isStringValidation = typeof validation === "string";
22
23
  return isStringValidation ? /*#__PURE__*/_react.default.createElement(_validationMessage.default, {
24
+ id: validationId,
23
25
  isWarning: !!(!error && warning)
24
26
  }, validation) : null;
25
27
  };
26
28
 
27
29
  ValidationMessage.propTypes = {
28
30
  "error": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool]),
31
+ "validationId": _propTypes.default.string,
29
32
  "warning": _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.bool])
30
33
  };
31
34
  var _default = ValidationMessage;
@@ -11,6 +11,8 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
11
11
 
12
12
  var _invariant = _interopRequireDefault(require("invariant"));
13
13
 
14
+ var _guid = _interopRequireDefault(require("../utils/helpers/guid"));
15
+
14
16
  var _icon = _interopRequireDefault(require("../../components/icon"));
15
17
 
16
18
  var _validationIcon = _interopRequireDefault(require("./validation-icon.style"));
@@ -54,6 +56,7 @@ const ValidationIcon = ({
54
56
  tooltipFlipOverrides,
55
57
  ...rest
56
58
  }) => {
59
+ const validationTooltipId = (0, _react.useRef)(tooltipId || (0, _guid.default)());
57
60
  const flipBehaviourCheck = Array.isArray(tooltipFlipOverrides) && tooltipFlipOverrides.every(override => ["bottom", "left", "right", "top"].includes(override));
58
61
 
59
62
  if (tooltipFlipOverrides) {
@@ -95,10 +98,11 @@ const ValidationIcon = ({
95
98
  },
96
99
  isPartOfInput: isPartOfInput
97
100
  }, (0, _utils.filterStyledSystemMarginProps)(rest)), /*#__PURE__*/_react.default.createElement(_icon.default, {
101
+ "aria-describedby": validationTooltipId.current,
98
102
  key: `${validationType}-icon`,
99
103
  type: validationType,
100
104
  tabIndex: tabIndex,
101
- tooltipId: tooltipId,
105
+ tooltipId: validationTooltipId.current,
102
106
  tooltipMessage: validationMessage,
103
107
  tooltipPosition: tooltipPosition,
104
108
  tooltipVisible: hasFocus || hasMouseOver || groupHasFocus || groupHasMouseOver || triggeredByIcon,
@@ -106,7 +110,6 @@ const ValidationIcon = ({
106
110
  isPartOfInput: isPartOfInput,
107
111
  inputSize: size,
108
112
  id: iconId,
109
- ariaLabel: validationMessage,
110
113
  focusable: tabIndex !== -1
111
114
  }));
112
115
  };
@@ -192,6 +192,7 @@ Checkbox.propTypes = {
192
192
  "aria-valuemin": _propTypes.default.number,
193
193
  "aria-valuenow": _propTypes.default.number,
194
194
  "aria-valuetext": _propTypes.default.string,
195
+ "ariaDescribedBy": _propTypes.default.string,
195
196
  "ariaLabelledBy": _propTypes.default.string,
196
197
  "autoCapitalize": _propTypes.default.string,
197
198
  "autoComplete": _propTypes.default.string,
@@ -652,6 +653,7 @@ Checkbox.propTypes = {
652
653
  "translate": _propTypes.default.oneOf(["no", "yes"]),
653
654
  "typeof": _propTypes.default.string,
654
655
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
656
+ "validationIconId": _propTypes.default.string,
655
657
  "validationOnLabel": _propTypes.default.bool,
656
658
  "value": _propTypes.default.string,
657
659
  "vocab": _propTypes.default.string,
@@ -292,6 +292,7 @@ Decimal.propTypes = {
292
292
  "aria-valuemin": _propTypes.default.number,
293
293
  "aria-valuenow": _propTypes.default.number,
294
294
  "aria-valuetext": _propTypes.default.string,
295
+ "ariaDescribedBy": _propTypes.default.string,
295
296
  "as": _propTypes.default.elementType,
296
297
  "autoCapitalize": _propTypes.default.string,
297
298
  "autoComplete": _propTypes.default.string,
@@ -761,6 +762,7 @@ Decimal.propTypes = {
761
762
  "translate": _propTypes.default.oneOf(["no", "yes"]),
762
763
  "typeof": _propTypes.default.string,
763
764
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
765
+ "validationIconId": _propTypes.default.string,
764
766
  "validationOnLabel": _propTypes.default.bool,
765
767
  "value": _propTypes.default.string,
766
768
  "vocab": _propTypes.default.string,
@@ -204,6 +204,7 @@ GroupedCharacter.propTypes = {
204
204
  "aria-valuemin": _propTypes.default.number,
205
205
  "aria-valuenow": _propTypes.default.number,
206
206
  "aria-valuetext": _propTypes.default.string,
207
+ "ariaDescribedBy": _propTypes.default.string,
207
208
  "as": _propTypes.default.elementType,
208
209
  "autoCapitalize": _propTypes.default.string,
209
210
  "autoComplete": _propTypes.default.string,
@@ -677,6 +678,7 @@ GroupedCharacter.propTypes = {
677
678
  "translate": _propTypes.default.oneOf(["no", "yes"]),
678
679
  "typeof": _propTypes.default.string,
679
680
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
681
+ "validationIconId": _propTypes.default.string,
680
682
  "validationOnLabel": _propTypes.default.bool,
681
683
  "value": _propTypes.default.string,
682
684
  "vocab": _propTypes.default.string,
@@ -128,6 +128,7 @@ Number.propTypes = {
128
128
  "aria-valuemin": _propTypes.default.number,
129
129
  "aria-valuenow": _propTypes.default.number,
130
130
  "aria-valuetext": _propTypes.default.string,
131
+ "ariaDescribedBy": _propTypes.default.string,
131
132
  "as": _propTypes.default.elementType,
132
133
  "autoCapitalize": _propTypes.default.string,
133
134
  "autoComplete": _propTypes.default.string,
@@ -599,6 +600,7 @@ Number.propTypes = {
599
600
  "translate": _propTypes.default.oneOf(["no", "yes"]),
600
601
  "typeof": _propTypes.default.string,
601
602
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
603
+ "validationIconId": _propTypes.default.string,
602
604
  "validationOnLabel": _propTypes.default.bool,
603
605
  "value": _propTypes.default.string,
604
606
  "vocab": _propTypes.default.string,
@@ -187,6 +187,7 @@ RadioButton.propTypes = {
187
187
  "aria-valuemin": _propTypes.default.number,
188
188
  "aria-valuenow": _propTypes.default.number,
189
189
  "aria-valuetext": _propTypes.default.string,
190
+ "ariaDescribedBy": _propTypes.default.string,
190
191
  "ariaLabelledBy": _propTypes.default.string,
191
192
  "autoCapitalize": _propTypes.default.string,
192
193
  "autoComplete": _propTypes.default.string,
@@ -647,6 +648,7 @@ RadioButton.propTypes = {
647
648
  "translate": _propTypes.default.oneOf(["no", "yes"]),
648
649
  "typeof": _propTypes.default.string,
649
650
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
651
+ "validationIconId": _propTypes.default.string,
650
652
  "validationOnLabel": _propTypes.default.bool,
651
653
  "value": _propTypes.default.string.isRequired,
652
654
  "vocab": _propTypes.default.string,
@@ -195,6 +195,7 @@ Switch.propTypes = {
195
195
  "aria-valuemin": _propTypes.default.number,
196
196
  "aria-valuenow": _propTypes.default.number,
197
197
  "aria-valuetext": _propTypes.default.string,
198
+ "ariaDescribedBy": _propTypes.default.string,
198
199
  "ariaLabelledBy": _propTypes.default.string,
199
200
  "autoCapitalize": _propTypes.default.string,
200
201
  "autoComplete": _propTypes.default.string,
@@ -656,6 +657,7 @@ Switch.propTypes = {
656
657
  "translate": _propTypes.default.oneOf(["no", "yes"]),
657
658
  "typeof": _propTypes.default.string,
658
659
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
660
+ "validationIconId": _propTypes.default.string,
659
661
  "validationOnLabel": _propTypes.default.bool,
660
662
  "value": _propTypes.default.string,
661
663
  "vocab": _propTypes.default.string,
@@ -136,11 +136,12 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
136
136
 
137
137
  const {
138
138
  labelId,
139
- validationIconId,
139
+ validationId,
140
140
  fieldHelpId,
141
141
  ariaDescribedBy
142
142
  } = (0, _useInputAccessibility.default)({
143
143
  id,
144
+ validationRedesignOptIn,
144
145
  error,
145
146
  warning,
146
147
  info,
@@ -174,13 +175,12 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
174
175
  }
175
176
  };
176
177
  }, [expandable]);
177
- const hasIconInside = !!(inputIcon || validationIconId && !validationOnLabel);
178
+ const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
178
179
  const hintId = (0, _react.useRef)((0, _guid.default)());
179
180
  const characterCountHintIdValue = characterCount ? characterCountHintId : undefined;
180
181
  const inputHintIdValue = inputHint ? hintId.current : undefined;
181
182
  const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
182
- const ariaDescribedByValues = [validationRedesignOptIn ? undefined : ariaDescribedBy, hintIdValue];
183
- const ariaDescribedByValue = ariaDescribedByValues.filter(Boolean).join(" ");
183
+ const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
184
184
 
185
185
  const input = /*#__PURE__*/_react.default.createElement(_input.InputPresentation, {
186
186
  size: size,
@@ -194,7 +194,7 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
194
194
  }, /*#__PURE__*/_react.default.createElement(_input2.default, _extends({
195
195
  "aria-invalid": !!error,
196
196
  "aria-labelledby": ariaLabelledBy,
197
- "aria-describedby": ariaDescribedByValue,
197
+ ariaDescribedBy: combinedAriaDescribedBy,
198
198
  autoFocus: autoFocus,
199
199
  name: name,
200
200
  value: value,
@@ -208,7 +208,8 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
208
208
  cols: cols,
209
209
  id: id,
210
210
  as: "textarea",
211
- inputRef: inputRef
211
+ inputRef: inputRef,
212
+ validationIconId: validationRedesignOptIn ? undefined : validationId
212
213
  }, rest)), children, /*#__PURE__*/_react.default.createElement(_inputIconToggle.default, {
213
214
  disabled: disabled,
214
215
  readOnly: readOnly,
@@ -217,7 +218,7 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
217
218
  error: error,
218
219
  warning: warning,
219
220
  info: info,
220
- validationIconId: validationRedesignOptIn ? undefined : validationIconId,
221
+ validationIconId: validationRedesignOptIn ? undefined : validationId,
221
222
  useValidationIcon: !(validationRedesignOptIn || validationOnLabel)
222
223
  }));
223
224
 
@@ -257,6 +258,7 @@ const Textarea = /*#__PURE__*/_react.default.forwardRef(({
257
258
  position: "relative"
258
259
  }, /*#__PURE__*/_react.default.createElement(_validationMessage.default, {
259
260
  error: error,
261
+ validationId: validationId,
260
262
  warning: warning
261
263
  }), (error || warning) && /*#__PURE__*/_react.default.createElement(_textbox.ErrorBorder, {
262
264
  warning: !!(!error && warning)
@@ -319,6 +321,7 @@ Textarea.propTypes = {
319
321
  "aria-valuemin": _propTypes.default.number,
320
322
  "aria-valuenow": _propTypes.default.number,
321
323
  "aria-valuetext": _propTypes.default.string,
324
+ "ariaDescribedBy": _propTypes.default.string,
322
325
  "as": _propTypes.default.elementType,
323
326
  "autoCapitalize": _propTypes.default.string,
324
327
  "autoComplete": _propTypes.default.string,
@@ -784,6 +787,7 @@ Textarea.propTypes = {
784
787
  "translate": _propTypes.default.oneOf(["no", "yes"]),
785
788
  "typeof": _propTypes.default.string,
786
789
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
790
+ "validationIconId": _propTypes.default.string,
787
791
  "validationOnLabel": _propTypes.default.bool,
788
792
  "value": _propTypes.default.string,
789
793
  "vocab": _propTypes.default.string,
@@ -129,11 +129,12 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
129
129
 
130
130
  const {
131
131
  labelId,
132
- validationIconId,
132
+ validationId,
133
133
  fieldHelpId,
134
134
  ariaDescribedBy
135
135
  } = (0, _useInputAccessibility.default)({
136
136
  id: uniqueId,
137
+ validationRedesignOptIn,
137
138
  error,
138
139
  warning,
139
140
  info,
@@ -144,9 +145,8 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
144
145
  const characterCountHintIdValue = characterCount ? characterCountHintId : undefined;
145
146
  const inputHintIdValue = inputHint ? hintId.current : undefined;
146
147
  const hintIdValue = characterLimit ? characterCountHintIdValue : inputHintIdValue;
147
- const ariaDescribedByValues = [validationRedesignOptIn ? undefined : ariaDescribedBy, hintIdValue];
148
- const ariaDescribedByValue = ariaDescribedByValues.filter(Boolean).join(" ");
149
- const hasIconInside = !!(inputIcon || validationIconId && !validationOnLabel);
148
+ const combinedAriaDescribedBy = [ariaDescribedBy, hintIdValue].filter(Boolean).join(" ");
149
+ const hasIconInside = !!(inputIcon || validationId && !validationOnLabel);
150
150
 
151
151
  const input = /*#__PURE__*/_react.default.createElement(_input.InputPresentation, {
152
152
  align: align,
@@ -169,7 +169,7 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
169
169
  align: align,
170
170
  "aria-invalid": !!error,
171
171
  "aria-labelledby": ariaLabelledBy,
172
- "aria-describedby": ariaDescribedByValue,
172
+ ariaDescribedBy: combinedAriaDescribedBy,
173
173
  autoFocus: autoFocus,
174
174
  deferTimeout: deferTimeout,
175
175
  disabled: disabled,
@@ -186,7 +186,8 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
186
186
  placeholder: disabled || readOnly ? "" : placeholder,
187
187
  readOnly: readOnly,
188
188
  value: typeof formattedValue === "string" ? formattedValue : value,
189
- maxLength: maxLength
189
+ maxLength: maxLength,
190
+ validationIconId: validationRedesignOptIn ? undefined : validationId
190
191
  }, props)), children, /*#__PURE__*/_react.default.createElement(_inputIconToggle.default, {
191
192
  align: align,
192
193
  disabled: disabled,
@@ -200,7 +201,7 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
200
201
  size: size,
201
202
  useValidationIcon: !(validationRedesignOptIn || validationOnLabel),
202
203
  warning: warning,
203
- validationIconId: validationRedesignOptIn ? undefined : validationIconId
204
+ validationIconId: validationRedesignOptIn ? undefined : validationId
204
205
  }));
205
206
 
206
207
  return /*#__PURE__*/_react.default.createElement(_tooltipProvider.TooltipProvider, {
@@ -229,7 +230,7 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
229
230
  "data-component": dataComponent,
230
231
  "data-role": dataRole,
231
232
  "data-element": dataElement,
232
- validationIconId: validationRedesignOptIn ? undefined : validationIconId,
233
+ validationIconId: validationRedesignOptIn ? undefined : validationId,
233
234
  validationRedesignOptIn: validationRedesignOptIn
234
235
  }, (0, _utils.filterStyledSystemMarginProps)(props)), characterLimit || inputHint ? /*#__PURE__*/_react.default.createElement(_textbox.StyledInputHint, {
235
236
  id: hintIdValue,
@@ -238,6 +239,7 @@ const Textbox = /*#__PURE__*/_react.default.forwardRef(({
238
239
  position: "relative"
239
240
  }, /*#__PURE__*/_react.default.createElement(_validationMessage.default, {
240
241
  error: error,
242
+ validationId: validationId,
241
243
  warning: warning
242
244
  }), !disableErrorBorder && (error || warning) && /*#__PURE__*/_react.default.createElement(_textbox.ErrorBorder, {
243
245
  warning: !!(!error && warning)
@@ -300,6 +302,7 @@ Textbox.propTypes = {
300
302
  "aria-valuemin": _propTypes.default.number,
301
303
  "aria-valuenow": _propTypes.default.number,
302
304
  "aria-valuetext": _propTypes.default.string,
305
+ "ariaDescribedBy": _propTypes.default.string,
303
306
  "as": _propTypes.default.elementType,
304
307
  "autoCapitalize": _propTypes.default.string,
305
308
  "autoComplete": _propTypes.default.string,
@@ -771,6 +774,7 @@ Textbox.propTypes = {
771
774
  "translate": _propTypes.default.oneOf(["no", "yes"]),
772
775
  "typeof": _propTypes.default.string,
773
776
  "unselectable": _propTypes.default.oneOf(["off", "on"]),
777
+ "validationIconId": _propTypes.default.string,
774
778
  "validationOnLabel": _propTypes.default.bool,
775
779
  "value": _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.string), _propTypes.default.number, _propTypes.default.string]),
776
780
  "vocab": _propTypes.default.string,
@@ -1,6 +1,7 @@
1
1
  /// <reference types="react" />
2
- export default function useInputAccessibility({ id, error, warning, info, label, fieldHelp, }: {
2
+ export default function useInputAccessibility({ id, validationRedesignOptIn, error, warning, info, label, fieldHelp, }: {
3
3
  id: string;
4
+ validationRedesignOptIn?: boolean;
4
5
  error?: string | boolean;
5
6
  warning?: string | boolean;
6
7
  info?: string | boolean;
@@ -8,7 +9,7 @@ export default function useInputAccessibility({ id, error, warning, info, label,
8
9
  fieldHelp?: React.ReactNode;
9
10
  }): {
10
11
  labelId?: string;
11
- validationIconId?: string;
12
+ validationId?: string;
12
13
  fieldHelpId?: string;
13
14
  ariaDescribedBy?: string;
14
15
  };
@@ -7,6 +7,7 @@ exports.default = useInputAccessibility;
7
7
 
8
8
  function useInputAccessibility({
9
9
  id,
10
+ validationRedesignOptIn,
10
11
  error,
11
12
  warning,
12
13
  info,
@@ -14,12 +15,18 @@ function useInputAccessibility({
14
15
  fieldHelp
15
16
  }) {
16
17
  const labelId = label ? `${id}-label` : undefined;
17
- const validationIconId = [error, warning, info].filter(validation => validation && typeof validation === "string").length ? `${id}-validation-icon` : undefined;
18
+ const validationId = [error, warning, info].filter(validation => validation && typeof validation === "string").length ? `${id}-validation` : undefined;
18
19
  const fieldHelpId = fieldHelp ? `${id}-field-help` : undefined;
19
- const ariaDescribedBy = [fieldHelpId, validationIconId].filter(Boolean).join(" ");
20
+ const descriptionList = fieldHelpId ? [fieldHelpId] : [];
21
+
22
+ if (validationRedesignOptIn && validationId) {
23
+ descriptionList.push(validationId);
24
+ }
25
+
26
+ const ariaDescribedBy = descriptionList.length ? descriptionList.filter(Boolean).join(" ") : undefined;
20
27
  return {
21
28
  labelId,
22
- validationIconId,
29
+ validationId,
23
30
  fieldHelpId,
24
31
  ariaDescribedBy
25
32
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carbon-react",
3
- "version": "117.1.1",
3
+ "version": "117.1.2",
4
4
  "description": "A library of reusable React components for easily building user interfaces.",
5
5
  "files": [
6
6
  "lib",