@patternfly/react-core 6.5.0-prerelease.2 → 6.5.0-prerelease.4

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 (194) hide show
  1. package/CHANGELOG.md +17 -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/Compass/package.json +1 -1
  23. package/dist/dynamic/components/Content/package.json +1 -1
  24. package/dist/dynamic/components/DataList/package.json +1 -1
  25. package/dist/dynamic/components/DatePicker/package.json +1 -1
  26. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  27. package/dist/dynamic/components/Divider/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/TextArea/package.json +1 -1
  72. package/dist/dynamic/components/TextInput/package.json +1 -1
  73. package/dist/dynamic/components/TextInputGroup/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/Tile/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/package.json +1 -1
  91. package/dist/dynamic/helpers/AnimationsProvider/AnimationsProvider/package.json +1 -1
  92. package/dist/dynamic/helpers/AnimationsProvider/package.json +1 -1
  93. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  94. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  95. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  96. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  97. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  98. package/dist/dynamic/helpers/constants/package.json +1 -1
  99. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  100. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  101. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  102. package/dist/dynamic/helpers/package.json +1 -1
  103. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  104. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  105. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  106. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  107. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  108. package/dist/dynamic/helpers/util/package.json +1 -1
  109. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  110. package/dist/dynamic/layouts/Flex/package.json +1 -1
  111. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  112. package/dist/dynamic/layouts/Grid/package.json +1 -1
  113. package/dist/dynamic/layouts/Level/package.json +1 -1
  114. package/dist/dynamic/layouts/Split/package.json +1 -1
  115. package/dist/dynamic/layouts/Stack/package.json +1 -1
  116. package/dist/dynamic/styles/package.json +1 -1
  117. package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts +9 -2
  118. package/dist/esm/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
  119. package/dist/esm/components/ExpandableSection/ExpandableSection.js +5 -3
  120. package/dist/esm/components/ExpandableSection/ExpandableSection.js.map +1 -1
  121. package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts +5 -0
  122. package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
  123. package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js +3 -2
  124. package/dist/esm/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
  125. package/dist/esm/components/Page/Page.d.ts.map +1 -1
  126. package/dist/esm/components/Page/Page.js +1 -0
  127. package/dist/esm/components/Page/Page.js.map +1 -1
  128. package/dist/esm/components/Page/PageContext.d.ts +1 -0
  129. package/dist/esm/components/Page/PageContext.d.ts.map +1 -1
  130. package/dist/esm/components/Page/PageContext.js +1 -0
  131. package/dist/esm/components/Page/PageContext.js.map +1 -1
  132. package/dist/esm/components/Page/PageSidebar.js +2 -2
  133. package/dist/esm/components/Page/PageSidebar.js.map +1 -1
  134. package/dist/esm/components/Progress/Progress.d.ts +2 -0
  135. package/dist/esm/components/Progress/Progress.d.ts.map +1 -1
  136. package/dist/esm/components/Progress/Progress.js +2 -2
  137. package/dist/esm/components/Progress/Progress.js.map +1 -1
  138. package/dist/esm/components/Progress/ProgressContainer.d.ts +2 -0
  139. package/dist/esm/components/Progress/ProgressContainer.d.ts.map +1 -1
  140. package/dist/esm/components/Progress/ProgressContainer.js +2 -2
  141. package/dist/esm/components/Progress/ProgressContainer.js.map +1 -1
  142. package/dist/js/components/ExpandableSection/ExpandableSection.d.ts +9 -2
  143. package/dist/js/components/ExpandableSection/ExpandableSection.d.ts.map +1 -1
  144. package/dist/js/components/ExpandableSection/ExpandableSection.js +5 -3
  145. package/dist/js/components/ExpandableSection/ExpandableSection.js.map +1 -1
  146. package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts +5 -0
  147. package/dist/js/components/ExpandableSection/ExpandableSectionToggle.d.ts.map +1 -1
  148. package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js +3 -2
  149. package/dist/js/components/ExpandableSection/ExpandableSectionToggle.js.map +1 -1
  150. package/dist/js/components/Page/Page.d.ts.map +1 -1
  151. package/dist/js/components/Page/Page.js +1 -0
  152. package/dist/js/components/Page/Page.js.map +1 -1
  153. package/dist/js/components/Page/PageContext.d.ts +1 -0
  154. package/dist/js/components/Page/PageContext.d.ts.map +1 -1
  155. package/dist/js/components/Page/PageContext.js +1 -0
  156. package/dist/js/components/Page/PageContext.js.map +1 -1
  157. package/dist/js/components/Page/PageSidebar.js +2 -2
  158. package/dist/js/components/Page/PageSidebar.js.map +1 -1
  159. package/dist/js/components/Progress/Progress.d.ts +2 -0
  160. package/dist/js/components/Progress/Progress.d.ts.map +1 -1
  161. package/dist/js/components/Progress/Progress.js +2 -2
  162. package/dist/js/components/Progress/Progress.js.map +1 -1
  163. package/dist/js/components/Progress/ProgressContainer.d.ts +2 -0
  164. package/dist/js/components/Progress/ProgressContainer.d.ts.map +1 -1
  165. package/dist/js/components/Progress/ProgressContainer.js +2 -2
  166. package/dist/js/components/Progress/ProgressContainer.js.map +1 -1
  167. package/dist/umd/assets/{output-DxVGusJA.css → output-TagVdL0S.css} +15766 -15766
  168. package/dist/umd/react-core.min.js +3 -3
  169. package/helpers/package.json +1 -1
  170. package/layouts/package.json +1 -1
  171. package/next/package.json +1 -1
  172. package/package.json +2 -2
  173. package/src/components/ExpandableSection/ExpandableSection.tsx +17 -5
  174. package/src/components/ExpandableSection/ExpandableSectionToggle.tsx +48 -38
  175. package/src/components/ExpandableSection/__tests__/ExpandableSection.test.tsx +63 -0
  176. package/src/components/ExpandableSection/__tests__/ExpandableSectionToggle.test.tsx +28 -0
  177. package/src/components/ExpandableSection/examples/ExpandableSection.md +16 -0
  178. package/src/components/ExpandableSection/examples/ExpandableSectionCustomToggle.tsx +31 -16
  179. package/src/components/ExpandableSection/examples/ExpandableSectionUncontrolledDynamicToggleFunction.tsx +13 -0
  180. package/src/components/ExpandableSection/examples/ExpandableSectionWithHeading.tsx +89 -0
  181. package/src/components/Page/Page.tsx +1 -0
  182. package/src/components/Page/PageContext.tsx +2 -0
  183. package/src/components/Page/PageSidebar.tsx +2 -2
  184. package/src/components/Page/__tests__/Generated/__snapshots__/PageSidebar.test.tsx.snap +1 -1
  185. package/src/components/Page/__tests__/__snapshots__/Page.test.tsx.snap +12 -12
  186. package/src/components/Progress/Progress.tsx +4 -0
  187. package/src/components/Progress/ProgressContainer.tsx +5 -2
  188. package/src/components/Progress/__tests__/Generated/ProgressContainer.test.tsx +1 -0
  189. package/src/components/Progress/__tests__/Progress.test.tsx +32 -0
  190. package/src/components/Progress/examples/Progress.md +2 -14
  191. package/src/components/Progress/examples/ProgressInteractiveFailure.tsx +55 -0
  192. package/src/components/Progress/examples/ProgressFailureWithoutMeasure.tsx +0 -5
  193. package/src/components/Progress/examples/ProgressInsideSuccess.tsx +0 -10
  194. package/src/components/Progress/examples/ProgressOutsideFailure.tsx +0 -10
