@patternfly/react-core 5.2.0-prerelease.42 → 5.2.0-prerelease.44

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 (149) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/AboutModal/package.json +1 -1
  5. package/dist/dynamic/components/Accordion/package.json +1 -1
  6. package/dist/dynamic/components/ActionList/package.json +1 -1
  7. package/dist/dynamic/components/Alert/package.json +1 -1
  8. package/dist/dynamic/components/Avatar/package.json +1 -1
  9. package/dist/dynamic/components/BackToTop/package.json +1 -1
  10. package/dist/dynamic/components/Backdrop/package.json +1 -1
  11. package/dist/dynamic/components/BackgroundImage/package.json +1 -1
  12. package/dist/dynamic/components/Badge/package.json +1 -1
  13. package/dist/dynamic/components/Banner/package.json +1 -1
  14. package/dist/dynamic/components/Brand/package.json +1 -1
  15. package/dist/dynamic/components/Breadcrumb/package.json +1 -1
  16. package/dist/dynamic/components/Button/package.json +1 -1
  17. package/dist/dynamic/components/CalendarMonth/package.json +1 -1
  18. package/dist/dynamic/components/Card/package.json +1 -1
  19. package/dist/dynamic/components/Checkbox/package.json +1 -1
  20. package/dist/dynamic/components/Chip/package.json +1 -1
  21. package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
  22. package/dist/dynamic/components/CodeBlock/package.json +1 -1
  23. package/dist/dynamic/components/DataList/package.json +1 -1
  24. package/dist/dynamic/components/DatePicker/package.json +1 -1
  25. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  26. package/dist/dynamic/components/Divider/package.json +1 -1
  27. package/dist/dynamic/components/DragDrop/package.json +1 -1
  28. package/dist/dynamic/components/Drawer/package.json +1 -1
  29. package/dist/dynamic/components/Dropdown/package.json +1 -1
  30. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  31. package/dist/dynamic/components/EmptyState/package.json +1 -1
  32. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  33. package/dist/dynamic/components/FileUpload/package.json +1 -1
  34. package/dist/dynamic/components/Form/package.json +1 -1
  35. package/dist/dynamic/components/FormSelect/package.json +1 -1
  36. package/dist/dynamic/components/HelperText/package.json +1 -1
  37. package/dist/dynamic/components/Hint/package.json +1 -1
  38. package/dist/dynamic/components/Icon/package.json +1 -1
  39. package/dist/dynamic/components/InputGroup/package.json +1 -1
  40. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  41. package/dist/dynamic/components/Label/package.json +1 -1
  42. package/dist/dynamic/components/List/package.json +1 -1
  43. package/dist/dynamic/components/LoginPage/package.json +1 -1
  44. package/dist/dynamic/components/Masthead/package.json +1 -1
  45. package/dist/dynamic/components/Menu/package.json +1 -1
  46. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  47. package/dist/dynamic/components/Modal/package.json +1 -1
  48. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  49. package/dist/dynamic/components/Nav/package.json +1 -1
  50. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  51. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  52. package/dist/dynamic/components/NumberInput/package.json +1 -1
  53. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  54. package/dist/dynamic/components/Page/package.json +1 -1
  55. package/dist/dynamic/components/Pagination/package.json +1 -1
  56. package/dist/dynamic/components/Panel/package.json +1 -1
  57. package/dist/dynamic/components/Popover/package.json +1 -1
  58. package/dist/dynamic/components/Progress/package.json +1 -1
  59. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  60. package/dist/dynamic/components/Radio/package.json +1 -1
  61. package/dist/dynamic/components/SearchInput/package.json +1 -1
  62. package/dist/dynamic/components/Select/package.json +1 -1
  63. package/dist/dynamic/components/Sidebar/package.json +1 -1
  64. package/dist/dynamic/components/SimpleList/package.json +1 -1
  65. package/dist/dynamic/components/Skeleton/package.json +1 -1
  66. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  67. package/dist/dynamic/components/Slider/package.json +1 -1
  68. package/dist/dynamic/components/Spinner/package.json +1 -1
  69. package/dist/dynamic/components/Switch/package.json +1 -1
  70. package/dist/dynamic/components/Tabs/package.json +1 -1
  71. package/dist/dynamic/components/Text/package.json +1 -1
  72. package/dist/dynamic/components/TextArea/package.json +1 -1
  73. package/dist/dynamic/components/TextInput/package.json +1 -1
  74. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  75. package/dist/dynamic/components/Tile/package.json +1 -1
  76. package/dist/dynamic/components/TimePicker/package.json +1 -1
  77. package/dist/dynamic/components/Timestamp/package.json +1 -1
  78. package/dist/dynamic/components/Title/package.json +1 -1
  79. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  80. package/dist/dynamic/components/Toolbar/package.json +1 -1
  81. package/dist/dynamic/components/Tooltip/package.json +1 -1
  82. package/dist/dynamic/components/TreeView/package.json +1 -1
  83. package/dist/dynamic/components/Truncate/package.json +1 -1
  84. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  85. package/dist/dynamic/components/Wizard/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/ApplicationLauncher/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/ContextSelector/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Dropdown/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/OptionsMenu/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/PageHeader/package.json +1 -1
  91. package/dist/dynamic/deprecated/components/Select/package.json +1 -1
  92. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  93. package/dist/dynamic/deprecated/components/package.json +1 -1
  94. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  95. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  96. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  97. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  98. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  99. package/dist/dynamic/helpers/constants/package.json +1 -1
  100. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  101. package/dist/dynamic/helpers/favorites/package.json +1 -1
  102. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  103. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  104. package/dist/dynamic/helpers/package.json +1 -1
  105. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  106. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  107. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  108. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  109. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  110. package/dist/dynamic/helpers/util/package.json +1 -1
  111. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  112. package/dist/dynamic/layouts/Flex/package.json +1 -1
  113. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  114. package/dist/dynamic/layouts/Grid/package.json +1 -1
  115. package/dist/dynamic/layouts/Level/package.json +1 -1
  116. package/dist/dynamic/layouts/Split/package.json +1 -1
  117. package/dist/dynamic/layouts/Stack/package.json +1 -1
  118. package/dist/dynamic/next/components/Modal/package.json +1 -1
  119. package/dist/dynamic/next/components/package.json +1 -1
  120. package/dist/esm/components/Checkbox/Checkbox.d.ts +9 -3
  121. package/dist/esm/components/Checkbox/Checkbox.d.ts.map +1 -1
  122. package/dist/esm/components/Checkbox/Checkbox.js +16 -8
  123. package/dist/esm/components/Checkbox/Checkbox.js.map +1 -1
  124. package/dist/esm/components/Popover/Popover.js +4 -4
  125. package/dist/esm/components/Popover/Popover.js.map +1 -1
  126. package/dist/esm/components/Radio/Radio.d.ts +7 -5
  127. package/dist/esm/components/Radio/Radio.d.ts.map +1 -1
  128. package/dist/esm/components/Radio/Radio.js +13 -20
  129. package/dist/esm/components/Radio/Radio.js.map +1 -1
  130. package/dist/js/components/Checkbox/Checkbox.d.ts +9 -3
  131. package/dist/js/components/Checkbox/Checkbox.d.ts.map +1 -1
  132. package/dist/js/components/Checkbox/Checkbox.js +16 -8
  133. package/dist/js/components/Checkbox/Checkbox.js.map +1 -1
  134. package/dist/js/components/Popover/Popover.js +4 -4
  135. package/dist/js/components/Popover/Popover.js.map +1 -1
  136. package/dist/js/components/Radio/Radio.d.ts +7 -5
  137. package/dist/js/components/Radio/Radio.d.ts.map +1 -1
  138. package/dist/js/components/Radio/Radio.js +13 -20
  139. package/dist/js/components/Radio/Radio.js.map +1 -1
  140. package/dist/umd/react-core.min.js +1 -1
  141. package/helpers/package.json +1 -1
  142. package/layouts/package.json +1 -1
  143. package/next/package.json +1 -1
  144. package/package.json +2 -2
  145. package/src/components/Checkbox/Checkbox.tsx +63 -29
  146. package/src/components/Checkbox/__tests__/Checkbox.test.tsx +41 -0
  147. package/src/components/Popover/Popover.tsx +4 -4
  148. package/src/components/Radio/Radio.tsx +39 -38
  149. package/src/components/Radio/__tests__/Radio.test.tsx +41 -0
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"5.2.0-prerelease.41","private":true}
1
+ {"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"5.2.0-prerelease.43","private":true}
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"5.2.0-prerelease.41","private":true}
1
+ {"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"5.2.0-prerelease.43","private":true}
package/next/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"5.2.0-prerelease.41","private":true}
1
+ {"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"5.2.0-prerelease.43","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "5.2.0-prerelease.42",
3
+ "version": "5.2.0-prerelease.44",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -71,5 +71,5 @@
71
71
  "react": "^17 || ^18",
72
72
  "react-dom": "^17 || ^18"
73
73
  },
