@patternfly/react-core 6.5.0-prerelease.57 → 6.5.0-prerelease.59

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 (186) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/AboutModal/package.json +1 -1
  5. package/dist/dynamic/components/Accordion/package.json +1 -1
  6. package/dist/dynamic/components/ActionList/package.json +1 -1
  7. package/dist/dynamic/components/Alert/package.json +1 -1
  8. package/dist/dynamic/components/Avatar/package.json +1 -1
  9. package/dist/dynamic/components/BackToTop/package.json +1 -1
  10. package/dist/dynamic/components/Backdrop/package.json +1 -1
  11. package/dist/dynamic/components/BackgroundImage/package.json +1 -1
  12. package/dist/dynamic/components/Badge/package.json +1 -1
  13. package/dist/dynamic/components/Banner/package.json +1 -1
  14. package/dist/dynamic/components/Brand/package.json +1 -1
  15. package/dist/dynamic/components/Breadcrumb/package.json +1 -1
  16. package/dist/dynamic/components/Button/package.json +1 -1
  17. package/dist/dynamic/components/CalendarMonth/package.json +1 -1
  18. package/dist/dynamic/components/Card/package.json +1 -1
  19. package/dist/dynamic/components/Checkbox/package.json +1 -1
  20. package/dist/dynamic/components/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/Hero/package.json +1 -1
  38. package/dist/dynamic/components/Hint/package.json +1 -1
  39. package/dist/dynamic/components/Icon/package.json +1 -1
  40. package/dist/dynamic/components/InputGroup/package.json +1 -1
  41. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  42. package/dist/dynamic/components/Label/package.json +1 -1
  43. package/dist/dynamic/components/List/package.json +1 -1
  44. package/dist/dynamic/components/LoginPage/package.json +1 -1
  45. package/dist/dynamic/components/Masthead/package.json +1 -1
  46. package/dist/dynamic/components/Menu/package.json +1 -1
  47. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  48. package/dist/dynamic/components/Modal/package.json +1 -1
  49. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  50. package/dist/dynamic/components/Nav/package.json +1 -1
  51. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  52. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  53. package/dist/dynamic/components/NumberInput/package.json +1 -1
  54. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  55. package/dist/dynamic/components/Page/package.json +1 -1
  56. package/dist/dynamic/components/Pagination/package.json +1 -1
  57. package/dist/dynamic/components/Panel/package.json +1 -1
  58. package/dist/dynamic/components/Popover/package.json +1 -1
  59. package/dist/dynamic/components/Progress/package.json +1 -1
  60. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  61. package/dist/dynamic/components/Radio/package.json +1 -1
  62. package/dist/dynamic/components/SearchInput/package.json +1 -1
  63. package/dist/dynamic/components/Select/package.json +1 -1
  64. package/dist/dynamic/components/Sidebar/package.json +1 -1
  65. package/dist/dynamic/components/SimpleList/package.json +1 -1
  66. package/dist/dynamic/components/Skeleton/package.json +1 -1
  67. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  68. package/dist/dynamic/components/Slider/package.json +1 -1
  69. package/dist/dynamic/components/Spinner/package.json +1 -1
  70. package/dist/dynamic/components/Switch/package.json +1 -1
  71. package/dist/dynamic/components/Tabs/package.json +1 -1
  72. package/dist/dynamic/components/TextArea/package.json +1 -1
  73. package/dist/dynamic/components/TextInput/package.json +1 -1
  74. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  75. package/dist/dynamic/components/TimePicker/package.json +1 -1
  76. package/dist/dynamic/components/Timestamp/package.json +1 -1
  77. package/dist/dynamic/components/Title/package.json +1 -1
  78. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  79. package/dist/dynamic/components/Toolbar/package.json +1 -1
  80. package/dist/dynamic/components/Tooltip/package.json +1 -1
  81. package/dist/dynamic/components/TreeView/package.json +1 -1
  82. package/dist/dynamic/components/Truncate/package.json +1 -1
  83. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  84. package/dist/dynamic/components/Wizard/package.json +1 -1
  85. package/dist/dynamic/deprecated/components/Chip/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/Tile/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  91. package/dist/dynamic/deprecated/components/package.json +1 -1
  92. package/dist/dynamic/helpers/AnimationsProvider/AnimationsProvider/package.json +1 -1
  93. package/dist/dynamic/helpers/AnimationsProvider/package.json +1 -1
  94. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  95. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  96. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  97. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  98. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  99. package/dist/dynamic/helpers/SSRSafeIds/SSRSafeIds/package.json +1 -1
  100. package/dist/dynamic/helpers/constants/package.json +1 -1
  101. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  102. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  103. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  104. package/dist/dynamic/helpers/package.json +1 -1
  105. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  106. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  107. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  108. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  109. package/dist/dynamic/helpers/useSSRSafeId/package.json +1 -1
  110. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  111. package/dist/dynamic/helpers/util/package.json +1 -1
  112. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  113. package/dist/dynamic/layouts/Flex/package.json +1 -1
  114. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  115. package/dist/dynamic/layouts/Grid/package.json +1 -1
  116. package/dist/dynamic/layouts/Level/package.json +1 -1
  117. package/dist/dynamic/layouts/Split/package.json +1 -1
  118. package/dist/dynamic/layouts/Stack/package.json +1 -1
  119. package/dist/dynamic/styles/package.json +1 -1
  120. package/dist/esm/components/Button/Button.d.ts +4 -0
  121. package/dist/esm/components/Button/Button.d.ts.map +1 -1
  122. package/dist/esm/components/Button/Button.js +2 -2
  123. package/dist/esm/components/Button/Button.js.map +1 -1
  124. package/dist/esm/components/Compass/Compass.js +1 -1
  125. package/dist/esm/components/Compass/Compass.js.map +1 -1
  126. package/dist/esm/components/Masthead/MastheadLogo.d.ts +2 -0
  127. package/dist/esm/components/Masthead/MastheadLogo.d.ts.map +1 -1
  128. package/dist/esm/components/Masthead/MastheadLogo.js +2 -2
  129. package/dist/esm/components/Masthead/MastheadLogo.js.map +1 -1
  130. package/dist/esm/components/MenuToggle/MenuToggle.d.ts +4 -0
  131. package/dist/esm/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  132. package/dist/esm/components/MenuToggle/MenuToggle.js +4 -2
  133. package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
  134. package/dist/esm/components/Nav/Nav.d.ts +2 -0
  135. package/dist/esm/components/Nav/Nav.d.ts.map +1 -1
  136. package/dist/esm/components/Nav/Nav.js +3 -2
  137. package/dist/esm/components/Nav/Nav.js.map +1 -1
  138. package/dist/esm/components/Page/Page.d.ts +9 -1
  139. package/dist/esm/components/Page/Page.d.ts.map +1 -1
  140. package/dist/esm/components/Page/Page.js +4 -4
  141. package/dist/esm/components/Page/Page.js.map +1 -1
  142. package/dist/js/components/Button/Button.d.ts +4 -0
  143. package/dist/js/components/Button/Button.d.ts.map +1 -1
  144. package/dist/js/components/Button/Button.js +2 -2
  145. package/dist/js/components/Button/Button.js.map +1 -1
  146. package/dist/js/components/Compass/Compass.js +1 -1
  147. package/dist/js/components/Compass/Compass.js.map +1 -1
  148. package/dist/js/components/Masthead/MastheadLogo.d.ts +2 -0
  149. package/dist/js/components/Masthead/MastheadLogo.d.ts.map +1 -1
  150. package/dist/js/components/Masthead/MastheadLogo.js +2 -2
  151. package/dist/js/components/Masthead/MastheadLogo.js.map +1 -1
  152. package/dist/js/components/MenuToggle/MenuToggle.d.ts +4 -0
  153. package/dist/js/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  154. package/dist/js/components/MenuToggle/MenuToggle.js +4 -2
  155. package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
  156. package/dist/js/components/Nav/Nav.d.ts +2 -0
  157. package/dist/js/components/Nav/Nav.d.ts.map +1 -1
  158. package/dist/js/components/Nav/Nav.js +3 -2
  159. package/dist/js/components/Nav/Nav.js.map +1 -1
  160. package/dist/js/components/Page/Page.d.ts +9 -1
  161. package/dist/js/components/Page/Page.d.ts.map +1 -1
  162. package/dist/js/components/Page/Page.js +3 -3
  163. package/dist/js/components/Page/Page.js.map +1 -1
  164. package/dist/umd/assets/{output-BMh4nXGd.css → output-DGLCZ_Kh.css} +19614 -19492
  165. package/dist/umd/react-core.min.js +3 -3
  166. package/helpers/package.json +1 -1
  167. package/layouts/package.json +1 -1
  168. package/next/package.json +1 -1
  169. package/package.json +6 -6
  170. package/src/components/Button/Button.tsx +8 -0
  171. package/src/components/Button/__tests__/Button.test.tsx +42 -0
  172. package/src/components/Button/__tests__/__snapshots__/Button.test.tsx.snap +1 -1
  173. package/src/components/Compass/Compass.tsx +1 -1
  174. package/src/components/Compass/__tests__/Compass.test.tsx +4 -4
  175. package/src/components/Masthead/MastheadLogo.tsx +8 -1
  176. package/src/components/Masthead/__tests__/Masthead.test.tsx +23 -0
  177. package/src/components/MenuToggle/MenuToggle.tsx +10 -0
  178. package/src/components/MenuToggle/__tests__/MenuToggle.test.tsx +40 -0
  179. package/src/components/Nav/Nav.tsx +6 -2
  180. package/src/components/Nav/__tests__/Nav.test.tsx +45 -0
  181. package/src/components/Page/Page.tsx +25 -5
  182. package/src/components/Page/__tests__/Page.test.tsx +84 -10
  183. package/src/demos/Nav.md +32 -1
  184. package/src/demos/Page.md +0 -6
  185. package/src/demos/examples/Nav/NavDockedNav.tsx +416 -0
  186. package/src/demos/examples/Page/PageDockedNav.tsx +0 -259
