@patternfly/react-core 6.0.0-alpha.100 → 6.0.0-alpha.101

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 (134) hide show
  1. package/CHANGELOG.md +6 -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/ClipboardCopy/package.json +1 -1
  21. package/dist/dynamic/components/CodeBlock/package.json +1 -1
  22. package/dist/dynamic/components/Content/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/Drawer/package.json +1 -1
  28. package/dist/dynamic/components/Dropdown/package.json +1 -1
  29. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  30. package/dist/dynamic/components/EmptyState/package.json +1 -1
  31. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  32. package/dist/dynamic/components/FileUpload/package.json +1 -1
  33. package/dist/dynamic/components/Form/package.json +1 -1
  34. package/dist/dynamic/components/FormSelect/package.json +1 -1
  35. package/dist/dynamic/components/HelperText/package.json +1 -1
  36. package/dist/dynamic/components/Hint/package.json +1 -1
  37. package/dist/dynamic/components/Icon/package.json +1 -1
  38. package/dist/dynamic/components/InputGroup/package.json +1 -1
  39. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  40. package/dist/dynamic/components/Label/package.json +1 -1
  41. package/dist/dynamic/components/List/package.json +1 -1
  42. package/dist/dynamic/components/LoginPage/package.json +1 -1
  43. package/dist/dynamic/components/Masthead/package.json +1 -1
  44. package/dist/dynamic/components/Menu/package.json +1 -1
  45. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  46. package/dist/dynamic/components/Modal/package.json +1 -1
  47. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  48. package/dist/dynamic/components/Nav/package.json +1 -1
  49. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  50. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  51. package/dist/dynamic/components/NumberInput/package.json +1 -1
  52. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  53. package/dist/dynamic/components/Page/package.json +1 -1
  54. package/dist/dynamic/components/Pagination/package.json +1 -1
  55. package/dist/dynamic/components/Panel/package.json +1 -1
  56. package/dist/dynamic/components/Popover/package.json +1 -1
  57. package/dist/dynamic/components/Progress/package.json +1 -1
  58. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  59. package/dist/dynamic/components/Radio/package.json +1 -1
  60. package/dist/dynamic/components/SearchInput/package.json +1 -1
  61. package/dist/dynamic/components/Select/package.json +1 -1
  62. package/dist/dynamic/components/Sidebar/package.json +1 -1
  63. package/dist/dynamic/components/SimpleList/package.json +1 -1
  64. package/dist/dynamic/components/Skeleton/package.json +1 -1
  65. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  66. package/dist/dynamic/components/Slider/package.json +1 -1
  67. package/dist/dynamic/components/Spinner/package.json +1 -1
  68. package/dist/dynamic/components/Switch/package.json +1 -1
  69. package/dist/dynamic/components/Tabs/package.json +1 -1
  70. package/dist/dynamic/components/TextArea/package.json +1 -1
  71. package/dist/dynamic/components/TextInput/package.json +1 -1
  72. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  73. package/dist/dynamic/components/Tile/package.json +1 -1
  74. package/dist/dynamic/components/TimePicker/package.json +1 -1
  75. package/dist/dynamic/components/Timestamp/package.json +1 -1
  76. package/dist/dynamic/components/Title/package.json +1 -1
  77. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  78. package/dist/dynamic/components/Toolbar/package.json +1 -1
  79. package/dist/dynamic/components/Tooltip/package.json +1 -1
  80. package/dist/dynamic/components/TreeView/package.json +1 -1
  81. package/dist/dynamic/components/Truncate/package.json +1 -1
  82. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  83. package/dist/dynamic/components/Wizard/package.json +1 -1
  84. package/dist/dynamic/deprecated/components/Chip/package.json +1 -1
  85. package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/package.json +1 -1
  90. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  91. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  92. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  93. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  94. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  95. package/dist/dynamic/helpers/constants/package.json +1 -1
  96. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  97. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  98. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  99. package/dist/dynamic/helpers/package.json +1 -1
  100. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  101. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  102. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  103. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  104. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  105. package/dist/dynamic/helpers/util/package.json +1 -1
  106. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  107. package/dist/dynamic/layouts/Flex/package.json +1 -1
  108. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  109. package/dist/dynamic/layouts/Grid/package.json +1 -1
  110. package/dist/dynamic/layouts/Level/package.json +1 -1
  111. package/dist/dynamic/layouts/Split/package.json +1 -1
  112. package/dist/dynamic/layouts/Stack/package.json +1 -1
  113. package/dist/dynamic/styles/package.json +1 -1
  114. package/dist/esm/components/Card/CardHeader.d.ts +16 -10
  115. package/dist/esm/components/Card/CardHeader.d.ts.map +1 -1
  116. package/dist/esm/components/Card/CardHeader.js +25 -29
  117. package/dist/esm/components/Card/CardHeader.js.map +1 -1
  118. package/dist/js/components/Card/CardHeader.d.ts +16 -10
  119. package/dist/js/components/Card/CardHeader.d.ts.map +1 -1
  120. package/dist/js/components/Card/CardHeader.js +25 -29
  121. package/dist/js/components/Card/CardHeader.js.map +1 -1
  122. package/dist/umd/assets/{output-BFvJVM-B.css → output-RbvW58uu.css} +3237 -3222
  123. package/dist/umd/react-core.min.js +3 -3
  124. package/helpers/package.json +1 -1
  125. package/layouts/package.json +1 -1
  126. package/next/package.json +1 -1
  127. package/package.json +6 -6
  128. package/src/components/Card/CardHeader.tsx +115 -93
  129. package/src/components/Card/__tests__/CardHeader.test.tsx +330 -1
  130. package/src/components/Card/examples/CardClickable.tsx +11 -21
  131. package/src/components/Card/examples/CardClickableSelectable.tsx +6 -3
  132. package/src/components/Card/examples/CardSelectable.tsx +6 -3
  133. package/src/components/Card/examples/CardSingleSelectable.tsx +6 -3
  134. package/dist/styles/assets/pficon/pf-v5-pficon.woff2 +0 -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":"6.0.0-alpha.99","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":"6.0.0-alpha.100","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":"6.0.0-alpha.99","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":"6.0.0-alpha.100","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.0.0-alpha.99","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":"6.0.0-alpha.100","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "6.0.0-alpha.100",