@@ -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.5.0-prerelease.1","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.5.0-prerelease.3","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.5.0-prerelease.1","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.5.0-prerelease.3","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.5.0-prerelease.1","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.5.0-prerelease.3","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "6.5.0-prerelease.2",
3
+ "version": "6.5.0-prerelease.4",
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": "c7b90a136c135c4cf74a7fc768cc17ee39227a6d"
66
+ "gitHead": "912729437db337b5e576adbe8d06bdf2bc04b189"
67
67
  }
@@ -44,8 +44,10 @@ export interface ExpandableSectionProps extends Omit<React.HTMLProps<HTMLDivElem
44
44
  * use the onToggle property of the expandable section toggle sub-component.
45
45
  */
46
46
  onToggle?: (event: React.MouseEvent, isExpanded: boolean) => void;
47
- /** React node that appears in the attached toggle in place of the toggleText property. */
48
- toggleContent?: React.ReactNode;
47
+ /** React node that appears in the attached toggle in place of the toggleText property.
48
+ * Can also be a function that receives the expanded state and returns a React node.
49
+ */
50
+ toggleContent?: React.ReactNode | ((isExpanded: boolean) => React.ReactNode);
49
51
  /** Text that appears in the attached toggle. */
50
52
  toggleText?: string;
51
53
  /** Text that appears in the attached toggle when collapsed (will override toggleText if
@@ -72,6 +74,11 @@ export interface ExpandableSectionProps extends Omit<React.HTMLProps<HTMLDivElem
72
74
  * animation will not occur.
73
75
  */