@@ -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.56","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.58","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.56","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.58","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.56","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.58","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "6.5.0-prerelease.57",
3
+ "version": "6.5.0-prerelease.59",
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",
@@ -46,15 +46,15 @@
46
46
  "subpaths": "node ../../scripts/exportSubpaths.mjs --config subpaths.config.json"
47
47
  },
48
48
  "dependencies": {
49
- "@patternfly/react-icons": "^6.5.0-prerelease.24",
50
- "@patternfly/react-styles": "^6.5.0-prerelease.17",
51
- "@patternfly/react-tokens": "^6.5.0-prerelease.16",
49
+ "@patternfly/react-icons": "^6.5.0-prerelease.25",
50
+ "@patternfly/react-styles": "^6.5.0-prerelease.18",
51
+ "@patternfly/react-tokens": "^6.5.0-prerelease.17",
52
52
  "focus-trap": "7.6.6",
53
53
  "react-dropzone": "^14.3.5",
54
54
  "tslib": "^2.8.1"
55
55
  },
56
56
  "devDependencies": {
57
- "@patternfly/patternfly": "6.5.0-prerelease.67",
57
+ "@patternfly/patternfly": "6.5.0-prerelease.71",
58
58
  "case-anything": "^3.1.2",
59
59
  "css": "^3.0.0",
60
60
  "fs-extra": "^11.3.3"
@@ -63,5 +63,5 @@
63
63
  "react": "^17 || ^18 || ^19",
64
64
  "react-dom": "^17 || ^18 || ^19"
65
65
  },