74
- "gitHead": "f4175ffc4c4db378d9512951f02e115128b675cb"
74
+ "gitHead": "53398c893451b88df64fc7458ce85daed6cad4d0"
75
75
  }
@@ -8,10 +8,16 @@ import { ASTERISK } from '../../helpers/htmlConstants';
8
8
  export interface CheckboxProps
9
9
  extends Omit<React.HTMLProps<HTMLInputElement>, 'type' | 'onChange' | 'disabled' | 'label'>,
10
10
  OUIAProps {
11
- /** Additional classes added to the checkbox. */
11
+ /** Additional classes added to the checkbox wrapper. This wrapper will be div element by default. It will be a label element if
12
+ * isLabelWrapped is true, or it can be overridden by any element specified in the component prop.
13
+ */
12
14
  className?: string;
13
- /** Additional classed added to the radio input */
15
+ /** Additional classes added to the checkbox input. */
14
16
  inputClassName?: string;
17
+ /** Flag to indicate whether the checkbox wrapper element is a <label> element for the checkbox input. Will not apply if a component prop (with a value other than a "label") is specified. */
18
+ isLabelWrapped?: boolean;
19
+ /** Flag to show if the checkbox label is shown before the checkbox input. */
20
+ isLabelBeforeButton?: boolean;
15
21
  /** Flag to show if the checkbox selection is valid or invalid. */
16
22
  isValid?: boolean;
17
23
  /** Flag to show if the checkbox is disabled. */
@@ -33,7 +39,7 @@ export interface CheckboxProps
33
39
  description?: React.ReactNode;
34
40
  /** Body text of the checkbox */
35
41
  body?: React.ReactNode;
36
- /** Sets the input wrapper component to render. Defaults to <div> */
42
+ /** Sets the checkbox wrapper component to render. Defaults to "div". If set to "label", behaves the same as if isLabelWrapped prop was specified. */
37
43
  component?: React.ElementType;
38
44
  /** Value to overwrite the randomly generated data-ouia-component-id.*/
39
45
  ouiaId?: number | string;
@@ -52,13 +58,13 @@ class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
52
58
  static displayName = 'Checkbox';
53
59
  static defaultProps: PickOptional<CheckboxProps> = {
54
60
  className: '',
61
+ isLabelWrapped: false,
55
62
  isValid: true,
56
63
  isDisabled: false,
57
64
  isRequired: false,
58
65
  isChecked: false,
59
66
  onChange: defaultOnChange,
60
- ouiaSafe: true,
61
- component: 'div'
67
+ ouiaSafe: true
62
68
  };
63
69
 
64
70
  constructor(props: any) {
@@ -78,6 +84,8 @@ class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
78
84
  className,
79
85
  inputClassName,
80
86
  onChange,
87
+ isLabelWrapped,
88
+ isLabelBeforeButton,
81
89
  isValid,
82
90
  isDisabled,
83
91
  isRequired,
@@ -89,7 +97,7 @@ class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
89
97
  body,
90
98
  ouiaId,
91
99
  ouiaSafe,
92
- component: Component,
100
+ component,
93
101
  ...props
94
102
  } = this.props;
95
103
  if (!props.id) {
@@ -107,31 +115,57 @@ class Checkbox extends React.Component<CheckboxProps, CheckboxState> {
107
115
  checkedProps.defaultChecked = defaultChecked;
108
116
  }
109
117
 
118
+ const inputRendered = (
119
+ <input
120
+ {...props}
121
+ className={css(styles.checkInput, inputClassName)}
122
+ type="checkbox"
123
+ onChange={this.handleChange}
124
+ aria-invalid={!isValid}
125
+ aria-label={ariaLabel}
126
+ disabled={isDisabled}
127
+ required={isRequired}
128
+ ref={(elem) => elem && (elem.indeterminate = isChecked === null)}
129
+ {...checkedProps}
130
+ {...getOUIAProps(Checkbox.displayName, ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe)}
131
+ />
132
+ );
133
+
134
+ const wrapWithLabel = (isLabelWrapped && !component) || component === 'label';
135
+
136
+ const Label = wrapWithLabel ? 'span' : 'label';
137
+ const labelRendered = label ? (
138
+ <Label
139
+ className={css(styles.checkLabel, isDisabled && styles.modifiers.disabled)}
140
+ htmlFor={!wrapWithLabel && props.id}
141
+ >
142
+ {label}
143
+ {isRequired && (
144
+ <span className={css(styles.checkLabelRequired)} aria-hidden="true">
145
+ {ASTERISK}
146
+ </span>
147
+ )}
148
+ </Label>
149
+ ) : null;
150
+
151
+ const Component = component ?? (wrapWithLabel ? 'label' : 'div');
152
+
110
153
  checkedProps.checked = checkedProps.checked === null ? false : checkedProps.checked;
111
154
  return (
112
- <Component className={css(styles.check, !label && styles.modifiers.standalone, className)}>
113
- <input
114
- {...props}
115
- className={css(styles.checkInput, inputClassName)}
116
- type="checkbox"
117
- onChange={this.handleChange}
118
- aria-invalid={!isValid}
119
- aria-label={ariaLabel}
120
- disabled={isDisabled}
121
- required={isRequired}
122
- ref={(elem) => elem && (elem.indeterminate = isChecked === null)}
123
- {...checkedProps}
124
- {...getOUIAProps(Checkbox.displayName, ouiaId !== undefined ? ouiaId : this.state.ouiaStateId, ouiaSafe)}
125
- />
126
- {label && (
127
- <label className={css(styles.checkLabel, isDisabled && styles.modifiers.disabled)} htmlFor={props.id}>
128
- {label}
129
- {isRequired && (
130
- <span className={css(styles.checkLabelRequired)} aria-hidden="true">
131
- {ASTERISK}
132
- </span>
133
- )}
134
- </label>
155
+ <Component
156
+ className={css(styles.check, !label && styles.modifiers.standalone, className)}
157
+ htmlFor={wrapWithLabel && props.id}
158
+ >
159
+ {isLabelBeforeButton ? (
160
+ <>
161
+ {labelRendered}
162
+ {inputRendered}
163
+ </>
164
+ ) : (
165
+ <>
166
+ {inputRendered}
167
+ {labelRendered}
168
+ </>
135
169
  )}
136
170
  {description && <span className={css(styles.checkDescription)}>{description}</span>}
137
171
  {body && <span className={css(styles.checkBody)}>{body}</span>}
@@ -231,6 +231,47 @@ test('Renders with the provided component', () => {
231
231
  expect(screen.getByRole('checkbox').parentElement?.tagName).toBe('SPAN');
232
232
  });
233
233
 
234
+ test('Renders with the label wrapper if isLabelWrapped is provided', () => {
235
+ render(<Checkbox id="test-id" isLabelWrapped />);
236
+
237
+ expect(screen.getByRole('checkbox').parentElement?.tagName).toBe('LABEL');
238
+ });
239
+
240
+ test('Renders with span element around the inner label text if isLabelWrapped is provided', () => {
241
+ const labelText = 'test checkbox label';
242
+ render(<Checkbox id="test-id" isLabelWrapped label={labelText} />);
243
+
244
+ expect(screen.getByText(labelText).tagName).toBe('SPAN');
245
+ });
246
+
247
+ test('Renders with the provided component although isLabelWrapped is provided', () => {
248
+ render(<Checkbox id="test-id" isLabelWrapped component="h3" />);
249
+
250
+ expect(screen.getByRole('checkbox').parentElement?.tagName).toBe('H3');
251
+ });
252
+
253
+ test('Renders with the label wrapper if component is set to label', () => {
254
+ render(<Checkbox id="test-id" component="label" />);
255
+
256
+ expect(screen.getByRole('checkbox').parentElement?.tagName).toBe('LABEL');
257
+ });
258
+
259
+ test('Renders with span element around the inner label text if component is set to label', () => {
260
+ const labelText = 'test checkbox label';
261
+ render(<Checkbox id="test-id" component="label" label={labelText} />);
262
+
263
+ expect(screen.getByText(labelText).tagName).toBe('SPAN');
264
+ });
265
+
266
+ test('Renders label before checkbox input if isLabelBeforeButton is provided', () => {
267
+ render(<Checkbox id="test-id" isLabelBeforeButton label={'test checkbox label'} />);
268
+
269
+ const wrapper = screen.getByRole('checkbox').parentElement!;
270
+
271
+ expect(wrapper.children[0].tagName).toBe('LABEL');
272
+ expect(wrapper.children[1].tagName).toBe('INPUT');
273
+ });
274
+
234
275
  test(`Spreads additional props`, () => {
235
276
  render(<Checkbox id="test-id" data-testid="test-id" />);
236
277
 
@@ -334,10 +334,10 @@ export const Popover: React.FunctionComponent<PopoverProps> = ({
334
334
  };
335
335
  const onDocumentClick = (event: MouseEvent, triggerElement: HTMLElement, popperElement: HTMLElement) => {
336
336
  if (hideOnOutsideClick && visible) {
337
- // check if we clicked within the popper, if so don't do anything
338
- const isChild = popperElement && popperElement.contains(event.target as Node);
339
- if (isChild) {
340
- // clicked within the popper
337
+ const isFromChild = popperElement && popperElement.contains(event.target as Node);
338
+ const isFromTrigger = triggerElement && triggerElement.contains(event.target as Node);
339
+ if (isFromChild || isFromTrigger) {
340
+ // if clicked within the popper or on the trigger, ignore this event
341
341
  return;
342
342
  }
343
343
  if (triggerManually) {
@@ -7,17 +7,17 @@ import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
7
7
  export interface RadioProps
8
8
  extends Omit<React.HTMLProps<HTMLInputElement>, 'disabled' | 'label' | 'onChange' | 'type'>,
9
9
  OUIAProps {
10
- /** Additional classes added to the radio wrapper. This will be a div element if
11
- * isLabelWrapped is true, otherwise this will be a label element.
10
+ /** Additional classes added to the radio wrapper. This wrapper will be div element by default. It will be a label element if
11
+ * isLabelWrapped is true, or it can be overridden by any element specified in the component prop.
12
12
  */
13
13
  className?: string;
14
- /** Additional classed added to the radio input */
14
+ /** Additional classes added to the radio input. */
15
15
  inputClassName?: string;
16
16
  /** Id of the radio. */
17
17
  id: string;
18
- /** Flag to show if the radio label is wrapped on small screen. */
18
+ /** Flag to indicate whether the radio wrapper element is a native label element for the radio input. Will not apply if a component prop (with a value other than a "label") is specified. */
19
19
  isLabelWrapped?: boolean;
20
- /** Flag to show if the radio label is shown before the radio button. */
20
+ /** Flag to show if the radio label is shown before the radio input. */
21
21
  isLabelBeforeButton?: boolean;
22
22
  /** Flag to show if the radio is checked. */
23
23
  checked?: boolean;
@@ -39,6 +39,8 @@ export interface RadioProps
39
39
  description?: React.ReactNode;
40
40
  /** Body of the radio. */
41
41
  body?: React.ReactNode;
42
+ /** Sets the radio wrapper component to render. Defaults to "div". If set to "label", behaves the same as if isLabelWrapped prop was specified. */
43
+ component?: React.ElementType;
42
44
  /** Value to overwrite the randomly generated data-ouia-component-id.*/
43
45
  ouiaId?: number | string;
44
46
  /** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -88,6 +90,7 @@ class Radio extends React.Component<RadioProps, { ouiaStateId: string }> {
88
90
  body,
89
91
  ouiaId,
90
92
  ouiaSafe = true,
93
+ component,
91
94
  ...props
92
95
  } = this.props;
93
96
  if (!props.id) {
@@ -110,41 +113,39 @@ class Radio extends React.Component<RadioProps, { ouiaStateId: string }> {
110
113
  />
111
114
  );
112
115
 
113
- let labelRendered: React.ReactNode = null;
114
- if (label && isLabelWrapped) {
115
- labelRendered = <span className={css(styles.radioLabel, isDisabled && styles.modifiers.disabled)}>{label}</span>;
116
- } else if (label) {
117
- labelRendered = (
118
- <label className={css(styles.radioLabel, isDisabled && styles.modifiers.disabled)} htmlFor={props.id}>
119
- {label}
120
- </label>
121
- );
122
- }
116
+ const wrapWithLabel = (isLabelWrapped && !component) || component === 'label';
123
117
 
124
- const descRender = description ? <span className={css(styles.radioDescription)}>{description}</span> : null;
125
- const bodyRender = body ? <span className={css(styles.radioBody)}>{body}</span> : null;
126
- const childrenRendered = isLabelBeforeButton ? (
127
- <>
128
- {labelRendered}
129
- {inputRendered}
130
- {descRender}
131
- {bodyRender}
132
- </>
133
- ) : (
134
- <>
135
- {inputRendered}
136
- {labelRendered}
137
- {descRender}
138
- {bodyRender}
139
- </>
140
- );
118
+ const Label = wrapWithLabel ? 'span' : 'label';
119
+ const labelRendered = label ? (
120
+ <Label
121
+ className={css(styles.radioLabel, isDisabled && styles.modifiers.disabled)}
122
+ htmlFor={!wrapWithLabel && props.id}
123
+ >
124
+ {label}
125
+ </Label>
126
+ ) : null;
127
+
128
+ const Component = component ?? (wrapWithLabel ? 'label' : 'div');
141
129
 
142
- return isLabelWrapped ? (
143
- <label className={css(styles.radio, className)} htmlFor={props.id}>
144
- {childrenRendered}
145
- </label>
146
- ) : (
147
- <div className={css(styles.radio, !label && styles.modifiers.standalone, className)}>{childrenRendered}</div>
130
+ return (
131
+ <Component
132
+ className={css(styles.radio, !label && styles.modifiers.standalone, className)}
133
+ htmlFor={wrapWithLabel && props.id}
134
+ >
135
+ {isLabelBeforeButton ? (
136
+ <>
137
+ {labelRendered}
138
+ {inputRendered}
139
+ </>
140
+ ) : (
141
+ <>
142
+ {inputRendered}
143
+ {labelRendered}
144
+ </>
145
+ )}
146
+ {description && <span className={css(styles.radioDescription)}>{description}</span>}
147
+ {body && <span className={css(styles.radioBody)}>{body}</span>}
148
+ </Component>
148
149
  );
149
150
  }
150
151
  }
@@ -100,4 +100,45 @@ describe('Radio', () => {
100
100
 
101
101
  expect(myMock).toHaveBeenCalled();
102
102
  });
103
+
104
+ test('Renders with the label wrapper if isLabelWrapped is provided', () => {
105
+ render(<Radio id="test-id" name="check" isLabelWrapped />);
106
+
107
+ expect(screen.getByRole('radio').parentElement?.tagName).toBe('LABEL');
108
+ });
109
+
110
+ test('Renders with span element around the inner label text if isLabelWrapped is provided', () => {
111
+ const labelText = 'test radio label';
112
+ render(<Radio id="test-id" name="check" isLabelWrapped label={labelText} />);
113
+
114
+ expect(screen.getByText(labelText).tagName).toBe('SPAN');
115
+ });
116
+
117
+ test('Renders with the provided component although isLabelWrapped is provided', () => {
118
+ render(<Radio id="test-id" name="check" isLabelWrapped component="h3" />);
119
+
120
+ expect(screen.getByRole('radio').parentElement?.tagName).toBe('H3');
121
+ });
122
+
123
+ test('Renders with the label wrapper if component is set to label', () => {
124
+ render(<Radio id="test-id" name="check" component="label" />);
125
+
126
+ expect(screen.getByRole('radio').parentElement?.tagName).toBe('LABEL');
127
+ });
128
+
129
+ test('Renders with span element around the inner label text if component is set to label', () => {
130
+ const labelText = 'test radio label';
131
+ render(<Radio id="test-id" name="check" component="label" label={labelText} />);
132
+
133
+ expect(screen.getByText(labelText).tagName).toBe('SPAN');
134
+ });
135
+
136
+ test('Renders label before radio input if isLabelBeforeButton is provided', () => {
137
+ render(<Radio id="test-id" name="check" isLabelBeforeButton label={'test radio label'} />);
138
+
139
+ const wrapper = screen.getByRole('radio').parentElement!;
140
+
141
+ expect(wrapper.children[0].tagName).toBe('LABEL');
142
+ expect(wrapper.children[1].tagName).toBe('INPUT');
143
+ });
103
144
  });