74
76
  direction?: 'up' | 'down';
77
+ /** The HTML element to use for the toggle wrapper. Can be 'div' (default) or any heading level.
78
+ * When using heading elements, the button will be rendered inside the heading for proper semantics.
79
+ * This is useful when the toggle text should function as a heading in the document structure.
80
+ */
81
+ toggleWrapper?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
75
82
  }
76
83
 
77
84
  interface ExpandableSectionState {
@@ -216,6 +223,7 @@ class ExpandableSection extends Component<ExpandableSectionProps, ExpandableSect
216
223
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
217
224
  truncateMaxLines,
218
225
  direction,
226
+ toggleWrapper = 'div',
219
227
  ...props
220
228
  } = this.props;
221
229
 
@@ -246,8 +254,12 @@ class ExpandableSection extends Component<ExpandableSectionProps, ExpandableSect
246
254
  propOrStateIsExpanded
247
255
  );
248
256
 
257
+ const computedToggleContent =
258
+ typeof toggleContent === 'function' ? toggleContent(propOrStateIsExpanded) : toggleContent;
259
+ const ToggleWrapper = toggleWrapper as any;
260
+
249
261
  const expandableToggle = !isDetached && (
250
- <div className={`${styles.expandableSection}__toggle`}>
262
+ <ToggleWrapper className={`${styles.expandableSection}__toggle`}>
251
263
  <Button
252
264
  variant="link"
253
265
  {...(variant === ExpandableSectionVariant.truncate && { isInline: true })}
@@ -265,9 +277,9 @@ class ExpandableSection extends Component<ExpandableSectionProps, ExpandableSect
265
277
  aria-label={toggleAriaLabel}
266
278
  aria-labelledby={toggleAriaLabelledBy}
267
279
  >
268
- {toggleContent || computedToggleText}
280
+ {computedToggleContent || computedToggleText}
269
281
  </Button>
270
- </div>
282
+ </ToggleWrapper>
271
283
  );
272
284
 
273
285
  return (
@@ -34,6 +34,11 @@ export interface ExpandableSectionToggleProps extends Omit<React.HTMLProps<HTMLD
34
34
  toggleAriaLabel?: string;
35
35
  /** Accessible name via space delimtted list of IDs for the expandable section toggle. */
36
36
  toggleAriaLabelledBy?: string;
37
+ /** The HTML element to use for the toggle wrapper. Can be 'div' (default) or any heading level.
38
+ * When using heading elements, the button will be rendered inside the heading for proper semantics.
39
+ * This is useful when the toggle text should function as a heading in the document structure.
40
+ */
41
+ toggleWrapper?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
37
42
  }
38
43
 