66
- "gitHead": "29497f3ff194a3f5a23019f52d2ffd8fb0a9d252"
66
+ "gitHead": "6558bdd10eb90061e973dcc37ec971eb2b1a354d"
67
67
  }
@@ -109,6 +109,10 @@ export interface ButtonProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'r
109
109
  hamburgerVariant?: 'expand' | 'collapse';
110
110
  /** @beta Flag indicating the button is a circle button. Intended for buttons that only contain an icon.. */
111
111
  isCircle?: boolean;
112
+ /** @beta Flag indicating the button is a docked variant button. For use in docked navigation. */
113
+ isDocked?: boolean;
114
+ /** @beta Flag indicating the docked button should display text. Only applies when isDocked is true. */
115
+ isTextExpanded?: boolean;
112
116
  /** @hide Forwarded ref */
113
117
  innerRef?: React.Ref<any>;
114
118
  /** Adds count number to button */
@@ -134,6 +138,8 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({
134
138
  isHamburger,
135
139
  hamburgerVariant,
136
140
  isCircle,
141
+ isDocked = false,
142
+ isTextExpanded = false,
137
143
  spinnerAriaValueText,
138
144
  spinnerAriaLabelledBy,
139
145
  spinnerAriaLabel,
@@ -265,6 +271,8 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({
265
271
  size === ButtonSize.sm && styles.modifiers.small,
266
272
  size === ButtonSize.lg && styles.modifiers.displayLg,
267
273
  isCircle && styles.modifiers.circle,
274
+ isDocked && styles.modifiers.docked,
275
+ isDocked && isTextExpanded && styles.modifiers.textExpanded,
268
276
  className
269
277
  )}
270
278
  disabled={isButtonElement ? isDisabled : null}
@@ -555,6 +555,48 @@ describe('Favorite button', () => {
555
555
  });
556
556
  });
557
557
 
