@patternfly/react-core 6.4.0 → 6.4.1-prerelease.1
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.
- package/CHANGELOG.md +16 -0
- package/components/package.json +1 -1
- package/deprecated/package.json +1 -1
- package/dist/dynamic/components/AboutModal/package.json +1 -1
- package/dist/dynamic/components/Accordion/package.json +1 -1
- package/dist/dynamic/components/ActionList/package.json +1 -1
- package/dist/dynamic/components/Alert/package.json +1 -1
- package/dist/dynamic/components/Avatar/package.json +1 -1
- package/dist/dynamic/components/BackToTop/package.json +1 -1
- package/dist/dynamic/components/Backdrop/package.json +1 -1
- package/dist/dynamic/components/BackgroundImage/package.json +1 -1
- package/dist/dynamic/components/Badge/package.json +1 -1
- package/dist/dynamic/components/Banner/package.json +1 -1
- package/dist/dynamic/components/Brand/package.json +1 -1
- package/dist/dynamic/components/Breadcrumb/package.json +1 -1
- package/dist/dynamic/components/Button/package.json +1 -1
- package/dist/dynamic/components/CalendarMonth/package.json +1 -1
- package/dist/dynamic/components/Card/package.json +1 -1
- package/dist/dynamic/components/Checkbox/package.json +1 -1
- package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
- package/dist/dynamic/components/CodeBlock/package.json +1 -1
- package/dist/dynamic/components/Content/package.json +1 -1
- package/dist/dynamic/components/DataList/package.json +1 -1
- package/dist/dynamic/components/DatePicker/package.json +1 -1
- package/dist/dynamic/components/DescriptionList/package.json +1 -1
- package/dist/dynamic/components/Divider/package.json +1 -1
- package/dist/dynamic/components/Drawer/package.json +1 -1
- package/dist/dynamic/components/Dropdown/package.json +1 -1
- package/dist/dynamic/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/components/EmptyState/package.json +1 -1
- package/dist/dynamic/components/ExpandableSection/package.json +1 -1
- package/dist/dynamic/components/FileUpload/package.json +1 -1
- package/dist/dynamic/components/Form/package.json +1 -1
- package/dist/dynamic/components/FormSelect/package.json +1 -1
- package/dist/dynamic/components/HelperText/package.json +1 -1
- package/dist/dynamic/components/Hint/package.json +1 -1
- package/dist/dynamic/components/Icon/package.json +1 -1
- package/dist/dynamic/components/InputGroup/package.json +1 -1
- package/dist/dynamic/components/JumpLinks/package.json +1 -1
- package/dist/dynamic/components/Label/package.json +1 -1
- package/dist/dynamic/components/List/package.json +1 -1
- package/dist/dynamic/components/LoginPage/package.json +1 -1
- package/dist/dynamic/components/Masthead/package.json +1 -1
- package/dist/dynamic/components/Menu/package.json +1 -1
- package/dist/dynamic/components/MenuToggle/package.json +1 -1
- package/dist/dynamic/components/Modal/package.json +1 -1
- package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
- package/dist/dynamic/components/Nav/package.json +1 -1
- package/dist/dynamic/components/NotificationBadge/package.json +1 -1
- package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
- package/dist/dynamic/components/NumberInput/package.json +1 -1
- package/dist/dynamic/components/OverflowMenu/package.json +1 -1
- package/dist/dynamic/components/Page/package.json +1 -1
- package/dist/dynamic/components/Pagination/package.json +1 -1
- package/dist/dynamic/components/Panel/package.json +1 -1
- package/dist/dynamic/components/Popover/package.json +1 -1
- package/dist/dynamic/components/Progress/package.json +1 -1
- package/dist/dynamic/components/ProgressStepper/package.json +1 -1
- package/dist/dynamic/components/Radio/package.json +1 -1
- package/dist/dynamic/components/SearchInput/package.json +1 -1
- package/dist/dynamic/components/Select/package.json +1 -1
- package/dist/dynamic/components/Sidebar/package.json +1 -1
- package/dist/dynamic/components/SimpleList/package.json +1 -1
- package/dist/dynamic/components/Skeleton/package.json +1 -1
- package/dist/dynamic/components/SkipToContent/package.json +1 -1
- package/dist/dynamic/components/Slider/package.json +1 -1
- package/dist/dynamic/components/Spinner/package.json +1 -1
- package/dist/dynamic/components/Switch/package.json +1 -1
- package/dist/dynamic/components/Tabs/package.json +1 -1
- package/dist/dynamic/components/TextArea/package.json +1 -1
- package/dist/dynamic/components/TextInput/package.json +1 -1
- package/dist/dynamic/components/TextInputGroup/package.json +1 -1
- package/dist/dynamic/components/TimePicker/package.json +1 -1
- package/dist/dynamic/components/Timestamp/package.json +1 -1
- package/dist/dynamic/components/Title/package.json +1 -1
- package/dist/dynamic/components/ToggleGroup/package.json +1 -1
- package/dist/dynamic/components/Toolbar/package.json +1 -1
- package/dist/dynamic/components/Tooltip/package.json +1 -1
- package/dist/dynamic/components/TreeView/package.json +1 -1
- package/dist/dynamic/components/Truncate/package.json +1 -1
- package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
- package/dist/dynamic/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/Chip/package.json +1 -1
- package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
- package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
- package/dist/dynamic/deprecated/components/Tile/package.json +1 -1
- package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/package.json +1 -1
- package/dist/dynamic/helpers/AnimationsProvider/AnimationsProvider/package.json +1 -1
- package/dist/dynamic/helpers/AnimationsProvider/package.json +1 -1
- package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
- package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
- package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
- package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
- package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
- package/dist/dynamic/helpers/constants/package.json +1 -1
- package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
- package/dist/dynamic/helpers/fileUtils/package.json +1 -1
- package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
- package/dist/dynamic/helpers/package.json +1 -1
- package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
- package/dist/dynamic/helpers/typeUtils/package.json +1 -1
- package/dist/dynamic/helpers/useInterval/package.json +1 -1
- package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
- package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
- package/dist/dynamic/helpers/util/package.json +1 -1
- package/dist/dynamic/layouts/Bullseye/package.json +1 -1
- package/dist/dynamic/layouts/Flex/package.json +1 -1
- package/dist/dynamic/layouts/Gallery/package.json +1 -1
- package/dist/dynamic/layouts/Grid/package.json +1 -1
- package/dist/dynamic/layouts/Level/package.json +1 -1
- package/dist/dynamic/layouts/Split/package.json +1 -1
- package/dist/dynamic/layouts/Stack/package.json +1 -1
- package/dist/dynamic/styles/package.json +1 -1
- package/dist/esm/components/Checkbox/Checkbox.d.ts +3 -0
- package/dist/esm/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/esm/components/Checkbox/Checkbox.js +14 -4
- package/dist/esm/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/Drawer/DrawerPanelContent.d.ts.map +1 -1
- package/dist/esm/components/Drawer/DrawerPanelContent.js +2 -4
- package/dist/esm/components/Drawer/DrawerPanelContent.js.map +1 -1
- package/dist/esm/components/Modal/Modal.d.ts +2 -0
- package/dist/esm/components/Modal/Modal.d.ts.map +1 -1
- package/dist/esm/components/Modal/Modal.js.map +1 -1
- package/dist/esm/components/Modal/ModalContent.d.ts +2 -0
- package/dist/esm/components/Modal/ModalContent.d.ts.map +1 -1
- package/dist/esm/components/Modal/ModalContent.js +2 -2
- package/dist/esm/components/Modal/ModalContent.js.map +1 -1
- package/dist/esm/components/Progress/ProgressContainer.js +2 -2
- package/dist/esm/components/Progress/ProgressContainer.js.map +1 -1
- package/dist/esm/components/Radio/Radio.d.ts +3 -0
- package/dist/esm/components/Radio/Radio.d.ts.map +1 -1
- package/dist/esm/components/Radio/Radio.js +15 -5
- package/dist/esm/components/Radio/Radio.js.map +1 -1
- package/dist/esm/components/Wizard/WizardNavItem.d.ts +1 -1
- package/dist/esm/components/Wizard/WizardNavItem.d.ts.map +1 -1
- package/dist/esm/components/Wizard/WizardNavItem.js +2 -1
- package/dist/esm/components/Wizard/WizardNavItem.js.map +1 -1
- package/dist/esm/components/Wizard/WizardStep.d.ts +1 -1
- package/dist/esm/components/Wizard/WizardStep.d.ts.map +1 -1
- package/dist/esm/components/Wizard/types.d.ts +3 -2
- package/dist/esm/components/Wizard/types.d.ts.map +1 -1
- package/dist/esm/components/Wizard/types.js +1 -0
- package/dist/esm/components/Wizard/types.js.map +1 -1
- package/dist/esm/helpers/FocusTrap/FocusTrap.d.ts +2 -0
- package/dist/esm/helpers/FocusTrap/FocusTrap.d.ts.map +1 -1
- package/dist/esm/helpers/FocusTrap/FocusTrap.js.map +1 -1
- package/dist/js/components/Checkbox/Checkbox.d.ts +3 -0
- package/dist/js/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/js/components/Checkbox/Checkbox.js +14 -4
- package/dist/js/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/js/components/Drawer/DrawerPanelContent.d.ts.map +1 -1
- package/dist/js/components/Drawer/DrawerPanelContent.js +2 -4
- package/dist/js/components/Drawer/DrawerPanelContent.js.map +1 -1
- package/dist/js/components/Modal/Modal.d.ts +2 -0
- package/dist/js/components/Modal/Modal.d.ts.map +1 -1
- package/dist/js/components/Modal/Modal.js.map +1 -1
- package/dist/js/components/Modal/ModalContent.d.ts +2 -0
- package/dist/js/components/Modal/ModalContent.d.ts.map +1 -1
- package/dist/js/components/Modal/ModalContent.js +2 -2
- package/dist/js/components/Modal/ModalContent.js.map +1 -1
- package/dist/js/components/Progress/ProgressContainer.js +2 -2
- package/dist/js/components/Progress/ProgressContainer.js.map +1 -1
- package/dist/js/components/Radio/Radio.d.ts +3 -0
- package/dist/js/components/Radio/Radio.d.ts.map +1 -1
- package/dist/js/components/Radio/Radio.js +15 -5
- package/dist/js/components/Radio/Radio.js.map +1 -1
- package/dist/js/components/Wizard/WizardNavItem.d.ts +1 -1
- package/dist/js/components/Wizard/WizardNavItem.d.ts.map +1 -1
- package/dist/js/components/Wizard/WizardNavItem.js +2 -1
- package/dist/js/components/Wizard/WizardNavItem.js.map +1 -1
- package/dist/js/components/Wizard/WizardStep.d.ts +1 -1
- package/dist/js/components/Wizard/WizardStep.d.ts.map +1 -1
- package/dist/js/components/Wizard/types.d.ts +3 -2
- package/dist/js/components/Wizard/types.d.ts.map +1 -1
- package/dist/js/components/Wizard/types.js +1 -0
- package/dist/js/components/Wizard/types.js.map +1 -1
- package/dist/js/helpers/FocusTrap/FocusTrap.d.ts +2 -0
- package/dist/js/helpers/FocusTrap/FocusTrap.d.ts.map +1 -1
- package/dist/js/helpers/FocusTrap/FocusTrap.js.map +1 -1
- package/dist/umd/assets/{output-DH8J4Va1.css → output-POOr9zB4.css} +20855 -20855
- package/dist/umd/react-core.min.js +1 -1
- package/helpers/package.json +1 -1
- package/layouts/package.json +1 -1
- package/next/package.json +1 -1
- package/package.json +2 -2
- package/src/components/Checkbox/Checkbox.tsx +21 -2
- package/src/components/Checkbox/__tests__/Checkbox.test.tsx +34 -0
- package/src/components/Drawer/DrawerPanelContent.tsx +5 -3
- package/src/components/Drawer/__tests__/DrawerPanelContent.test.tsx +37 -0
- package/src/components/Modal/Modal.tsx +2 -0
- package/src/components/Modal/ModalContent.tsx +4 -0
- package/src/components/Modal/__tests__/Modal.test.tsx +9 -0
- package/src/components/Modal/__tests__/ModalContent.test.tsx +17 -1
- package/src/components/Modal/examples/Modal.md +14 -5
- package/src/components/Modal/examples/ModalWithDropdown.tsx +34 -7
- package/src/components/Progress/ProgressContainer.tsx +2 -2
- package/src/components/Progress/__tests__/Generated/__snapshots__/ProgressContainer.test.tsx.snap +1 -1
- package/src/components/Progress/__tests__/__snapshots__/Progress.test.tsx.snap +1 -1
- package/src/components/Radio/Radio.tsx +21 -3
- package/src/components/Radio/__tests__/Radio.test.tsx +44 -0
- package/src/components/Tabs/__tests__/Tabs.test.tsx +109 -1
- package/src/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +2 -2
- package/src/components/Tabs/examples/Tabs.md +38 -4
- package/src/components/Tabs/examples/TabsSeparateContent.tsx +3 -2
- package/src/components/Wizard/WizardNavItem.tsx +5 -2
- package/src/components/Wizard/WizardStep.tsx +1 -1
- package/src/components/Wizard/__tests__/WizardNavItem.test.tsx +6 -0
- package/src/components/Wizard/__tests__/WizardStep.test.tsx +2 -0
- package/src/components/Wizard/examples/WizardStepStatus.tsx +28 -7
- package/src/components/Wizard/types.tsx +3 -2
- package/src/demos/SearchInput/SearchInput.md +2 -487
- package/src/demos/SearchInput/examples/SearchInputAdvancedComposable.tsx +322 -0
- package/src/demos/SearchInput/examples/SearchInputAutocomplete.tsx +165 -0
- package/src/helpers/FocusTrap/FocusTrap.tsx +2 -0
- package/src/helpers/FocusTrap/__tests__/FocusTrap.test.tsx +17 -0
- package/src/helpers/FocusTrap/__tests__/Generated/FocusTrap.test.tsx +3 -3
- package/src/helpers/OUIA/OUIA.md +2 -10
- package/src/helpers/OUIA/examples/OuiaExample.tsx +11 -0
- package/src/layouts/Bullseye/examples/Bullseye.md +1 -5
- package/src/layouts/Bullseye/examples/BullseyeBasic.tsx +7 -0
- package/src/layouts/Flex/examples/Flex.md +17 -209
- package/src/layouts/Flex/examples/FlexAdjustingWidth.tsx +18 -0
- package/src/layouts/Flex/examples/FlexBasic.tsx +11 -0
- package/src/layouts/Flex/examples/FlexCanGrow.tsx +18 -0
- package/src/layouts/Flex/examples/FlexColumnGap.tsx +16 -0
- package/src/layouts/Flex/examples/FlexColumnLayout.tsx +9 -0
- package/src/layouts/Flex/examples/FlexColumnWidths.tsx +18 -0
- package/src/layouts/Flex/examples/FlexDefaultLayout.tsx +11 -0
- package/src/layouts/Flex/examples/FlexGap.tsx +16 -0
- package/src/layouts/Flex/examples/FlexIndividuallySpaced.tsx +14 -0
- package/src/layouts/Flex/examples/FlexInline.tsx +11 -0
- package/src/layouts/Flex/examples/FlexNestedItems.tsx +15 -0
- package/src/layouts/Flex/examples/FlexNesting.tsx +15 -0
- package/src/layouts/Flex/examples/FlexNestingInColumns.tsx +15 -0
- package/src/layouts/Flex/examples/FlexRowGap.tsx +16 -0
- package/src/layouts/Flex/examples/FlexSpacingNone.tsx +11 -0
- package/src/layouts/Flex/examples/FlexSpacingXl.tsx +11 -0
- package/src/layouts/Flex/examples/FlexStackingElements.tsx +18 -0
- package/src/layouts/Level/examples/Level.md +3 -15
- package/src/layouts/Level/examples/LevelBasic.tsx +9 -0
- package/src/layouts/Level/examples/LevelWithGutters.tsx +9 -0
- package/src/layouts/Split/examples/Split.md +4 -34
- package/src/layouts/Split/examples/SplitBasic.tsx +9 -0
- package/src/layouts/Split/examples/SplitWithGutter.tsx +9 -0
- package/src/layouts/Split/examples/SplitWrappable.tsx +20 -0
package/helpers/package.json
CHANGED
|
@@ -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":"6.4.
|
|
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":"6.4.1-prerelease.0","private":true}
|
package/layouts/package.json
CHANGED
|
@@ -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":"6.4.
|
|
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":"6.4.1-prerelease.0","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":"6.4.
|
|
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":"6.4.1-prerelease.0","private":true}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "6.4.
|
|
3
|
+
"version": "6.4.1-prerelease.1",
|
|
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",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"react": "^17 || ^18 || ^19",
|
|
64
64
|
"react-dom": "^17 || ^18 || ^19"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "2ec8ac7e1e6d08adcf36faca24d6a7a5a4043552"
|
|
67
67
|
}
|
|
@@ -3,6 +3,7 @@ import styles from '@patternfly/react-styles/css/components/Check/check';
|
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
4
|
import { PickOptional } from '../../helpers/typeUtils';
|
|
5
5
|
import { getDefaultOUIAId, getOUIAProps, OUIAProps } from '../../helpers';
|
|
6
|
+
import { getUniqueId } from '../../helpers/util';
|
|
6
7
|
import { ASTERISK } from '../../helpers/htmlConstants';
|
|
7
8
|
|
|
8
9
|
export interface CheckboxProps
|
|
@@ -39,6 +40,8 @@ export interface CheckboxProps
|
|
|
39
40
|
description?: React.ReactNode;
|
|
40
41
|
/** Body text of the checkbox */
|
|
41
42
|
body?: React.ReactNode;
|
|
43
|
+
/** Custom aria-describedby value for the checkbox input. If not provided and description is set, a unique ID will be generated automatically. */
|
|
44
|
+
'aria-describedby'?: string;
|
|
42
45
|
/** Sets the checkbox wrapper component to render. Defaults to "div". If set to "label", behaves the same as if isLabelWrapped prop was specified. */
|
|
43
46
|
component?: React.ElementType;
|
|
44
47
|
/** Value to overwrite the randomly generated data-ouia-component-id.*/
|
|
@@ -52,6 +55,7 @@ const defaultOnChange = () => {};
|
|
|
52
55
|
|
|
53
56
|
interface CheckboxState {
|
|
54
57
|
ouiaStateId: string;
|
|
58
|
+
descriptionId: string;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
@@ -70,7 +74,8 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
70
74
|
constructor(props: any) {
|
|
71
75
|
super(props);
|
|
72
76
|
this.state = {
|
|
73
|
-
ouiaStateId: getDefaultOUIAId(Checkbox.displayName)
|
|
77
|
+
ouiaStateId: getDefaultOUIAId(Checkbox.displayName),
|
|
78
|
+
descriptionId: getUniqueId('pf-checkbox-description')
|
|
74
79
|
};
|
|
75
80
|
}
|
|
76
81
|
|
|
@@ -81,6 +86,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
81
86
|
render() {
|
|
82
87
|
const {
|
|
83
88
|
'aria-label': ariaLabel,
|
|
89
|
+
'aria-describedby': ariaDescribedBy,
|
|
84
90
|
className,
|
|
85
91
|
inputClassName,
|
|
86
92
|
onChange,
|
|
@@ -115,6 +121,14 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
115
121
|
checkedProps.defaultChecked = defaultChecked;
|
|
116
122
|
}
|
|
117
123
|
|
|
124
|
+
// Handle aria-describedby logic
|
|
125
|
+
let ariaDescribedByValue: string | undefined;
|
|
126
|
+
if (ariaDescribedBy !== undefined) {
|
|
127
|
+
ariaDescribedByValue = ariaDescribedBy === '' ? undefined : ariaDescribedBy;
|
|
128
|
+
} else if (description) {
|
|
129
|
+
ariaDescribedByValue = this.state.descriptionId;
|
|
130
|
+
}
|
|
131
|
+
|
|
118
132
|
const inputRendered = (
|
|
119
133
|
<input
|
|
120
134
|
{...props}
|
|
@@ -123,6 +137,7 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
123
137
|
onChange={this.handleChange}
|
|
124
138
|
aria-invalid={!isValid}
|
|
125
139
|
aria-label={ariaLabel}
|
|
140
|
+
aria-describedby={ariaDescribedByValue}
|
|
126
141
|
disabled={isDisabled}
|
|
127
142
|
required={isRequired}
|
|
128
143
|
ref={(elem) => {
|
|
@@ -169,7 +184,11 @@ class Checkbox extends Component<CheckboxProps, CheckboxState> {
|
|
|
169
184
|
{labelRendered}
|
|
170
185
|
</>
|
|
171
186
|
)}
|
|
172
|
-
{description &&
|
|
187
|
+
{description && (
|
|
188
|
+
<span id={this.state.descriptionId} className={css(styles.checkDescription)}>
|
|
189
|
+
{description}
|
|
190
|
+
</span>
|
|
191
|
+
)}
|
|
173
192
|
{body && <span className={css(styles.checkBody)}>{body}</span>}
|
|
174
193
|
</Component>
|
|
175
194
|
);
|
|
@@ -287,3 +287,37 @@ test('Matches snapshot', () => {
|
|
|
287
287
|
|
|
288
288
|
expect(asFragment()).toMatchSnapshot();
|
|
289
289
|
});
|
|
290
|
+
|
|
291
|
+
test('Sets aria-describedby when description is provided', () => {
|
|
292
|
+
render(<Checkbox id="test-id" description="test description" />);
|
|
293
|
+
|
|
294
|
+
const checkbox = screen.getByRole('checkbox');
|
|
295
|
+
const descriptionElement = screen.getByText('test description');
|
|
296
|
+
|
|
297
|
+
expect(checkbox).toHaveAttribute('aria-describedby', descriptionElement.id);
|
|
298
|
+
expect(descriptionElement).toHaveAttribute('id');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('Sets custom aria-describedby when provided', () => {
|
|
302
|
+
render(<Checkbox id="test-id" description="test description" aria-describedby="custom-id" />);
|
|
303
|
+
|
|
304
|
+
const checkbox = screen.getByRole('checkbox');
|
|
305
|
+
|
|
306
|
+
expect(checkbox).toHaveAttribute('aria-describedby', 'custom-id');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test('Does not set aria-describedby when no description is provided', () => {
|
|
310
|
+
render(<Checkbox id="test-id" />);
|
|
311
|
+
|
|
312
|
+
const checkbox = screen.getByRole('checkbox');
|
|
313
|
+
|
|
314
|
+
expect(checkbox).not.toHaveAttribute('aria-describedby');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('Does not set aria-describedby when description is provided but aria-describedby is empty string', () => {
|
|
318
|
+
render(<Checkbox id="test-id" description="test description" aria-describedby="" />);
|
|
319
|
+
|
|
320
|
+
const checkbox = screen.getByRole('checkbox');
|
|
321
|
+
|
|
322
|
+
expect(checkbox).not.toHaveAttribute('aria-describedby');
|
|
323
|
+
});
|
|
@@ -77,6 +77,7 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
|
|
|
77
77
|
widths,
|
|
78
78
|
colorVariant = DrawerColorVariant.default,
|
|
79
79
|
focusTrap,
|
|
80
|
+
style,
|
|
80
81
|
...props
|
|
81
82
|
}: DrawerPanelContentProps) => {
|
|
82
83
|
const panel = useRef<HTMLDivElement>(undefined);
|
|
@@ -381,9 +382,10 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
|
|
|
381
382
|
}
|
|
382
383
|
}}
|
|
383
384
|
hidden={hidden}
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
|
|
385
|
+
style={{
|
|
386
|
+
...((defaultSize || minSize || maxSize) && boundaryCssVars),
|
|
387
|
+
...style
|
|
388
|
+
}}
|
|
387
389
|
{...props}
|
|
388
390
|
ref={panel}
|
|
389
391
|
>
|
|
@@ -121,3 +121,40 @@ test('Renders with role="dialog" when focusTrap.enabled is true', () => {
|
|
|
121
121
|
|
|
122
122
|
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
test('Applies style prop as expected', () => {
|
|
126
|
+
render(
|
|
127
|
+
<Drawer isExpanded>
|
|
128
|
+
<DrawerPanelContent style={{ backgroundColor: 'red', padding: '20px' }}>Drawer panel content</DrawerPanelContent>
|
|
129
|
+
</Drawer>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const panelContent = screen.getByText('Drawer panel content');
|
|
133
|
+
expect(panelContent).toHaveStyle({ backgroundColor: 'red', padding: '20px' });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('Style prop overrides boundaryCssVars', () => {
|
|
137
|
+
render(
|
|
138
|
+
<Drawer isExpanded>
|
|
139
|
+
<DrawerPanelContent
|
|
140
|
+
defaultSize="200px"
|
|
141
|
+
minSize="100px"
|
|
142
|
+
maxSize="400px"
|
|
143
|
+
style={{
|
|
144
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis': '300px',
|
|
145
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis--min': '150px',
|
|
146
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis--max': '500px'
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
Drawer panel content
|
|
150
|
+
</DrawerPanelContent>
|
|
151
|
+
</Drawer>
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const panelContent = screen.getByText('Drawer panel content');
|
|
155
|
+
expect(panelContent).toHaveStyle({
|
|
156
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis': '300px',
|
|
157
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis--min': '150px',
|
|
158
|
+
'--pf-v6-c-drawer__panel--md--FlexBasis--max': '500px'
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -27,6 +27,8 @@ export interface ModalProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
|
27
27
|
* focusable element will receive focus.
|
|
28
28
|
*/
|
|
29
29
|
elementToFocus?: HTMLElement | SVGElement | string;
|
|
30
|
+
/** Id of the focus trap in the ModalContent component */
|
|
31
|
+
focusTrapId?: string;
|
|
30
32
|
/** An id to use for the modal box container. */
|
|
31
33
|
id?: string;
|
|
32
34
|
/** Flag to show the modal. */
|
|
@@ -29,6 +29,8 @@ export interface ModalContentProps extends OUIAProps {
|
|
|
29
29
|
* focusable element will receive focus.
|
|
30
30
|
*/
|
|
31
31
|
elementToFocus?: HTMLElement | SVGElement | string;
|
|
32
|
+
/** Id of the focus trap */
|
|
33
|
+
focusTrapId?: string;
|
|
32
34
|
/** Flag to show the modal. */
|
|
33
35
|
isOpen?: boolean;
|
|
34
36
|
/** A callback for when the close button is clicked. */
|
|
@@ -69,6 +71,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
|
69
71
|
ouiaId,
|
|
70
72
|
ouiaSafe = true,
|
|
71
73
|
elementToFocus,
|
|
74
|
+
focusTrapId,
|
|
72
75
|
...props
|
|
73
76
|
}: ModalContentProps) => {
|
|
74
77
|
if (!isOpen) {
|
|
@@ -122,6 +125,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
|
122
125
|
initialFocus: elementToFocus || undefined
|
|
123
126
|
}}
|
|
124
127
|
className={css(bullsEyeStyles.bullseye)}
|
|
128
|
+
id={focusTrapId}
|
|
125
129
|
>
|
|
126
130
|
{modalBox}
|
|
127
131
|
</FocusTrap>
|
|
@@ -172,4 +172,13 @@ describe('Modal', () => {
|
|
|
172
172
|
expect(asideSibling).not.toHaveAttribute('aria-hidden');
|
|
173
173
|
expect(articleSibling).not.toHaveAttribute('aria-hidden');
|
|
174
174
|
});
|
|
175
|
+
|
|
176
|
+
test('Modal can add id to focus trap correctly for use with dropdowns', () => {
|
|
177
|
+
render(<Modal focusTrapId="focus-trap" isOpen onClose={jest.fn()} children="modal content" />);
|
|
178
|
+
expect(screen.getByRole('dialog', { name: /modal content/i }).parentElement).toHaveAttribute('id', 'focus-trap');
|
|
179
|
+
expect(screen.getByRole('dialog', { name: /modal content/i }).parentElement).toHaveAttribute(
|
|
180
|
+
'class',
|
|
181
|
+
'pf-v6-l-bullseye'
|
|
182
|
+
);
|
|
183
|
+
});
|
|
175
184
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render } from '@testing-library/react';
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
2
|
|
|
3
3
|
import { ModalContent } from '../ModalContent';
|
|
4
4
|
|
|
@@ -43,3 +43,19 @@ test('Modal Content Test with onclose', () => {
|
|
|
43
43
|
);
|
|
44
44
|
expect(asFragment()).toMatchSnapshot();
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
test('Modal content can add id to focus trap correctly for use with dropdowns', () => {
|
|
48
|
+
render(
|
|
49
|
+
<ModalContent focusTrapId="focus-trap" isOpen {...modalContentProps}>
|
|
50
|
+
This is a ModalBox header
|
|
51
|
+
</ModalContent>
|
|
52
|
+
);
|
|
53
|
+
expect(screen.getByRole('dialog', { name: /This is a ModalBox header/i }).parentElement).toHaveAttribute(
|
|
54
|
+
'id',
|
|
55
|
+
'focus-trap'
|
|
56
|
+
);
|
|
57
|
+
expect(screen.getByRole('dialog', { name: /This is a ModalBox header/i }).parentElement).toHaveAttribute(
|
|
58
|
+
'class',
|
|
59
|
+
'pf-v6-l-bullseye'
|
|
60
|
+
);
|
|
61
|
+
});
|
|
@@ -17,7 +17,7 @@ import formStyles from '@patternfly/react-styles/css/components/Form/form';
|
|
|
17
17
|
|
|
18
18
|
### Basic modals
|
|
19
19
|
|
|
20
|
-
Basic modals give users the option to either confirm or cancel an action.
|
|
20
|
+
Basic modals give users the option to either confirm or cancel an action.
|
|
21
21
|
|
|
22
22
|
To flag an open modal, use the `isOpen` property. To execute a callback when a modal is closed, use the `onClose` property.
|
|
23
23
|
|
|
@@ -71,7 +71,7 @@ To choose a specific width for a modal, use the `width` property. The following
|
|
|
71
71
|
|
|
72
72
|
### Custom header
|
|
73
73
|
|
|
74
|
-
To add a custom header to a modal, your custom content must be passed as a child of the `<ModalHeader>` component. Do not pass the `title` property to `<ModalHeader>` when using a custom header.
|
|
74
|
+
To add a custom header to a modal, your custom content must be passed as a child of the `<ModalHeader>` component. Do not pass the `title` property to `<ModalHeader>` when using a custom header.
|
|
75
75
|
|
|
76
76
|
```ts file="./ModalCustomHeader.tsx"
|
|
77
77
|
|
|
@@ -113,9 +113,18 @@ To guide users through a series of steps in a modal, you can add a [wizard](/com
|
|
|
113
113
|
|
|
114
114
|
### With dropdown
|
|
115
115
|
|
|
116
|
-
To present a menu of actions or links to a user, you can add a [dropdown](/components/menus/dropdown) to a modal.
|
|
116
|
+
To present a menu of actions or links to a user, you can add a [dropdown](/components/menus/dropdown) to a modal.
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
Using the Dropdown's default append location will interfere with keyboard accessibility due to the modal's
|
|
119
|
+
built-in focus trap. To allow the dropdown to visually break out of the modal container, set the Dropdown's
|
|
120
|
+
`popperProps appendTo` property to id of the focus trap for the modal. This can be done by adding prop
|
|
121
|
+
`focusTrapId` to the modal, and then setting the append location to that as per this example. Otherwise you
|
|
122
|
+
can use `inline` to allow it to scroll within the modal itself. Appending to the focus trap should allow the
|
|
123
|
+
menu to expand visually outside the Modal (no scrollbar created in the Modal itself), and still allow
|
|
124
|
+
focusing the content within that menu via keyboard. You should also handle the modal's closing behavior by
|
|
125
|
+
listening to the
|
|
126
|
+
`onEscapePress` callback on the `<Modal>` component. This allows the "escape" key to collapse the
|
|
127
|
+
dropdown without closing the entire modal.
|
|
119
128
|
|
|
120
129
|
```ts file="./ModalWithDropdown.tsx"
|
|
121
130
|
|
|
@@ -141,7 +150,7 @@ To enable form submission from a button in the modal's footer (outside of the `<
|
|
|
141
150
|
|
|
142
151
|
### Custom focus
|
|
143
152
|
|
|
144
|
-
To customize which element inside the modal receives focus when initially opened, use the `elementToFocus` property`.
|
|
153
|
+
To customize which element inside the modal receives focus when initially opened, use the `elementToFocus` property`.
|
|
145
154
|
|
|
146
155
|
```ts file="./ModalCustomFocus.tsx"
|
|
147
156
|
|
|
@@ -32,8 +32,10 @@ export const ModalWithDropdown: React.FunctionComponent = () => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const onFocus = () => {
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (typeof document !== 'undefined') {
|
|
36
|
+
const element = document.getElementById('modal-dropdown-toggle');
|
|
37
|
+
(element as HTMLElement)?.focus();
|
|
38
|
+
}
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
const onEscapePress = (event: KeyboardEvent) => {
|
|
@@ -45,6 +47,16 @@ export const ModalWithDropdown: React.FunctionComponent = () => {
|
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
|
|
50
|
+
const getAppendLocation = () => {
|
|
51
|
+
// document doesn't exist when PatternFly website docs framework gets pre-rendered
|
|
52
|
+
// this is just to avoid that issue - works fine at runtime without it
|
|
53
|
+
if (typeof document !== 'undefined' && document.getElementById) {
|
|
54
|
+
return document.getElementById('modal-with-dropdown-focus-trap');
|
|
55
|
+
} else {
|
|
56
|
+
return 'inline';
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
48
60
|
return (
|
|
49
61
|
<Fragment>
|
|
50
62
|
<Button variant="primary" onClick={handleModalToggle}>
|
|
@@ -57,14 +69,21 @@ export const ModalWithDropdown: React.FunctionComponent = () => {
|
|
|
57
69
|
onEscapePress={onEscapePress}
|
|
58
70
|
aria-labelledby="modal-with-dropdown"
|
|
59
71
|
aria-describedby="modal-box-body-with-dropdown"
|
|
72
|
+
focusTrapId="modal-with-dropdown-focus-trap"
|
|
60
73
|
>
|
|
61
74
|
<ModalHeader title="Dropdown modal" labelId="modal-with-dropdown" />
|
|
62
75
|
<ModalBody id="modal-box-body-with-dropdown">
|
|
63
76
|
<div>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
77
|
+
Using the Dropdown's default append location will interfere with keyboard accessibility due to the modal's
|
|
78
|
+
built-in focus trap. To allow the dropdown to visually break out of the modal container, set the Dropdown's
|
|
79
|
+
popperProps appendTo property to id of the focus trap for the modal. This can be done by adding prop
|
|
80
|
+
focusTrapId to the modal, and then setting the append location to that as per this example. Otherwise you
|
|
81
|
+
can use "inline" to allow it to scroll within the modal itself. Appending to the focus trap should allow the
|
|
82
|
+
menu to expand visually outside the Modal (no scrollbar created in the Modal itself), and still allow
|
|
83
|
+
focusing the content within that menu via keyboard. You should also handle the modal's closing behavior by
|
|
84
|
+
listening to the
|
|
85
|
+
<em>onEscapePress</em> callback on the Modal component. This allows the "escape" key to collapse the
|
|
86
|
+
dropdown without closing the entire modal.
|
|
68
87
|
</div>
|
|
69
88
|
<br />
|
|
70
89
|
<div>
|
|
@@ -73,10 +92,18 @@ export const ModalWithDropdown: React.FunctionComponent = () => {
|
|
|
73
92
|
onSelect={onSelect}
|
|
74
93
|
onOpenChange={(isOpen: boolean) => setIsDropdownOpen(isOpen)}
|
|
75
94
|
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
|
|
76
|
-
<MenuToggle
|
|
95
|
+
<MenuToggle
|
|
96
|
+
id="modal-dropdown-toggle"
|
|
97
|
+
ref={toggleRef}
|
|
98
|
+
onClick={handleDropdownToggle}
|
|
99
|
+
isExpanded={isDropdownOpen}
|
|
100
|
+
>
|
|
77
101
|
Dropdown
|
|
78
102
|
</MenuToggle>
|
|
79
103
|
)}
|
|
104
|
+
popperProps={{
|
|
105
|
+
appendTo: getAppendLocation()
|
|
106
|
+
}}
|
|
80
107
|
>
|
|
81
108
|
<DropdownList>
|
|
82
109
|
<DropdownItem value={0} key="action">
|
|
@@ -3,7 +3,7 @@ import progressStyle from '@patternfly/react-styles/css/components/Progress/prog
|
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
4
|
import { Tooltip, TooltipPosition } from '../Tooltip';
|
|
5
5
|
import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
|
|
6
|
-
import
|
|
6
|
+
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
|
|
7
7
|
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
|
|
8
8
|
import { AriaProps, ProgressBar } from './ProgressBar';
|
|
9
9
|
import { ProgressHelperText } from './ProgressHelperText';
|
|
@@ -61,7 +61,7 @@ export interface ProgressContainerProps extends Omit<React.HTMLProps<HTMLDivElem
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const variantToIcon = {
|
|
64
|
-
danger:
|
|
64
|
+
danger: ExclamationCircleIcon,
|
|
65
65
|
success: CheckCircleIcon,
|
|
66
66
|
warning: ExclamationTriangleIcon
|
|
67
67
|
};
|
package/src/components/Progress/__tests__/Generated/__snapshots__/ProgressContainer.test.tsx.snap
CHANGED
|
@@ -31,7 +31,7 @@ exports[`ProgressContainer should match snapshot (auto-generated) 1`] = `
|
|
|
31
31
|
width="1em"
|
|
32
32
|
>
|
|
33
33
|
<path
|
|
34
|
-
d="
|
|
34
|
+
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
|
|
35
35
|
/>
|
|
36
36
|
</svg>
|
|
37
37
|
</span>
|
|
@@ -298,7 +298,7 @@ exports[`Progress variant danger 1`] = `
|
|
|
298
298
|
width="1em"
|
|
299
299
|
>
|
|
300
300
|
<path
|
|
301
|
-
d="
|
|
301
|
+
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
|
|
302
302
|
/>
|
|
303
303
|
</svg>
|
|
304
304
|
</span>
|
|
@@ -3,6 +3,7 @@ import styles from '@patternfly/react-styles/css/components/Radio/radio';
|
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
4
|
import { PickOptional } from '../../helpers/typeUtils';
|
|
5
5
|
import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
|
|
6
|
+
import { getUniqueId } from '../../helpers/util';
|
|
6
7
|
|
|
7
8
|
export interface RadioProps
|
|
8
9
|
extends Omit<React.HTMLProps<HTMLInputElement>, 'disabled' | 'label' | 'onChange' | 'type'>,
|
|
@@ -39,6 +40,8 @@ export interface RadioProps
|
|
|
39
40
|
description?: React.ReactNode;
|
|
40
41
|
/** Body of the radio. */
|
|
41
42
|
body?: React.ReactNode;
|
|
43
|
+
/** Custom aria-describedby value for the radio input. If not provided and description is set, a unique ID will be generated automatically. */
|
|
44
|
+
'aria-describedby'?: string;
|
|
42
45
|
/** Sets the radio wrapper component to render. Defaults to "div". If set to "label", behaves the same as if isLabelWrapped prop was specified. */
|
|
43
46
|
component?: React.ElementType;
|
|
44
47
|
/** Value to overwrite the randomly generated data-ouia-component-id.*/
|
|
@@ -47,7 +50,7 @@ export interface RadioProps
|
|
|
47
50
|
ouiaSafe?: boolean;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
53
|
+
class Radio extends Component<RadioProps, { ouiaStateId: string; descriptionId: string }> {
|
|
51
54
|
static displayName = 'Radio';
|
|
52
55
|
static defaultProps: PickOptional<RadioProps> = {
|
|
53
56
|
className: '',
|
|
@@ -63,7 +66,8 @@ class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
|
63
66
|
console.error('Radio:', 'Radio requires an aria-label to be specified');
|
|
64
67
|
}
|
|
65
68
|
this.state = {
|
|
66
|
-
ouiaStateId: getDefaultOUIAId(Radio.displayName)
|
|
69
|
+
ouiaStateId: getDefaultOUIAId(Radio.displayName),
|
|
70
|
+
descriptionId: getUniqueId('pf-radio-description')
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
|
|
@@ -74,6 +78,7 @@ class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
|
74
78
|
render() {
|
|
75
79
|
const {
|
|
76
80
|
'aria-label': ariaLabel,
|
|
81
|
+
'aria-describedby': ariaDescribedBy,
|
|
77
82
|
checked,
|
|
78
83
|
className,
|
|
79
84
|
inputClassName,
|
|
@@ -98,6 +103,14 @@ class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
|
98
103
|
console.error('Radio:', 'id is required to make input accessible');
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
// Handle aria-describedby logic
|
|
107
|
+
let ariaDescribedByValue: string | undefined;
|
|
108
|
+
if (ariaDescribedBy !== undefined) {
|
|
109
|
+
ariaDescribedByValue = ariaDescribedBy === '' ? undefined : ariaDescribedBy;
|
|
110
|
+
} else if (description) {
|
|
111
|
+
ariaDescribedByValue = this.state.descriptionId;
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
const inputRendered = (
|
|
102
115
|
<input
|
|
103
116
|
{...props}
|
|
@@ -105,6 +118,7 @@ class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
|
105
118
|
type="radio"
|
|
106
119
|
onChange={this.handleChange}
|
|
107
120
|
aria-invalid={!isValid}
|
|
121
|
+
aria-describedby={ariaDescribedByValue}
|
|
108
122
|
disabled={isDisabled}
|
|
109
123
|
checked={checked || isChecked}
|
|
110
124
|
{...(checked === undefined && { defaultChecked })}
|
|
@@ -143,7 +157,11 @@ class Radio extends Component<RadioProps, { ouiaStateId: string }> {
|
|
|
143
157
|
{labelRendered}
|
|
144
158
|
</>
|
|
145
159
|
)}
|
|
146
|
-
{description &&
|
|
160
|
+
{description && (
|
|
161
|
+
<span id={this.state.descriptionId} className={css(styles.radioDescription)}>
|
|
162
|
+
{description}
|
|
163
|
+
</span>
|
|
164
|
+
)}
|
|
147
165
|
{body && <span className={css(styles.radioBody)}>{body}</span>}
|
|
148
166
|
</Component>
|
|
149
167
|
);
|
|
@@ -139,4 +139,48 @@ describe('Radio', () => {
|
|
|
139
139
|
expect(wrapper.children[0].tagName).toBe('LABEL');
|
|
140
140
|
expect(wrapper.children[1].tagName).toBe('INPUT');
|
|
141
141
|
});
|
|
142
|
+
|
|
143
|
+
test('Sets aria-describedby when description is provided', () => {
|
|
144
|
+
render(<Radio id="test-id" name="check" aria-label="test radio" description="test description" />);
|
|
145
|
+
|
|
146
|
+
const radio = screen.getByRole('radio');
|
|
147
|
+
const descriptionElement = screen.getByText('test description');
|
|
148
|
+
|
|
149
|
+
expect(radio).toHaveAttribute('aria-describedby', descriptionElement.id);
|
|
150
|
+
expect(descriptionElement).toHaveAttribute('id');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('Sets custom aria-describedby when provided', () => {
|
|
154
|
+
render(
|
|
155
|
+
<Radio
|
|
156
|
+
id="test-id"
|
|
157
|
+
name="check"
|
|
158
|
+
aria-label="test radio"
|
|
159
|
+
description="test description"
|
|
160
|
+
aria-describedby="custom-id"
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const radio = screen.getByRole('radio');
|
|
165
|
+
|
|
166
|
+
expect(radio).toHaveAttribute('aria-describedby', 'custom-id');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('Does not set aria-describedby when no description is provided', () => {
|
|
170
|
+
render(<Radio id="test-id" name="check" aria-label="test radio" />);
|
|
171
|
+
|
|
172
|
+
const radio = screen.getByRole('radio');
|
|
173
|
+
|
|
174
|
+
expect(radio).not.toHaveAttribute('aria-describedby');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('Does not set aria-describedby when description is provided but aria-describedby is empty string', () => {
|
|
178
|
+
render(
|
|
179
|
+
<Radio id="test-id" name="check" aria-label="test radio" description="test description" aria-describedby="" />
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const radio = screen.getByRole('radio');
|
|
183
|
+
|
|
184
|
+
expect(radio).not.toHaveAttribute('aria-describedby');
|
|
185
|
+
});
|
|
142
186
|
});
|