39
44
  export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionToggleProps> = ({
@@ -48,45 +53,50 @@ export const ExpandableSectionToggle: React.FunctionComponent<ExpandableSectionT
48
53
  isDetached,
49
54
  toggleAriaLabel,
50
55
  toggleAriaLabelledBy,
56
+ toggleWrapper = 'div',
51
57
  ...props
52
- }: ExpandableSectionToggleProps) => (
53
- <div
54
- className={css(
55
- styles.expandableSection,
56
- isExpanded && styles.modifiers.expanded,
57
- hasTruncatedContent && styles.modifiers.truncate,
58
- isDetached && 'pf-m-detached',
59
- className
60
- )}
61
- {...props}
62
- >
63
- <div className={`${styles.expandableSection}__toggle`}>
64
- <Button
65
- variant="link"
66
- {...(hasTruncatedContent && { isInline: true })}
67
- aria-expanded={isExpanded}
68
- aria-controls={contentId}
69
- onClick={() => onToggle(!isExpanded)}
70
- id={toggleId}
71
- {...(!hasTruncatedContent && {
72
- icon: (
73
- <span
74
- className={css(
75
- styles.expandableSectionToggleIcon,
76
- isExpanded && direction === 'up' && styles.modifiers.expandTop // TODO: next breaking change move this class to the outer styles.expandableSection wrapper
77
- )}
78
- >
79
- <AngleRightIcon />
80
- </span>
81
- )
82
- })}
83
- aria-label={toggleAriaLabel}
84
- aria-labelledby={toggleAriaLabelledBy}
85
- >
86
- {children}
87
- </Button>
58
+ }: ExpandableSectionToggleProps) => {
59
+ const ToggleWrapper = toggleWrapper as any;
60
+
61
+ return (
62
+ <div
63
+ className={css(
64
+ styles.expandableSection,
65
+ isExpanded && styles.modifiers.expanded,
66
+ hasTruncatedContent && styles.modifiers.truncate,
67
+ isDetached && 'pf-m-detached',
68
+ className
69
+ )}
70
+ {...props}
71
+ >
72
+ <ToggleWrapper className={`${styles.expandableSection}__toggle`}>
73
+ <Button
74
+ variant="link"
75
+ {...(hasTruncatedContent && { isInline: true })}
76
+ aria-expanded={isExpanded}
77
+ aria-controls={contentId}
78
+ onClick={() => onToggle(!isExpanded)}
79
+ id={toggleId}
80
+ {...(!hasTruncatedContent && {
81
+ icon: (
82
+ <span
83
+ className={css(
84
+ styles.expandableSectionToggleIcon,
85
+ isExpanded && direction === 'up' && styles.modifiers.expandTop // TODO: next breaking change move this class to the outer styles.expandableSection wrapper
86
+ )}
87
+ >
88
+ <AngleRightIcon />
89
+ </span>
90
+ )
91
+ })}
92
+ aria-label={toggleAriaLabel}
93
+ aria-labelledby={toggleAriaLabelledBy}
94
+ >
95
+ {children}
96
+ </Button>
97
+ </ToggleWrapper>
88
98
  </div>
89
- </div>
90
- );
99
+ );
100
+ };
91
101
 
92
102
  ExpandableSectionToggle.displayName = 'ExpandableSectionToggle';
@@ -208,3 +208,66 @@ test('Renders with aria-labelledby when toggleAriaLabelledBy is passed', () => {
208
208
 
209
209
  expect(screen.getByRole('button')).toHaveAccessibleName('Test label');
210
210
  });
211
+
212
+ test('Renders toggleContent as a function in uncontrolled mode (collapsed)', () => {
213
+ render(
214
+ <ExpandableSection toggleContent={(isExpanded) => (isExpanded ? 'Hide details' : 'Show details')}>
215
+ Test content
216
+ </ExpandableSection>
217
+ );
218
+
219
+ expect(screen.getByRole('button', { name: 'Show details' })).toBeInTheDocument();
220
+ });
221
+
222
+ test('Renders toggleContent as a function in uncontrolled mode (expanded after click)', async () => {
223
+ const user = userEvent.setup();
224
+
225
+ render(
226
+ <ExpandableSection toggleContent={(isExpanded) => (isExpanded ? 'Hide details' : 'Show details')}>
227
+ Test content
228
+ </ExpandableSection>
229
+ );
230
+
231
+ const button = screen.getByRole('button', { name: 'Show details' });
232
+ await user.click(button);
233
+
234
+ expect(screen.getByRole('button', { name: 'Hide details' })).toBeInTheDocument();
235
+ });
236
+
237
+ test('Renders toggleContent as a function in controlled mode', () => {
238
+ render(
239
+ <ExpandableSection isExpanded={true} toggleContent={(isExpanded) => (isExpanded ? 'Collapse' : 'Expand')}>
240
+ Test content
241
+ </ExpandableSection>
242
+ );
243
+
244
+ expect(screen.getByRole('button', { name: 'Collapse' })).toBeInTheDocument();
245
+ });
246
+
247
+ test('Renders with default div wrapper when toggleWrapper is not specified', () => {
248
+ render(<ExpandableSection data-testid="test-id">Test content</ExpandableSection>);
249
+
250
+ const toggle = screen.getByRole('button').parentElement;
251
+ expect(toggle?.tagName).toBe('DIV');
252
+ });
253
+
254
+ test('Renders with h2 wrapper when toggleWrapper="h2"', () => {
255
+ render(
256
+ <ExpandableSection data-testid="test-id" toggleWrapper="h2">
257
+ Test content
258
+ </ExpandableSection>
259
+ );
260
+
261
+ expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
262
+ });
263
+
264
+ test('Renders with div wrapper when toggleWrapper="div"', () => {
265
+ render(
266
+ <ExpandableSection data-testid="test-id" toggleWrapper="div">
267
+ Test content
268
+ </ExpandableSection>
269
+ );
270
+
271
+ const toggle = screen.getByRole('button').parentElement;
272
+ expect(toggle?.tagName).toBe('DIV');
273
+ });
@@ -47,3 +47,31 @@ test('Renders with aria-labelledby when toggleAriaLabelledBy is passed', () => {
47
47
 
48
48
  expect(screen.getByRole('button')).toHaveAccessibleName('Test label');
49
49
  });