3
+ "version": "6.0.0-alpha.101",
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",
@@ -47,15 +47,15 @@
47
47
  "clean:exports": "node scripts/cleanDistExports.js"
48
48
  },
49
49
  "dependencies": {
50
- "@patternfly/react-icons": "^6.0.0-alpha.35",
51
- "@patternfly/react-styles": "^6.0.0-alpha.34",
52
- "@patternfly/react-tokens": "^6.0.0-alpha.34",
50
+ "@patternfly/react-icons": "^6.0.0-alpha.36",
51
+ "@patternfly/react-styles": "^6.0.0-alpha.35",
52
+ "@patternfly/react-tokens": "^6.0.0-alpha.35",
53
53
  "focus-trap": "7.5.4",
54
54
  "react-dropzone": "^14.2.3",
55
55
  "tslib": "^2.6.2"
56
56
  },
57
57
  "devDependencies": {
58
- "@patternfly/patternfly": "6.0.0-alpha.205",
58
+ "@patternfly/patternfly": "6.0.0-alpha.210",
59
59
  "case-anything": "^2.1.13",
60
60
  "css": "^3.0.0",
61
61
  "fs-extra": "^11.2.0"
@@ -64,5 +64,5 @@
64
64
  "react": "^17 || ^18",
65
65
  "react-dom": "^17 || ^18"
66
66
  },
67
- "gitHead": "aee9a75f90293e6533c2d7b59d7cf099b5f3d506"
67
+ "gitHead": "11fa597623c93a0cab58d80a1768ca16bd903759"
68
68
  }