558
+ describe('Dock variant', () => {
559
+ test(`Renders with class ${styles.modifiers.docked} when isDocked = true`, () => {
560
+ render(<Button isDocked>Dock Button</Button>);
561
+ expect(screen.getByRole('button')).toHaveClass(styles.modifiers.docked);
562
+ });
563
+
564
+ test(`Does not render with class ${styles.modifiers.docked} when isDocked is not passed`, () => {
565
+ render(<Button>Button</Button>);
566
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.docked);
567
+ });
568
+
569
+ test(`Renders with class ${styles.modifiers.textExpanded} when isTextExpanded = true and isDocked = true`, () => {
570
+ render(
571
+ <Button isTextExpanded isDocked>
572
+ Text Expanded Button
573
+ </Button>
574
+ );
575
+ expect(screen.getByRole('button')).toHaveClass(styles.modifiers.textExpanded);
576
+ });
577
+
578
+ test(`Does not render with class ${styles.modifiers.textExpanded} when isTextExpanded is not passed`, () => {
579
+ render(<Button>Button</Button>);
580
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.textExpanded);
581
+ });
582
+
583
+ test(`Does not render with class ${styles.modifiers.textExpanded} when isTextExpanded = true but isDocked is not passed`, () => {
584
+ render(<Button isTextExpanded>Text Expanded Button</Button>);
585
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.textExpanded);
586
+ });
587
+
588
+ test(`Renders with both ${styles.modifiers.docked} and ${styles.modifiers.textExpanded} when both props are true`, () => {
589
+ render(
590
+ <Button isDocked isTextExpanded>
591
+ Dock Text Expanded Button
592
+ </Button>
593
+ );
594
+ const button = screen.getByRole('button');
595
+ expect(button).toHaveClass(styles.modifiers.docked);
596
+ expect(button).toHaveClass(styles.modifiers.textExpanded);
597
+ });
598
+ });
599
+
558
600
  test(`Renders basic button`, () => {
559
601
  const { asFragment } = render(<Button aria-label="basic button">Basic Button</Button>);
560
602
  expect(asFragment()).toMatchSnapshot();
@@ -5,7 +5,7 @@ exports[`Renders basic button 1`] = `
5
5
  <button
6
6
  aria-label="basic button"
7
7
  class="pf-v6-c-button pf-m-primary"
8
- data-ouia-component-id="OUIA-Generated-Button-primary-:r30:"
8
+ data-ouia-component-id="OUIA-Generated-Button-primary-:r36:"
9
9
  data-ouia-component-type="PF6/Button"
10
10
  data-ouia-safe="true"
11
11
  type="button"
@@ -68,7 +68,7 @@ export const Compass: React.FunctionComponent<CompassProps> = ({
68
68
 
69
69
  const compassContent = (
70
70
  <div
71
- className={css(styles.compass, dock !== undefined && styles.modifiers.dock, className)}
71
+ className={css(styles.compass, dock !== undefined && styles.modifiers.docked, className)}
72
72
  {...props}
73
73
  style={{ ...props.style, ...backgroundImageStyles }}
74
74
  >
@@ -171,12 +171,12 @@ test('Matches the snapshot with drawer', () => {
171
171
  expect(asFragment()).toMatchSnapshot();
172
172
  });
173
173
 
174
- test(`Renders with ${styles.modifiers.dock} class when dock is passed`, () => {
174
+ test(`Renders with ${styles.modifiers.docked} class when dock is passed`, () => {
175
175
  render(<Compass dock={<div>Dock content</div>} data-testid="compass" />);
176
- expect(screen.getByTestId('compass')).toHaveClass(styles.modifiers.dock);
176
+ expect(screen.getByTestId('compass')).toHaveClass(styles.modifiers.docked);
177
177
  });
178
178
 
179
- test(`Does not render with ${styles.modifiers.dock} class when dock is not passed`, () => {
179
+ test(`Does not render with ${styles.modifiers.docked} class when dock is not passed`, () => {
180
180
  render(<Compass data-testid="compass" />);
181
- expect(screen.getByTestId('compass')).not.toHaveClass(styles.modifiers.dock);
181
+ expect(screen.getByTestId('compass')).not.toHaveClass(styles.modifiers.docked);
182
182
  });
@@ -11,12 +11,15 @@ export interface MastheadLogoProps extends React.DetailedHTMLProps<
11
11
  className?: string;
12
12
  /** Component type of the masthead logo. */
13
13
  component?: React.ElementType<any> | React.ComponentType<any>;
14
+ /** @beta Flag indicating the logo is a compact variant. Used in docked layouts. */
15
+ isCompact?: boolean;
14
16
  }
15
17
 
16
18
  export const MastheadLogo: React.FunctionComponent<MastheadLogoProps> = ({
17
19
  children,
18
20
  className,
19
21
  component,
22
+ isCompact = false,
20
23
  ...props
21
24
  }: MastheadLogoProps) => {
22
25
  let Component = component as any;
@@ -28,7 +31,11 @@ export const MastheadLogo: React.FunctionComponent<MastheadLogoProps> = ({
28
31
  }
29
32
  }
30
33
  return (
31
- <Component className={css(styles.mastheadLogo, className)} {...(Component === 'a' && { tabIndex: 0 })} {...props}>
34
+ <Component
35
+ className={css(styles.mastheadLogo, isCompact && styles.modifiers.compact, className)}
36
+ {...(Component === 'a' && { tabIndex: 0 })}
37
+ {...props}
38
+ >
32
39
  {children}
33
40
  </Component>
34
41
  );
@@ -127,6 +127,29 @@ describe('MastheadLogo', () => {
127
127
 
128
128
  expect(asFragment()).toMatchSnapshot();
129
129
  });
130
+
131
+ test(`Renders with ${styles.modifiers.compact} class when isCompact is true`, () => {
132
+ render(
133
+ <MastheadLogo isCompact data-testid="compact-logo">
134
+ test
135
+ </MastheadLogo>
136
+ );
137
+ expect(screen.getByTestId('compact-logo')).toHaveClass(styles.modifiers.compact);
138
+ });
139
+
140
+ test(`Does not render with ${styles.modifiers.compact} class when isCompact is false`, () => {
141
+ render(
142
+ <MastheadLogo isCompact={false} data-testid="logo">
143
+ test
144
+ </MastheadLogo>
145
+ );
146
+ expect(screen.getByTestId('logo')).not.toHaveClass(styles.modifiers.compact);
147
+ });
148
+
149
+ test(`Does not render with ${styles.modifiers.compact} class when isCompact is not passed`, () => {
150
+ render(<MastheadLogo data-testid="logo">test</MastheadLogo>);
151
+ expect(screen.getByTestId('logo')).not.toHaveClass(styles.modifiers.compact);
152
+ });
130
153
  });
131
154
 
132
155
  describe('MastheadContent', () => {
@@ -53,6 +53,10 @@ export interface MenuToggleProps
53
53
  isCircle?: boolean;
54
54
  /** Flag indicating whether the toggle is a settings toggle. This will override the icon property */
55
55
  isSettings?: boolean;
56
+ /** @beta Flag indicating the menu toggle is a docked variant. For use in docked navigation. */
57
+ isDocked?: boolean;
58
+ /** @beta Flag indicating the docked toggle should display text. Only applies when isDocked is true. */
59
+ isTextExpanded?: boolean;
56
60
  /** Elements to display before the toggle button. When included, renders the menu toggle as a split button. */
57
61
  splitButtonItems?: React.ReactNode[];
58
62
  /** Variant styles of the menu toggle */
@@ -88,6 +92,8 @@ class MenuToggleBase extends Component<MenuToggleProps> {
88
92
  isInForm: false,
89
93
  isPlaceholder: false,
90
94
  isCircle: false,
95
+ isDocked: false,
96
+ isTextExpanded: false,
91
97
  size: 'default',
92
98
  ouiaSafe: true
93
99
  };
@@ -106,6 +112,8 @@ class MenuToggleBase extends Component<MenuToggleProps> {
106
112
  isPlaceholder,
107
113
  isCircle,
108
114
  isSettings,
115
+ isDocked,
116
+ isTextExpanded,
109
117
  splitButtonItems,
110
118
  variant,
111
119
  status,
@@ -190,6 +198,8 @@ class MenuToggleBase extends Component<MenuToggleProps> {
190
198
  isDisabled && styles.modifiers.disabled,
191
199
  isPlaceholder && styles.modifiers.placeholder,
192
200
  isSettings && styles.modifiers.settings,
201
+ isDocked && styles.modifiers.docked,
202
+ isDocked && isTextExpanded && styles.modifiers.textExpanded,
193
203
  size === MenuToggleSize.sm && styles.modifiers.small,
194
204
  className
195
205
  );
@@ -155,3 +155,43 @@ test('Does not render custom icon when icon prop and isSettings are passed', ()
155
155
  );
156
156
  expect(screen.queryByText('Custom icon')).not.toBeInTheDocument();
157
157
  });
158
+
159
+ test(`Renders with class ${styles.modifiers.docked} when isDocked is passed`, () => {
160
+ render(<MenuToggle isDocked>Dock Toggle</MenuToggle>);
161
+ expect(screen.getByRole('button')).toHaveClass(styles.modifiers.docked);
162
+ });
163
+
164
+ test(`Does not render with class ${styles.modifiers.docked} when isDocked is not passed`, () => {
165
+ render(<MenuToggle>Toggle</MenuToggle>);
166
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.docked);
167
+ });
168
+
169
+ test(`Renders with class ${styles.modifiers.textExpanded} when isTextExpanded is passed and isDocked is passed`, () => {
170
+ render(
171
+ <MenuToggle isTextExpanded isDocked>
172
+ Text Expanded Toggle
173
+ </MenuToggle>
174
+ );
175
+ expect(screen.getByRole('button')).toHaveClass(styles.modifiers.textExpanded);
176
+ });
177
+
178
+ test(`Does not render with class ${styles.modifiers.textExpanded} when isTextExpanded is not passed`, () => {
179
+ render(<MenuToggle>Toggle</MenuToggle>);
180
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.textExpanded);
181
+ });
182
+
183
+ test(`Does not render with class ${styles.modifiers.textExpanded} when isTextExpanded is passed but isDocked is not passed`, () => {
184
+ render(<MenuToggle isTextExpanded>Text Expanded Toggle</MenuToggle>);
185
+ expect(screen.getByRole('button')).not.toHaveClass(styles.modifiers.textExpanded);
186
+ });
187
+
188
+ test(`Renders with both ${styles.modifiers.docked} and ${styles.modifiers.textExpanded} when both props are passed`, () => {
189
+ render(
190
+ <MenuToggle isDocked isTextExpanded>
191
+ Dock Text Expanded Toggle
192
+ </MenuToggle>
193
+ );
194
+ const button = screen.getByRole('button');
195
+ expect(button).toHaveClass(styles.modifiers.docked);
196
+ expect(button).toHaveClass(styles.modifiers.textExpanded);
197
+ });
@@ -39,6 +39,8 @@ export interface NavProps
39
39
  'aria-label'?: string;
40
40
  /** The nav variant to use. Docked is in beta. */
41
41
  variant?: 'default' | 'horizontal' | 'horizontal-subnav' | 'docked';
42
+ /** @beta Flag indicating the docked nav should display text. Only applies when variant is docked. */
43
+ isTextExpanded?: boolean;
42
44
  /** Value to overwrite the randomly generated data-ouia-component-id.*/
43
45
  ouiaId?: number | string;
44
46
  /** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
@@ -119,10 +121,11 @@ class Nav extends Component<NavProps, { isScrollable: boolean; flyoutRef: React.
119
121
  ouiaId,
120
122
  ouiaSafe,
121
123
  variant,
124
+ isTextExpanded = false,
122
125
  ...props
123
126
  } = this.props;
124
127
  const isHorizontal = ['horizontal', 'horizontal-subnav'].includes(variant);
125
-
128
+ const isDocked = variant === 'docked';
126
129
  return (
127
130
  <SSRSafeIds prefix="pf-" ouiaComponentType={`Nav${variant ? `-${variant}` : ''}`}>
128
131
  {(_, generatedOuiaId) => (
@@ -154,8 +157,9 @@ class Nav extends Component<NavProps, { isScrollable: boolean; flyoutRef: React.
154
157
  className={css(
155
158
  styles.nav,
156
159
  isHorizontal && styles.modifiers.horizontal,
157
- variant === 'docked' && styles.modifiers.docked,
160
+ isDocked && styles.modifiers.docked,
158
161
  variant === 'horizontal-subnav' && styles.modifiers.subnav,
162
+ isDocked && isTextExpanded && styles.modifiers.textExpanded,
159
163
  this.state.isScrollable && styles.modifiers.scrollable,
160
164
  className
161
165
  )}
@@ -274,4 +274,49 @@ describe('Nav', () => {
274
274
  );
275
275
  expect(screen.getByTestId('docked-nav')).toHaveClass(styles.modifiers.docked);
276
276
  });
277
+
278
+ test(`Renders with ${styles.modifiers.textExpanded} class when isTextExpanded is true and variant is docked`, () => {
279
+ renderNav(
280
+ <Nav isTextExpanded variant="docked" data-testid="text-expanded-nav">
281
+ <NavList>
282
+ {props.items.map((item) => (
283
+ <NavItem to={item.to} key={item.to}>
284
+ {item.label}
285
+ </NavItem>
286
+ ))}
287
+ </NavList>
288
+ </Nav>
289
+ );
290
+ expect(screen.getByTestId('text-expanded-nav')).toHaveClass(styles.modifiers.textExpanded);
291
+ });
292
+
293
+ test(`Does not render with ${styles.modifiers.textExpanded} class when isTextExpanded is not passed`, () => {
294
+ renderNav(
295
+ <Nav data-testid="nav">
296
+ <NavList>
297
+ {props.items.map((item) => (
298
+ <NavItem to={item.to} key={item.to}>
299
+ {item.label}
300
+ </NavItem>
301
+ ))}
302
+ </NavList>
303
+ </Nav>
304
+ );
305
+ expect(screen.getByTestId('nav')).not.toHaveClass(styles.modifiers.textExpanded);
306
+ });
307
+
308
+ test(`Does not render with ${styles.modifiers.textExpanded} class when isTextExpanded is true but variant is not docked`, () => {
309
+ renderNav(
310
+ <Nav isTextExpanded data-testid="nav">
311
+ <NavList>
312
+ {props.items.map((item) => (
313
+ <NavItem to={item.to} key={item.to}>
314
+ {item.label}
315
+ </NavItem>
316
+ ))}
317
+ </NavList>
318
+ </Nav>
319
+ );
320
+ expect(screen.getByTestId('nav')).not.toHaveClass(styles.modifiers.textExpanded);
321
+ });
277
322
  });
@@ -22,8 +22,16 @@ export interface PageProps extends React.HTMLProps<HTMLDivElement> {
22
22
  className?: string;
23
23
  /** @beta Indicates the layout variant */
24
24
  variant?: 'default' | 'docked';
25
- /** Masthead component (e.g. <Masthead />) */
25
+ /** @beta Flag indicating the docked nav is expanded on mobile. Only applies when variant is docked. */
26
+ isDockExpanded?: boolean;
27
+ /** @beta Flag indicating the docked nav should display text on desktop. Only applies when variant is docked, and will handle
28
+ * setting isTextExpanded on individual isDocked components.
29
+ * */
30
+ isDockTextExpanded?: boolean;
31
+ /** The horizontal masthead content (e.g. <Masthead />). When using the docked variant, this content will only render at mobile viewports. */
26
32
  masthead?: React.ReactNode;
33
+ /** @beta Content to render in the vertical dock when variant of docked is used. At mobile viewports, this content will be replaced with the content passed to masthead. */
34
+ dockContent?: React.ReactNode;
27
35
  /** Sidebar component for a side nav, recommended to be a PageSidebar. If set to null, the page grid layout
28
36
  * will render without a sidebar.
29
37
  */
@@ -232,7 +240,10 @@ class Page extends Component<PageProps, PageState> {
232
240
  className,
233
241
  children,
234
242
  variant,
243
+ isDockExpanded = false,
244
+ isDockTextExpanded = false,
235
245
  masthead,
246
+ dockContent,
236
247
  sidebar,
237
248
  notificationDrawer,
238
249
  isNotificationDrawerExpanded,
@@ -339,7 +350,7 @@ class Page extends Component<PageProps, PageState> {
339
350
  {...rest}
340
351
  className={css(
341
352
  styles.page,
342
- variant === 'docked' && styles.modifiers.dock,
353
+ variant === 'docked' && styles.modifiers.docked,
343
354
  width !== null && height !== null && 'pf-m-resize-observer',
344
355
  width !== null && `pf-m-breakpoint-${getBreakpoint(width)}`,
345
356
  height !== null && `pf-m-height-breakpoint-${getVerticalBreakpoint(height)}`,
@@ -349,9 +360,18 @@ class Page extends Component<PageProps, PageState> {
349
360
  >
350
361
  {skipToContent}
351
362
  {variant === 'docked' ? (
352
- <div className={css(styles.pageDock)}>
353
- <div className={css(styles.pageDockMain)}>{masthead}</div>
354
- </div>
363
+ <>
364
+ {masthead}
365
+ <div
366
+ className={css(
367
+ styles.pageDock,
368
+ isDockExpanded && styles.modifiers.expanded,
369
+ isDockTextExpanded && styles.modifiers.textExpanded
370
+ )}
371
+ >
372
+ <div className={css(styles.pageDockMain)}>{dockContent}</div>
373
+ </div>
374
+ </>
355
375
  ) : (
356
376
  masthead
357
377
  )}
@@ -389,28 +389,102 @@ describe('Page', () => {
389
389
 
390
390
  expect(screen.getByRole('main').parentElement).not.toHaveClass(styles.modifiers.noFill);
391
391
  });
392
+ });
393
+
394
+ describe('Page docked variant', () => {
395
+ test(`Does not render with docked classes when variant is default`, () => {
396
+ render(<Page {...props} variant="default" data-testid="page"></Page>);
397
+
398
+ const page = screen.getByTestId('page');
399
+ expect(page).not.toHaveClass(styles.modifiers.docked);
400
+ expect(page.querySelector(`.${styles.pageDock}`)).not.toBeInTheDocument();
401
+ expect(page.querySelector(`.${styles.pageDockMain}`)).not.toBeInTheDocument();
402
+ });
403
+
404
+ test(`Does not render with docked classes when variant is not passed`, () => {
405
+ render(<Page data-testid="page"></Page>);
406
+
407
+ const page = screen.getByTestId('page');
408
+ expect(page).not.toHaveClass(styles.modifiers.docked);
409
+ expect(page.querySelector(`.${styles.pageDock}`)).not.toBeInTheDocument();
410
+ expect(page.querySelector(`.${styles.pageDockMain}`)).not.toBeInTheDocument();
411
+ });
392
412
 
393
- test(`Renders with ${styles.modifiers.dock} class when variant is docked`, () => {
413
+ test(`Renders with ${styles.modifiers.docked} class when variant is docked`, () => {
394
414
  render(<Page {...props} variant="docked" data-testid="page"></Page>);
395
415
 
396
- expect(screen.getByTestId('page')).toHaveClass(styles.modifiers.dock);
416
+ expect(screen.getByTestId('page')).toHaveClass(styles.modifiers.docked);
397
417
  });
398
418
 
399
- test(`Does not render with ${styles.modifiers.dock} class when variant is default`, () => {
400
- render(<Page {...props} variant="default" data-testid="page"></Page>);
419
+ test('Renders dock content when dockContent and variant="docked" is passed', () => {
420
+ render(<Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead</>} data-testid="page"></Page>);
401
421
 
402
- expect(screen.getByTestId('page')).not.toHaveClass(styles.modifiers.dock);
422
+ expect(screen.getByText('Dock content')).toBeInTheDocument();
403
423
  });
404
424
 
405
- test(`Does not render with ${styles.modifiers.dock} class when variant is not passed`, () => {
406
- render(<Page data-testid="page"></Page>);
407
- expect(screen.getByTestId('page')).not.toHaveClass(styles.modifiers.dock);
425
+ test('Renders masthead content when masthead and variant="docked" is passed', () => {
426
+ render(
427
+ <Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead content</>} data-testid="page"></Page>
428
+ );
429
+
430
+ expect(screen.getByText('Masthead content')).toBeInTheDocument();
431
+ });
432
+
433
+ test(`Renders with ${styles.pageDock} wrapper when variant is docked`, () => {
434
+ render(<Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead</>} data-testid="page"></Page>);
435
+
436
+ const pageDock = screen.getByText('Dock content').closest(`.${styles.pageDock}`);
437
+ expect(pageDock).toBeInTheDocument();
438
+ });
439
+
440
+ test(`Does not render with ${styles.modifiers.expanded} by default when variant is docked`, () => {
441
+ render(<Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead</>} data-testid="page"></Page>);
442
+
443
+ const pageDock = screen.getByText('Dock content').closest(`.${styles.pageDock}`);
444
+ expect(pageDock).not.toHaveClass(styles.modifiers.expanded);
445
+ });
446
+
447
+ test(`Does not render with ${styles.modifiers.textExpanded} by default when variant is docked`, () => {
448
+ render(<Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead</>} data-testid="page"></Page>);
449
+
450
+ const pageDock = screen.getByText('Dock content').closest(`.${styles.pageDock}`);
451
+ expect(pageDock).not.toHaveClass(styles.modifiers.textExpanded);
452
+ });
453
+
454
+ test(`Renders with ${styles.modifiers.expanded} when isDockExpanded is true and variant is docked`, () => {
455
+ render(
456
+ <Page
457
+ variant="docked"
458
+ isDockExpanded
459
+ dockContent={<>Dock content</>}
460
+ masthead={<>Masthead</>}
461
+ data-testid="page"
462
+ ></Page>
463
+ );
464
+
465
+ const pageDock = screen.getByText('Dock content').closest(`.${styles.pageDock}`);
466
+ expect(pageDock).toHaveClass(styles.modifiers.expanded);
467
+ });
468
+
469
+ test(`Renders with ${styles.modifiers.textExpanded} when isDockTextExpanded is true and variant is docked`, () => {
470
+ render(
471
+ <Page
472
+ variant="docked"
473
+ isDockTextExpanded
474
+ dockContent={<>Dock content</>}
475
+ masthead={<>Masthead</>}
476
+ data-testid="page"
477
+ ></Page>
478
+ );
479
+
480
+ const pageDock = screen.getByText('Dock content').closest(`.${styles.pageDock}`);
481
+ expect(pageDock).toHaveClass(styles.modifiers.textExpanded);
408
482
  });
409
483
 
410
484
  test(`Renders with ${styles.pageDockMain} wrapper when variant is docked`, () => {
411
- render(<Page variant="docked" masthead={<>Masthead</>} data-testid="page"></Page>);
485
+ render(<Page variant="docked" dockContent={<>Dock content</>} masthead={<>Masthead</>} data-testid="page"></Page>);
412
486
 
413
- const pageDockMain = screen.getByText('Masthead').closest(`.${styles.pageDockMain}`);
487
+ const pageDockMain = screen.getByText('Dock content').closest(`.${styles.pageDockMain}`);
414
488
  expect(pageDockMain).toBeInTheDocument();
415
489
  });
416
490
  });
package/src/demos/Nav.md CHANGED
@@ -3,7 +3,7 @@ id: Navigation
3
3
  section: components
4
4
  ---
5
5
 
6
- import { Fragment, useState } from 'react';
6
+ import { Fragment, useState, useRef } from 'react';
7
7
  import RhUiSettingsFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-settings-fill-icon';
8
8
  import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
9
9
  import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
@@ -12,6 +12,13 @@ import RhUiNotificationFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-
12
12
  import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
13
13
  import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg';
14
14
  import pfLogo from '@patternfly/react-core/src/demos/assets/PF-HorizontalLogo-Color.svg';
15
+ import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
16
+ import FolderIcon from '@patternfly/react-icons/dist/esm/icons/folder-icon';
17
+ import CloudIcon from '@patternfly/react-icons/dist/esm/icons/cloud-icon';
18
+ import CodeIcon from '@patternfly/react-icons/dist/esm/icons/code-icon';
19
+ import ThIcon from '@patternfly/react-icons/dist/esm/icons/th-icon';
20
+ import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
21
+ import pfIconLogo from '@patternfly/react-core/src/demos/assets/PF-IconLogo-color.svg';
15
22
  import { DashboardBreadcrumb } from '@patternfly/react-core/dist/js/demos/DashboardWrapper';
16
23
  import { DashboardHeader } from '@patternfly/react-core/dist/js/demos/DashboardHeader';
17
24
 
@@ -70,3 +77,27 @@ import { DashboardHeader } from '@patternfly/react-core/dist/js/demos/DashboardH
70
77
  ```ts isFullscreen file="./examples/Nav/NavDrilldown.tsx"
71
78
 
72
79
  ```
80
+
81
+ ### Docked nav
82
+
83
+ To save space in the UI, you can use docked navigation to replace text-labeled navigation items with relevant icons. On mobile (or narrow viewports), docked navigation appears as an overlay, while on desktop (or wider viewports) it displays as a persistent vertical sidebar that can toggle between icon-only and text+icon modes.
84
+
85
+ This demo includes the following features:
86
+
87
+ 1. A [page](/components/page) component with a docked layout, enabled via `variant="docked"`. Control the mobile overlay with `isDockExpanded` and the desktop label visibility with `isDockTextExpanded`.
88
+
89
+ 2. Two separate [masthead](/components/masthead) components:
90
+ - **Horizontal mobile masthead**: Shown on small viewports using `display={{ default: 'inline' }}`, with a hamburger menu toggle button, brand logo, and action buttons that should be immediately visible to users.
91
+ - **Vertical docked masthead**: Uses `variant="docked"` to create a thinner navigation sidebar with a hamburger menu toggle button, nav items, and action buttons.
92
+
93
+ 3. A [navigation](/components/navigation) component with `variant="docked"` and multiple `<NavItem>` components that must include both icons and text labels. To control text visibility, `isTextExpanded={isDockTextExpanded}` is passed to the `<Nav>` component.
94
+
95
+ 4. [Button](/components/button) and [menu toggle](/components/menus/menu-toggle) components, which use `isDocked` and `isTextExpanded` to toggle between icon-only and text+icon styles. When the nav is docked, and only icons are shown, [tooltips](/components/tooltip) must provide full text labels for the navigation items, buttons, and menu toggles.
96
+
97
+ 5. A `<MastheadLogo>` component that uses `isCompact` to show an icon-only logo when the dock is collapsed, and a full logo with text when expanded.
98
+
99
+ **Note**: For better keyboard accessibility, ensure that focus shifts between the hamburger menu toggle button in the mobile masthead and the docked overlay menu toggle button as the navigation is opened and closed.
100
+
101
+ ```ts file="./examples/Nav/NavDockedNav.tsx" isFullscreen
102
+
103
+ ```
package/src/demos/Page.md CHANGED
@@ -52,9 +52,3 @@ When adding a context selector/perspective switcher in a `PageSidebar`, you must
52
52
  ```ts file='./examples/Page/PageContextSelector.tsx' isFullscreen
53
53
 
54
54
  ```
55
-
56
- ### Docked nav
57
-
58
- ```ts file='./examples/Page/PageDockedNav.tsx' isFullscreen
59
-
60
- ```