50
+
51
+ test('Renders with default div wrapper when toggleWrapper is not specified', () => {
52
+ render(<ExpandableSectionToggle data-testid="test-id">Toggle test</ExpandableSectionToggle>);
53
+
54
+ const toggle = screen.getByRole('button').parentElement;
55
+ expect(toggle?.tagName).toBe('DIV');
56
+ });
57
+
58
+ test('Renders with h2 wrapper when toggleWrapper="h2"', () => {
59
+ render(
60
+ <ExpandableSectionToggle data-testid="test-id" toggleWrapper="h2">
61
+ Toggle test
62
+ </ExpandableSectionToggle>
63
+ );
64
+
65
+ expect(screen.getByRole('heading', { level: 2 })).toBeInTheDocument();
66
+ });
67
+
68
+ test('Renders with div wrapper when toggleWrapper="div"', () => {
69
+ render(
70
+ <ExpandableSectionToggle data-testid="test-id" toggleWrapper="div">
71
+ Toggle test
72
+ </ExpandableSectionToggle>
73
+ );
74
+
75
+ const toggle = screen.getByRole('button').parentElement;
76
+ expect(toggle?.tagName).toBe('DIV');
77
+ });
@@ -28,6 +28,14 @@ import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle
28
28
 
29
29
  ```
30
30
 
31
+ ### Uncontrolled with dynamic toggle content (function)
32
+
33
+ Use `toggleContent` as a function to dynamically render different content based on the expanded state without managing state yourself.
34
+
35
+ ```ts file="ExpandableSectionUncontrolledDynamicToggleFunction.tsx"
36
+
37
+ ```
38
+
31
39
  ### Detached
32
40
 
33
41
  When passing the `isDetached` property into `<ExpandableSection>`, you must also manually pass in the same `toggleId` and `contentId` properties to both `<ExpandableSection>` and `<ExpandableSectionToggle>`. This will link the content to the toggle via ARIA attributes.
@@ -62,6 +70,14 @@ By using the `toggleContent` prop, you can pass in content other than a simple s
62
70
 
63
71
  ```
64
72
 
73
+ ### With heading semantics
74
+
75
+ When the toggle text should function as a heading in the document structure, use the `toggleWrapper` prop to specify a heading element (h1-h6). This ensures proper semantic structure for screen readers and other assistive technologies. The component automatically uses a native button element when heading wrappers are used, allowing the heading styles to display properly.
76
+
77
+ ```ts file="ExpandableSectionWithHeading.tsx"
78
+
79
+ ```
80
+
65
81
  ### Truncate expansion
66
82
 
67
83
  By passing in `variant="truncate"`, the expandable content will be visible up to a maximum number of lines before being truncated, with the toggle revealing or hiding the truncated content. By default the expandable content will truncate after 3 lines, and this can be customized by also passing in the `truncateMaxLines` prop.
@@ -1,5 +1,5 @@
1
1
  import { useState } from 'react';
2
- import { ExpandableSection, Badge } from '@patternfly/react-core';
2
+ import { ExpandableSection, Badge, Stack, StackItem } from '@patternfly/react-core';
3
3
  import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
4
4
 