@@ -26,23 +26,29 @@ export interface CardHeaderSelectableActionsObject {
26
26
  hasNoOffset?: boolean;
27
27
  /** Additional classes added to the selectable actions wrapper */
28
28
  className?: string;
29
- /** ID passed to the selectable or clickable input */
30
- selectableActionId: string;
31
- /** Adds an accessible label to the selectable or clickable input */
29
+ /** Custom ID passed to the selectable card's input or a clickable-only card's button/anchor.
30
+ * If omitted, a random unique ID will be assigned to a selectable card's input. */
31
+ selectableActionId?: string;
32
+ /** Adds an accessible name to the input of a selectable card or clickable button/anchor of a clickable-only card.
33
+ * This or selectableActionAriaLabelledby is required for clickable-only cards.
34
+ */
32
35
  selectableActionAriaLabel?: string;
33
- /** Adds an accessible label to the selectable or clickable input by passing in a
34
- * space separated list of id's.
36
+ /** A single or list of space-delimited ID's that provide an accessible name to the input of a selectable card
37
+ * or clickable button/anchor of a clickable-only card. This or selectableActionAriaLabelledby is required
38
+ * for clickable-only cards.
35
39
  */
36
40
  selectableActionAriaLabelledby?: string;
37
41
  /** Callback for when a selectable card input changes */
38
42
  onChange?: (event: React.FormEvent<HTMLInputElement>, checked: boolean) => void;
39
- /** Action to call when clickable card is clicked */
40
- onClickAction?: (event: React.FormEvent<HTMLInputElement> | React.MouseEvent) => void;
41
- /** Link to navigate to when clickable card is clicked */
43
+ /** Action to call when a clickable-only card is clicked. This cannot be combined with the to prop. */
44
+ onClickAction?: (event: React.MouseEvent) => void;
45
+ /** Link to navigate to when a clickable-only card is clicked. This cannot be combined with the onClickAction prop. */
42
46
  to?: string;
43
- /** Flag to indicate whether a clickable card's link should open in a new tab/window. */
47
+ /** Additional props spread to a selectable card input or clickable-only card's button/anchor. */
48
+ selectableActionProps?: any;
49
+ /** Flag to indicate whether a clickable-only card's link should open in a new tab/window. */
44
50
  isExternalLink?: boolean;
45
- /** Name for the input element of a clickable or selectable card. */
51
+ /** Name for the input element of a selectable card. */
46
52
  name?: string;
47
53
  /** @deprecated Flag indicating whether the selectable card input is checked. We recommend using
48
54
  * the isSelected prop on the card component instead.
@@ -79,101 +85,117 @@ export const CardHeader: React.FunctionComponent<CardHeaderProps> = ({
79
85
  toggleButtonProps,
80
86
  isToggleRightAligned,
81
87
  ...props
82
- }: CardHeaderProps) => (
83
- <CardContext.Consumer>
84
- {({ cardId, isClickable, isSelectable, isSelected, isClicked, isDisabled: isCardDisabled }) => {
85
- const cardHeaderToggle = (
86
- <div className={css(styles.cardHeaderToggle)}>
87
- <Button
88
- variant="plain"
89
- type="button"
90
- onClick={(evt) => {
91
- onExpand(evt, cardId);
92
- }}
93
- {...toggleButtonProps}
94
- icon={
95
- <span className={css(styles.cardHeaderToggleIcon)}>
96
- <AngleRightIcon aria-hidden="true" />
97
- </span>
98
- }
99
- />
100
- </div>
101
- );
88
+ }: CardHeaderProps) => {
89
+ const uniqueId = React.useId();
102
90
 
103
- const isClickableOrSelectableOnly = (isClickable && !isSelectable) || (isSelectable && !isClickable);
104
- if (actions?.actions && isClickableOrSelectableOnly) {
105
- // eslint-disable-next-line no-console
106
- console.warn(
107
- `${
108
- isClickable ? 'Clickable' : 'Selectable'
109
- } only cards should not contain any other actions. If you wish to include additional actions, use a clickable and selectable card.`
91
+ return (
92
+ <CardContext.Consumer>
93
+ {({ cardId, isClickable, isSelectable, isSelected, isDisabled: isCardDisabled }) => {
94
+ const cardHeaderToggle = (
95
+ <div className={css(styles.cardHeaderToggle)}>
96
+ <Button
97
+ variant="plain"
98
+ type="button"
99
+ onClick={(evt) => {
100
+ onExpand(evt, cardId);
101
+ }}
102
+ {...toggleButtonProps}
103
+ icon={
104
+ <span className={css(styles.cardHeaderToggleIcon)}>
105
+ <AngleRightIcon aria-hidden="true" />
106
+ </span>
107
+ }
108
+ />
109
+ </div>
110
110
  );
111
- }
112
111
 
113
- const handleActionClick = (event: React.FormEvent<HTMLInputElement> | React.MouseEvent) => {
114
- if (selectableActions?.onClickAction) {
115
- selectableActions.onClickAction(event);
116
- } else if (selectableActions?.to) {
117
- window.open(selectableActions.to, selectableActions.isExternalLink ? '_blank' : '_self');
112
+ const isClickableOrSelectableOnly = (isClickable && !isSelectable) || (isSelectable && !isClickable);
113
+ if (actions?.actions && isClickableOrSelectableOnly) {
114
+ // eslint-disable-next-line no-console
115
+ console.error(
116
+ `Card: ${
117
+ isClickable ? 'Clickable' : 'Selectable'
118
+ } only cards should not contain any other actions. If you wish to include additional actions, use a clickable and selectable card.`
119
+ );
120
+ }
121
+
122
+ const isClickableOnlyCard = isClickable && !isSelectable;
123
+ if (
124
+ (isClickableOnlyCard || isSelectable) &&
125
+ !selectableActions?.selectableActionAriaLabel &&
126
+ !selectableActions?.selectableActionAriaLabelledby
127
+ ) {
128
+ // eslint-disable-next-line no-console
129
+ console.error(
130
+ `Card: ${isClickableOnlyCard ? 'Clickable-only cards' : 'Cards with a selectable input'} must have either the selectableActions.selectableActionAriaLabel or selectableActions.selectableActionAriaLabelledby prop passed in order to provide an accessible name to the clickable element.`
131
+ );
118
132
  }
119
- };
120
133
 
121
- const getClickableSelectableProps = () => {
122
- const baseProps = {
134
+ const SelectableCardInput = selectableActions?.variant === 'single' ? Radio : Checkbox;
135
+ const getSelectableProps = () => ({
123
136
  className: 'pf-m-standalone',
124
- inputClassName: isClickable && !isSelectable && 'pf-v6-screen-reader',
125
137
  label: <></>,
126
138
  'aria-label': selectableActions.selectableActionAriaLabel,
127
139
  'aria-labelledby': selectableActions.selectableActionAriaLabelledby,
128
- id: selectableActions.selectableActionId,
140
+ id: selectableActions.selectableActionId ?? `card-selectable-${uniqueId}`,
129
141
  name: selectableActions.name,
130
- isDisabled: isCardDisabled
131
- };
132
- const isSelectableInputChecked = selectableActions.isChecked ?? isSelected;
142
+ isDisabled: isCardDisabled,
143
+ onChange: selectableActions.onChange,
144
+ isChecked: selectableActions.isChecked ?? isSelected,
145
+ ...selectableActions.selectableActionProps
146
+ });
133
147
 
134
- if (isClickable && !isSelectable) {
135
- return {
136
- ...baseProps,
137
- onClick: handleActionClick,
138
- isChecked: isClicked
148
+ const isClickableLinkCard = selectableActions?.to !== undefined;
149
+ const ClickableCardComponent = isClickableLinkCard ? 'a' : 'button';
150
+ const getClickableProps = () => {
151
+ const isDisabledLinkCard = isCardDisabled && isClickableLinkCard;
152
+ const baseProps = {
153
+ className: css('pf-v6-c-card__clickable-action', isDisabledLinkCard && styles.modifiers.disabled),
154
+ id: selectableActions.selectableActionId,
155
+ 'aria-label': selectableActions.selectableActionAriaLabel,
156
+ 'aria-labelledby': selectableActions.selectableActionAriaLabelledby,
157
+ ...selectableActions.selectableActionProps
139
158
  };
140
- }
141
- if (isSelectable) {
142
- return { ...baseProps, onChange: selectableActions.onChange, isChecked: isSelectableInputChecked };
143
- }
144
159
 
145
- return baseProps;
146
- };
160
+ if (isClickableLinkCard) {
161
+ return {
162
+ ...baseProps,
163
+ href: selectableActions.to,
164
+ ...(isCardDisabled && { tabIndex: -1, 'aria-disabled': true }),
165
+ ...(selectableActions.isExternalLink && { target: '_blank' })
166
+ };
167
+ }
168
+
169
+ return { ...baseProps, type: 'button', disabled: isCardDisabled, onClick: selectableActions.onClickAction };
170
+ };
147
171
 
148
- return (
149
- <div
150
- className={css(styles.cardHeader, isToggleRightAligned && styles.modifiers.toggleRight, className)}
151
- id={id}
152
- {...props}
153
- >
154
- {onExpand && !isToggleRightAligned && cardHeaderToggle}
155
- {(actions || (selectableActions && (isClickable || isSelectable))) && (
156
- <CardActions
157
- className={actions?.className}
158
- hasNoOffset={actions?.hasNoOffset || selectableActions?.hasNoOffset}
159
- >
160
- {actions?.actions}
161
- {selectableActions && (isClickable || isSelectable) && (
162
- <CardSelectableActions className={selectableActions?.className}>
163
- {selectableActions?.variant === 'single' || (isClickable && !isSelectable) ? (
164
- <Radio {...getClickableSelectableProps()} />
165
- ) : (
166
- <Checkbox {...getClickableSelectableProps()} />
167
- )}
168
- </CardSelectableActions>
169
- )}
170
- </CardActions>
171
- )}
172
- {children && <CardHeaderMain>{children}</CardHeaderMain>}
173
- {onExpand && isToggleRightAligned && cardHeaderToggle}
174
- </div>
175
- );
176
- }}
177
- </CardContext.Consumer>
178
- );
172
+ return (
173
+ <div
174
+ className={css(styles.cardHeader, isToggleRightAligned && styles.modifiers.toggleRight, className)}
175
+ id={id}
176
+ {...props}
177
+ >
178
+ {onExpand && !isToggleRightAligned && cardHeaderToggle}
179
+ {(actions || (selectableActions && (isClickable || isSelectable))) && (
180
+ <CardActions
181
+ className={actions?.className}
182
+ hasNoOffset={actions?.hasNoOffset || selectableActions?.hasNoOffset}
183
+ >
184
+ {actions?.actions}
185
+ {selectableActions && (isClickable || isSelectable) && (
186
+ <CardSelectableActions className={selectableActions?.className}>
187
+ {isSelectable && <SelectableCardInput {...getSelectableProps()} />}
188
+ {isClickableOnlyCard && <ClickableCardComponent {...getClickableProps()} />}
189
+ </CardSelectableActions>
190
+ )}
191
+ </CardActions>
192
+ )}
193
+ {children && <CardHeaderMain>{children}</CardHeaderMain>}
194
+ {onExpand && isToggleRightAligned && cardHeaderToggle}
195
+ </div>
196
+ );
197
+ }}
198
+ </CardContext.Consumer>
199
+ );
200
+ };
179
201
  CardHeader.displayName = 'CardHeader';
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
- import { render } from '@testing-library/react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import { CardHeader } from '../CardHeader';
5
+ import { CardContext } from '../Card';
4
6
 
5
7
  describe('CardHeader', () => {
6
8
  test('onExpand adds the toggle button', () => {
@@ -13,3 +15,330 @@ describe('CardHeader', () => {
13
15
  expect(asFragment()).toMatchSnapshot();
14
16
  });
15
17
  });
18
+
19
+ // TODO: check if hasNoOffset for actions/selectableActions and className for selectableActions render
20
+ describe('Card with actions and selectableActions', () => {
21
+ test('Does not render selectable or clickable actions by default', () => {
22
+ render(<CardHeader>Test</CardHeader>);
23
+
24
+ expect(screen.queryByRole('checkbox')).not.toBeInTheDocument();
25
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
26
+ expect(screen.queryByRole('link')).not.toBeInTheDocument();
27
+ });
28
+
29
+ test('Renders checkbox input when Card has isSelectable and selectableActions.variant = "multiple"', () => {
30
+ render(
31
+ <CardContext.Provider value={{ isSelectable: true }}>
32
+ <CardHeader selectableActions={{ variant: 'multiple' }}>Test</CardHeader>
33
+ </CardContext.Provider>
34
+ );
35
+
36
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
37
+ });
38
+ test('Renders checkbox input by default when Card has isSelectable and selectableActions is passed with any props', () => {
39
+ render(
40
+ <CardContext.Provider value={{ isSelectable: true }}>
41
+ <CardHeader selectableActions={{ className: 'test' }}>Test</CardHeader>
42
+ </CardContext.Provider>
43
+ );
44
+
45
+ expect(screen.getByRole('checkbox')).toBeInTheDocument();
46
+ });
47
+ test('Renders radio input when Card has isSelectable and selectableActions.variant = "single"', () => {
48
+ render(
49
+ <CardContext.Provider value={{ isSelectable: true }}>
50
+ <CardHeader selectableActions={{ variant: 'single' }}>Test</CardHeader>
51
+ </CardContext.Provider>
52
+ );
53
+
54
+ expect(screen.getByRole('radio')).toBeInTheDocument();
55
+ });
56
+ test('Renders default ID for selectable card input when selectableActions.selectableActionId is not passed', () => {
57
+ render(
58
+ <CardContext.Provider value={{ isSelectable: true }}>
59
+ <CardHeader selectableActions={{ variant: 'multiple' }}>Test</CardHeader>
60
+ </CardContext.Provider>
61
+ );
62
+
63
+ expect(screen.getByRole('checkbox').getAttribute('id')).toMatch(/^card-selectable/);
64
+ });
65
+ test('Renders custom ID for selectable card input when selectableActions.selectableActionId is passed', () => {
66
+ render(
67
+ <CardContext.Provider value={{ isSelectable: true }}>
68
+ <CardHeader selectableActions={{ variant: 'multiple', selectableActionId: 'custom-id' }}>Test</CardHeader>
69
+ </CardContext.Provider>
70
+ );
71
+
72
+ expect(screen.getByRole('checkbox')).toHaveAttribute('id', 'custom-id');
73
+ });
74
+ test('Renders name on a selectable card input', () => {
75
+ render(
76
+ <CardContext.Provider value={{ isSelectable: true }}>
77
+ <CardHeader selectableActions={{ variant: 'single', name: 'test' }}>Test</CardHeader>
78
+ </CardContext.Provider>
79
+ );
80
+
81
+ expect(screen.getByRole('radio')).toHaveAttribute('name', 'test');
82
+ });
83
+ test('Renders a selectable card input as not checked by default', () => {
84
+ render(
85
+ <CardContext.Provider value={{ isSelectable: true }}>
86
+ <CardHeader selectableActions={{ variant: 'multiple' }}>Test</CardHeader>
87
+ </CardContext.Provider>
88
+ );
89
+
90
+ expect(screen.getByRole('checkbox')).not.toBeChecked();
91
+ });
92
+ test('Renders a selectable card input as checked when Card has isSelected = true', () => {
93
+ render(
94
+ <CardContext.Provider value={{ isSelectable: true, isSelected: true }}>
95
+ <CardHeader selectableActions={{ variant: 'multiple' }}>Test</CardHeader>
96
+ </CardContext.Provider>
97
+ );
98
+
99
+ expect(screen.getByRole('checkbox')).toBeChecked();
100
+ });
101
+ test('Renders a selectable card input as checked when deprecated selectableActions.isChecked is true', () => {
102
+ render(
103
+ <CardContext.Provider value={{ isSelectable: true }}>
104
+ <CardHeader selectableActions={{ variant: 'multiple', isChecked: true }}>Test</CardHeader>
105
+ </CardContext.Provider>
106
+ );
107
+
108
+ expect(screen.getByRole('checkbox')).toBeChecked();
109
+ });
110
+ test('Renders additional props on selectable card input when selectableActions.selectableActionProps is passed', () => {
111
+ render(
112
+ <CardContext.Provider value={{ isSelectable: true }}>
113
+ <CardHeader selectableActions={{ variant: 'multiple', selectableActionProps: { style: { color: 'red' } } }}>
114
+ Test
115
+ </CardHeader>
116
+ </CardContext.Provider>
117
+ );
118
+
119
+ expect(screen.getByRole('checkbox')).toHaveAttribute('style');
120
+ });
121
+
122
+ test('Does not call onChange for selectable card when card is not clicked', async () => {
123
+ const user = userEvent.setup();
124
+ const onChangeMock = jest.fn();
125
+
126
+ render(
127
+ <>
128
+ <button>Test click</button>
129
+ <CardContext.Provider value={{ isSelectable: true }}>
130
+ <CardHeader selectableActions={{ variant: 'multiple', onChange: onChangeMock }}>Test</CardHeader>
131
+ </CardContext.Provider>
132
+ </>
133
+ );
134
+
135
+ await user.click(screen.getByRole('button', { name: 'Test click' }));
136
+ expect(onChangeMock).not.toHaveBeenCalled();
137
+ });
138
+ test('Calls onChange for selectable card when card is clicked', async () => {
139
+ const user = userEvent.setup();
140
+ const onChangeMock = jest.fn();
141
+
142
+ render(
143
+ <CardContext.Provider value={{ isSelectable: true }}>
144
+ <CardHeader selectableActions={{ variant: 'multiple', onChange: onChangeMock }}>Test</CardHeader>
145
+ </CardContext.Provider>
146
+ );
147
+
148
+ const labelElement = document.querySelector('label');
149
+ await user.click(labelElement as HTMLElement);
150
+ expect(onChangeMock).toHaveBeenCalled();
151
+ });
152
+
153
+ test('Renders button element when Card has isClickable and selectableActions.onClickAction is passed"', () => {
154
+ render(
155
+ <CardContext.Provider value={{ isClickable: true }}>
156
+ <CardHeader selectableActions={{ onClickAction: () => {} }}>Test</CardHeader>
157
+ </CardContext.Provider>
158
+ );
159
+
160
+ expect(screen.getByRole('button')).toBeInTheDocument();
161
+ });
162
+ test('Renders anchor element when Card has isClickable and selectableActions.to is passed"', () => {
163
+ render(
164
+ <CardContext.Provider value={{ isClickable: true }}>
165
+ <CardHeader selectableActions={{ to: '#' }}>Test</CardHeader>
166
+ </CardContext.Provider>
167
+ );
168
+
169
+ expect(screen.getByRole('link')).toBeInTheDocument();
170
+ });
171
+ test('Renders href when Card has isClickable and selectableActions.to is passed"', () => {
172
+ render(
173
+ <CardContext.Provider value={{ isClickable: true }}>
174
+ <CardHeader selectableActions={{ to: '#some-id' }}>Test</CardHeader>
175
+ </CardContext.Provider>
176
+ );
177
+
178
+ expect(screen.getByRole('link')).toHaveAttribute('href', '#some-id');
179
+ });
180
+ test('Does not render ID on clickable element when selectableActions.selectableActionId is not passed"', () => {
181
+ render(
182
+ <CardContext.Provider value={{ isClickable: true }}>
183
+ <CardHeader selectableActions={{ to: '#' }}>Test</CardHeader>
184
+ </CardContext.Provider>
185
+ );
186
+
187
+ expect(screen.getByRole('link')).not.toHaveAttribute('id');
188
+ });
189
+ test('Renders ID on clickable element when selectableActions.selectableActionId is passed"', () => {
190
+ render(
191
+ <CardContext.Provider value={{ isClickable: true }}>
192
+ <CardHeader selectableActions={{ to: '#', selectableActionId: 'custom-id' }}>Test</CardHeader>
193
+ </CardContext.Provider>
194
+ );
195
+
196
+ expect(screen.getByRole('link')).toHaveAttribute('id', 'custom-id');
197
+ });
198
+ test('Renders additional props on clickable element when selectableActions.selectableActionProps is passed', () => {
199
+ render(
200
+ <CardContext.Provider value={{ isClickable: true }}>
201
+ <CardHeader selectableActions={{ to: '#', selectableActionProps: { style: { color: 'red' } } }}>
202
+ Test
203
+ </CardHeader>
204
+ </CardContext.Provider>
205
+ );
206
+
207
+ expect(screen.getByRole('link')).toHaveAttribute('style');
208
+ });
209
+ test('Does not render target="_blank" on clickable card anchor element by default"', () => {
210
+ render(
211
+ <CardContext.Provider value={{ isClickable: true }}>
212
+ <CardHeader selectableActions={{ to: '#' }}>Test</CardHeader>
213
+ </CardContext.Provider>
214
+ );
215
+
216
+ expect(screen.getByRole('link')).not.toHaveAttribute('target');
217
+ });
218
+ test('Renders target="_blank" on clickable card anchor element when selectableActions.isExternalLink is true"', () => {
219
+ render(
220
+ <CardContext.Provider value={{ isClickable: true }}>
221
+ <CardHeader selectableActions={{ to: '#', isExternalLink: true }}>Test</CardHeader>
222
+ </CardContext.Provider>
223
+ );
224
+
225
+ expect(screen.getByRole('link')).toHaveAttribute('target', '_blank');
226
+ });
227
+ test('Calls onClickAction for clickable card when card is clicked', async () => {
228
+ const user = userEvent.setup();
229
+ const onClickMock = jest.fn();
230
+
231
+ render(
232
+ <CardContext.Provider value={{ isClickable: true }}>
233
+ <CardHeader selectableActions={{ onClickAction: onClickMock }}>Test</CardHeader>
234
+ </CardContext.Provider>
235
+ );
236
+
237
+ await user.click(screen.getByRole('button'));
238
+ expect(onClickMock).toHaveBeenCalled();
239
+ });
240
+
241
+ test('Throws error when Card is either clickable or selectable only and has actions prop passed"', () => {
242
+ const errorMock = jest.fn() as any;
243
+ global.console = { error: errorMock } as any;
244
+ render(
245
+ <CardContext.Provider value={{ isClickable: true }}>
246
+ <CardHeader
247
+ actions={{ actions: <button>Toggle</button> }}
248
+ selectableActions={{ to: '#', selectableActionAriaLabel: 'Link' }}
249
+ >
250
+ Test
251
+ </CardHeader>
252
+ </CardContext.Provider>
253
+ );
254
+
255
+ expect(errorMock).toHaveBeenCalled();
256
+ });
257
+ test('Does not throw error when Card is both clickable and selectable and has actions prop passed"', () => {
258
+ const errorMock = jest.fn() as any;
259
+ global.console = { error: errorMock } as any;
260
+ render(
261
+ <CardContext.Provider value={{ isClickable: true, isSelectable: true }}>
262
+ <CardHeader
263
+ actions={{ actions: <button>Toggle</button> }}
264
+ selectableActions={{ to: '#', selectableActionAriaLabel: 'Link' }}
265
+ >
266
+ Test
267
+ </CardHeader>
268
+ </CardContext.Provider>
269
+ );
270
+
271
+ expect(errorMock).not.toHaveBeenCalled();
272
+ });
273
+ test('Throws error when Card has isClickable and selectableActions.selectableActionAriaLabel or selectableActions.selectableActionAriaLabelledby is not passed"', () => {
274
+ const errorMock = jest.fn() as any;
275
+ global.console = { error: errorMock } as any;
276
+ render(
277
+ <CardContext.Provider value={{ isClickable: true }}>
278
+ <CardHeader selectableActions={{ to: '#' }}>Test</CardHeader>
279
+ </CardContext.Provider>
280
+ );
281
+
282
+ expect(errorMock).toHaveBeenCalled();
283
+ });
284
+ test('Throws error when Card has isSelectable and selectableActions.selectableActionAriaLabel or selectableActions.selectableActionAriaLabelledby is not passed"', () => {
285
+ const errorMock = jest.fn() as any;
286
+ global.console = { error: errorMock } as any;
287
+ render(
288
+ <CardContext.Provider value={{ isSelectable: true }}>
289
+ <CardHeader selectableActions={{ variant: 'multiple' }}>Test</CardHeader>
290
+ </CardContext.Provider>
291
+ );
292
+
293
+ expect(errorMock).toHaveBeenCalled();
294
+ });
295
+
296
+ test('Renders accessible name on selectable card input when selectableActions.selectableActionAriaLabel is passed', () => {
297
+ const accessibleName = 'Accessible name';
298
+ render(
299
+ <CardContext.Provider value={{ isSelectable: true }}>
300
+ <CardHeader selectableActions={{ variant: 'multiple', selectableActionAriaLabel: accessibleName }}>
301
+ Test
302
+ </CardHeader>
303
+ </CardContext.Provider>
304
+ );
305
+
306
+ expect(screen.getByRole('checkbox')).toHaveAccessibleName(accessibleName);
307
+ });
308
+ test('Renders accessible name on selectable card input when selectableActions.selectableActionAriaLabelledby is passed', () => {
309
+ const labelingId = 'labelling-id';
310
+ const accessibleName = 'Accessible name';
311
+ render(
312
+ <CardContext.Provider value={{ isSelectable: true }}>
313
+ <CardHeader selectableActions={{ variant: 'multiple', selectableActionAriaLabelledby: labelingId }}>
314
+ <div id={labelingId}>{accessibleName}</div>
315
+ </CardHeader>
316
+ </CardContext.Provider>
317
+ );
318
+
319
+ expect(screen.getByRole('checkbox')).toHaveAccessibleName(accessibleName);
320
+ });
321
+ test('Renders accessible name on clickable card clickable element when selectableActions.selectableActionAriaLabel is passed', () => {
322
+ const accessibleName = 'Accessible name';
323
+ render(
324
+ <CardContext.Provider value={{ isClickable: true }}>
325
+ <CardHeader selectableActions={{ to: '#', selectableActionAriaLabel: accessibleName }}>Test</CardHeader>
326
+ </CardContext.Provider>
327
+ );
328
+
329
+ expect(screen.getByRole('link')).toHaveAccessibleName(accessibleName);
330
+ });
331
+ test('Renders accessible name on clickable card clickable element when selectableActions.selectableActionAriaLabelledby is passed', () => {
332
+ const labelingId = 'labelling-id';
333
+ const accessibleName = 'Accessible name';
334
+ render(
335
+ <CardContext.Provider value={{ isClickable: true }}>
336
+ <CardHeader selectableActions={{ to: '#', selectableActionAriaLabelledby: labelingId }}>
337
+ <div id={labelingId}>{accessibleName}</div>
338
+ </CardHeader>
339
+ </CardContext.Provider>
340
+ );
341
+
342
+ expect(screen.getByRole('link')).toHaveAccessibleName(accessibleName);
343
+ });
344
+ });