5
5
  export const ExpandableSectionCustomToggle: React.FunctionComponent = () => {
@@ -10,20 +10,35 @@ export const ExpandableSectionCustomToggle: React.FunctionComponent = () => {
10
10
  };
11
11
 
12
12
  return (
13
- <ExpandableSection
14
- toggleContent={
15
- <div>
16
- <span>You can also use icons </span>
17
- <CheckCircleIcon />
18
- <span> or badges </span>
19
- <Badge isRead={true}>4</Badge>
20
- <span> !</span>
21
- </div>
22
- }
23
- onToggle={onToggle}
24
- isExpanded={isExpanded}
25
- >
26
- This content is visible only when the component is expanded.
27
- </ExpandableSection>
13
+ <Stack hasGutter>
14
+ <StackItem>
15
+ <h3>Custom Toggle Content</h3>
16
+ <p>You can use custom content such as icons and badges in the toggle:</p>
17
+ <ExpandableSection
18
+ toggleContent={
19
+ <div>
20
+ <span>You can also use icons </span>
21
+ <CheckCircleIcon />
22
+ <span> or badges </span>
23
+ <Badge isRead={true}>4</Badge>
24
+ <span> !</span>
25
+ </div>
26
+ }
27
+ onToggle={onToggle}
28
+ isExpanded={isExpanded}
29
+ >
30
+ This content is visible only when the component is expanded.
31
+ </ExpandableSection>
32
+ </StackItem>
33
+
34
+ <StackItem>
35
+ <h3>Accessibility Note</h3>
36
+ <p>
37
+ <strong>Important:</strong> If you need the toggle text to function as a heading in the document structure, do
38
+ NOT put heading elements (h1-h6) inside the <code>toggleContent</code> prop, as this creates invalid HTML
39
+ structure. Instead, use the <code>toggleWrapper</code> prop.
40
+ </p>
41
+ </StackItem>
42
+ </Stack>
28
43
  );
29
44
  };
@@ -0,0 +1,13 @@
1
+ import { ExpandableSection } from '@patternfly/react-core';
2
+
3
+ export const ExpandableSectionUncontrolledDynamicToggleFunction: React.FunctionComponent = () => (
4
+ <ExpandableSection
5
+ toggleContent={(isExpanded) =>
6
+ isExpanded
7
+ ? 'Show less uncontrolled dynamic toggle example content'
8
+ : 'Show more uncontrolled dynamic toggle example content'
9
+ }
10
+ >
11
+ This content is visible only when the component is expanded.
12
+ </ExpandableSection>
13
+ );
@@ -0,0 +1,89 @@
1
+ import { useState, MouseEvent } from 'react';
2
+ import { ExpandableSection, ExpandableSectionToggle, Stack, StackItem } from '@patternfly/react-core';
3
+ import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
4
+
5
+ export const ExpandableSectionWithHeading = () => {
6
+ const [isExpanded1, setIsExpanded1] = useState(false);
7
+ const [isExpanded2, setIsExpanded2] = useState(false);
8
+ const [isExpandedDetached, setIsExpandedDetached] = useState(false);
9
+
10
+ const onToggle1 = (_event: MouseEvent, isExpanded: boolean) => {
11
+ setIsExpanded1(isExpanded);
12
+ };
13
+
14
+ const onToggle2 = (_event: MouseEvent, isExpanded: boolean) => {
15
+ setIsExpanded2(isExpanded);
16
+ };
17
+
18
+ const onToggleDetached = (isExpanded: boolean) => {
19
+ setIsExpandedDetached(isExpanded);
20
+ };
21
+
22
+ return (
23
+ <Stack hasGutter>
24
+ <StackItem>
25
+ <h4>Document with Expandable Sections</h4>
26
+ <p>This demonstrates how to use expandable sections with proper heading semantics.</p>
27
+
28
+ {/* Using toggleWrapper prop for proper heading semantics */}
29
+ <ExpandableSection
30
+ toggleWrapper="h5"
31
+ toggleText="Toggle as a heading"
32
+ onToggle={onToggle1}
33
+ isExpanded={isExpanded1}
34
+ >
35
+ <p>
36
+ This content is visible only when the component is expanded. The toggle text above functions as a proper
37
+ heading in the document structure, which is important for screen readers and other assistive technologies.
38
+ </p>
39
+ <p>
40
+ When using the <code>toggleWrapper</code> prop with heading elements (h1-h6), the button is rendered inside
41
+ the heading element, maintaining proper semantic structure.
42
+ </p>
43
+ </ExpandableSection>
44
+ </StackItem>
45
+
46
+ <StackItem>
47
+ <h4>Detached Variant with Heading</h4>
48
+ <p>You can also use the detached variant with heading semantics:</p>
49
+
50
+ <ExpandableSectionToggle
51
+ toggleWrapper="h5"
52
+ toggleId="detached-heading-toggle"
53
+ contentId="detached-heading-content"
54
+ isExpanded={isExpandedDetached}
55
+ onToggle={onToggleDetached}
56
+ >
57
+ Detached Toggle with Heading
58
+ </ExpandableSectionToggle>
59
+
60
+ <ExpandableSection
61
+ isDetached
62
+ toggleId="detached-heading-toggle"
63
+ contentId="detached-heading-content"
64
+ isExpanded={isExpandedDetached}
65
+ >
66
+ <p>This is detached content that can be positioned anywhere in the DOM.</p>
67
+ </ExpandableSection>
68
+ </StackItem>
69
+
70
+ <StackItem>
71
+ <h4>Custom Content with Heading</h4>
72
+ <p>You can also use custom content within heading wrappers:</p>
73
+
74
+ <ExpandableSection
75
+ toggleWrapper="h5"
76
+ toggleContent={
77
+ <span>
78
+ <CheckCircleIcon /> Custom Heading Content with Icon
79
+ </span>
80
+ }
81
+ onToggle={onToggle2}
82
+ isExpanded={isExpanded2}
83
+ >
84
+ <p>This expandable section uses custom content with an icon inside a heading wrapper.</p>
85
+ </ExpandableSection>
86
+ </StackItem>
87
+ </Stack>
88
+ );
89
+ };
@@ -266,6 +266,7 @@ class Page extends Component<PageProps, PageState> {
266
266
  isManagedSidebar,
267
267
  onSidebarToggle: mobileView ? this.onSidebarToggleMobile : this.onSidebarToggleDesktop,
268
268
  isSidebarOpen: mobileView ? mobileIsSidebarOpen : desktopIsSidebarOpen,
269
+ isMobile: mobileView,
269
270
  width,
270
271
  height,
271
272
  getBreakpoint,
@@ -6,6 +6,7 @@ export interface PageContextProps {
6
6
  isManagedSidebar: boolean;
7
7
  onSidebarToggle: () => void;
8
8
  isSidebarOpen: boolean;
9
+ isMobile: boolean;
9
10
  width: number;
10
11
  height: number;
11
12
  getBreakpoint: (width: number | null) => 'default' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
@@ -16,6 +17,7 @@ export const pageContextDefaults: PageContextProps = {
16
17
  isManagedSidebar: false,
17
18
  isSidebarOpen: false,
18
19
  onSidebarToggle: () => null,
20
+ isMobile: false,
19
21
  width: null,
20
22
  height: null,
21
23
  getBreakpoint,
@@ -35,7 +35,7 @@ export const PageSidebar: React.FunctionComponent<PageSidebarProps> = ({
35
35
  ...props
36
36
  }: PageSidebarProps) => (
37
37
  <PageContextConsumer>
38
- {({ isManagedSidebar, isSidebarOpen: managedIsNavOpen }: PageSidebarProps) => {
38
+ {({ isManagedSidebar, isSidebarOpen: managedIsNavOpen, isMobile }) => {
39
39
  const sidebarOpen = isManagedSidebar ? managedIsNavOpen : isSidebarOpen;
40
40
 
41
41
  return (
@@ -43,7 +43,7 @@ export const PageSidebar: React.FunctionComponent<PageSidebarProps> = ({
43
43
  id={id}
44
44
  className={css(
45
45
  styles.pageSidebar,
46
- sidebarOpen && styles.modifiers.expanded,
46
+ sidebarOpen && isMobile && styles.modifiers.expanded,
47
47
  !sidebarOpen && styles.modifiers.collapsed,
48
48
  className
49
49
  )}
@@ -4,7 +4,7 @@ exports[`PageSidebar should match snapshot (auto-generated) 1`] = `
4
4
  <DocumentFragment>
5
5
  <div
6
6
  aria-hidden="false"
7
- class="pf-v6-c-page__sidebar pf-m-expanded ''"
7
+ class="pf-v6-c-page__sidebar ''"
8
8
  id="page-sidebar"
9
9
  >
10
10
  <div>
@@ -12,7 +12,7 @@ exports[`Page Check dark page against snapshot 1`] = `
12
12
  />
13
13
  <div
14
14
  aria-hidden="false"
15
- class="pf-v6-c-page__sidebar pf-m-expanded"
15
+ class="pf-v6-c-page__sidebar"
16
16
  id="page-sidebar"
17
17
  >
18
18
  <div
@@ -55,7 +55,7 @@ exports[`Page Check page horizontal layout example against snapshot 1`] = `
55
55
  />
56
56
  <div
57
57
  aria-hidden="false"
58
- class="pf-v6-c-page__sidebar pf-m-expanded"
58
+ class="pf-v6-c-page__sidebar"
59
59
  id="page-sidebar"
60
60
  />
61
61
  <div
@@ -92,7 +92,7 @@ exports[`Page Check page to verify breadcrumb is created - PageBreadcrumb syntax
92
92
  />
93
93
  <div
94
94
  aria-hidden="false"
95
- class="pf-v6-c-page__sidebar pf-m-expanded"
95
+ class="pf-v6-c-page__sidebar"
96
96
  id="page-sidebar"
97
97
  />
98
98
  <div
@@ -237,7 +237,7 @@ exports[`Page Check page to verify breadcrumb is created 1`] = `
237
237
  />
238
238
  <div
239
239
  aria-hidden="false"
240
- class="pf-v6-c-page__sidebar pf-m-expanded"
240
+ class="pf-v6-c-page__sidebar"
241
241
  id="page-sidebar"
242
242
  />
243
243
  <div
@@ -386,7 +386,7 @@ exports[`Page Check page to verify grouped nav and breadcrumb - new components s
386
386
  />
387
387
  <div
388
388
  aria-hidden="false"
389
- class="pf-v6-c-page__sidebar pf-m-expanded"
389
+ class="pf-v6-c-page__sidebar"
390
390
  id="page-sidebar"
391
391
  />
392
392
  <div
@@ -629,7 +629,7 @@ exports[`Page Check page to verify grouped nav and breadcrumb - old / props synt
629
629
  />
630
630
  <div
631
631
  aria-hidden="false"
632
- class="pf-v6-c-page__sidebar pf-m-expanded"
632
+ class="pf-v6-c-page__sidebar"
633
633
  id="page-sidebar"
634
634
  />
635
635
  <div
@@ -913,7 +913,7 @@ exports[`Page Check page to verify skip to content points to main content region
913
913
  />
914
914
  <div
915
915
  aria-hidden="false"
916
- class="pf-v6-c-page__sidebar pf-m-expanded"
916
+ class="pf-v6-c-page__sidebar"
917
917
  id="page-sidebar"
918
918
  />
919
919
  <div
@@ -1063,7 +1063,7 @@ exports[`Page Check page vertical layout example against snapshot 1`] = `
1063
1063
  />
1064
1064
  <div
1065
1065
  aria-hidden="false"
1066
- class="pf-v6-c-page__sidebar pf-m-expanded"
1066
+ class="pf-v6-c-page__sidebar"
1067
1067
  id="page-sidebar"
1068
1068
  >
1069
1069
  <div
@@ -1106,7 +1106,7 @@ exports[`Page Sticky bottom breadcrumb on all height breakpoints - PageBreadcrum
1106
1106
  />
1107
1107
  <div
1108
1108
  aria-hidden="false"
1109
- class="pf-v6-c-page__sidebar pf-m-expanded"
1109
+ class="pf-v6-c-page__sidebar"
1110
1110
  id="page-sidebar"
1111
1111
  />
1112
1112
  <div
@@ -1251,7 +1251,7 @@ exports[`Page Verify sticky bottom breadcrumb on all height breakpoints 1`] = `
1251
1251
  />
1252
1252
  <div
1253
1253
  aria-hidden="false"
1254
- class="pf-v6-c-page__sidebar pf-m-expanded"
1254
+ class="pf-v6-c-page__sidebar"
1255
1255
  id="page-sidebar"
1256
1256
  />
1257
1257
  <div
@@ -1400,7 +1400,7 @@ exports[`Page Verify sticky top breadcrumb on all height breakpoints - PageBread
1400
1400
  />
1401
1401
  <div
1402
1402
  aria-hidden="false"
1403
- class="pf-v6-c-page__sidebar pf-m-expanded"
1403
+ class="pf-v6-c-page__sidebar"
1404
1404
  id="page-sidebar"
1405
1405
  />
1406
1406
  <div
@@ -1545,7 +1545,7 @@ exports[`Page Verify sticky top breadcrumb on all height breakpoints 1`] = `
1545
1545
  />
1546
1546
  <div
1547
1547
  aria-hidden="false"
1548
- class="pf-v6-c-page__sidebar pf-m-expanded"
1548
+ class="pf-v6-c-page__sidebar"
1549
1549
  id="page-sidebar"
1550
1550
  />
1551
1551
  <div