@patternfly/react-core 6.6.1-prerelease.7 → 6.6.1-prerelease.9

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 (189) hide show
  1. package/CHANGELOG.md +12 -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/inert/package.json +1 -1
  105. package/dist/dynamic/helpers/package.json +1 -1
  106. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  107. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  108. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  109. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  110. package/dist/dynamic/helpers/useSSRSafeId/package.json +1 -1
  111. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  112. package/dist/dynamic/helpers/util/package.json +1 -1
  113. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  114. package/dist/dynamic/layouts/Flex/package.json +1 -1
  115. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  116. package/dist/dynamic/layouts/Grid/package.json +1 -1
  117. package/dist/dynamic/layouts/Level/package.json +1 -1
  118. package/dist/dynamic/layouts/Split/package.json +1 -1
  119. package/dist/dynamic/layouts/Stack/package.json +1 -1
  120. package/dist/dynamic/styles/package.json +1 -1
  121. package/dist/dynamic-modules.json +2 -0
  122. package/dist/esm/components/Drawer/Drawer.d.ts +3 -0
  123. package/dist/esm/components/Drawer/Drawer.d.ts.map +1 -1
  124. package/dist/esm/components/Drawer/Drawer.js +4 -3
  125. package/dist/esm/components/Drawer/Drawer.js.map +1 -1
  126. package/dist/esm/components/Drawer/DrawerContent.d.ts +1 -1
  127. package/dist/esm/components/Drawer/DrawerContent.d.ts.map +1 -1
  128. package/dist/esm/components/Drawer/DrawerPanelContent.js +22 -25
  129. package/dist/esm/components/Drawer/DrawerPanelContent.js.map +1 -1
  130. package/dist/esm/components/Page/Page.d.ts +4 -0
  131. package/dist/esm/components/Page/Page.d.ts.map +1 -1
  132. package/dist/esm/components/Page/Page.js +4 -3
  133. package/dist/esm/components/Page/Page.js.map +1 -1
  134. package/dist/esm/components/Page/PageFooter.d.ts +11 -0
  135. package/dist/esm/components/Page/PageFooter.d.ts.map +1 -0
  136. package/dist/esm/components/Page/PageFooter.js +11 -0
  137. package/dist/esm/components/Page/PageFooter.js.map +1 -0
  138. package/dist/esm/components/Page/index.d.ts +1 -0
  139. package/dist/esm/components/Page/index.d.ts.map +1 -1
  140. package/dist/esm/components/Page/index.js +1 -0
  141. package/dist/esm/components/Page/index.js.map +1 -1
  142. package/dist/js/components/Drawer/Drawer.d.ts +3 -0
  143. package/dist/js/components/Drawer/Drawer.d.ts.map +1 -1
  144. package/dist/js/components/Drawer/Drawer.js +4 -3
  145. package/dist/js/components/Drawer/Drawer.js.map +1 -1
  146. package/dist/js/components/Drawer/DrawerContent.d.ts +1 -1
  147. package/dist/js/components/Drawer/DrawerContent.d.ts.map +1 -1
  148. package/dist/js/components/Drawer/DrawerPanelContent.js +22 -25
  149. package/dist/js/components/Drawer/DrawerPanelContent.js.map +1 -1
  150. package/dist/js/components/Page/Page.d.ts +4 -0
  151. package/dist/js/components/Page/Page.d.ts.map +1 -1
  152. package/dist/js/components/Page/Page.js +4 -3
  153. package/dist/js/components/Page/Page.js.map +1 -1
  154. package/dist/js/components/Page/PageFooter.d.ts +11 -0
  155. package/dist/js/components/Page/PageFooter.d.ts.map +1 -0
  156. package/dist/js/components/Page/PageFooter.js +15 -0
  157. package/dist/js/components/Page/PageFooter.js.map +1 -0
  158. package/dist/js/components/Page/index.d.ts +1 -0
  159. package/dist/js/components/Page/index.d.ts.map +1 -1
  160. package/dist/js/components/Page/index.js +1 -0
  161. package/dist/js/components/Page/index.js.map +1 -1
  162. package/dist/umd/assets/{output-BGwwN1RO.css → output-B35Oc6dG.css} +15784 -15727
  163. package/dist/umd/react-core.min.js +1 -1
  164. package/helpers/package.json +1 -1
  165. package/layouts/package.json +1 -1
  166. package/next/package.json +1 -1
  167. package/package.json +6 -6
  168. package/src/components/Drawer/Drawer.tsx +10 -2
  169. package/src/components/Drawer/DrawerContent.tsx +1 -1
  170. package/src/components/Drawer/DrawerPanelContent.tsx +25 -25
  171. package/src/components/Drawer/__tests__/Drawer.test.tsx +53 -0
  172. package/src/components/Drawer/examples/Drawer.md +1 -1
  173. package/src/components/Page/Page.tsx +10 -1
  174. package/src/components/Page/PageFooter.tsx +28 -0
  175. package/src/components/Page/__tests__/Page.test.tsx +46 -0
  176. package/src/components/Page/__tests__/PageFooter.test.tsx +38 -0
  177. package/src/components/Page/examples/Page.md +24 -8
  178. package/src/components/Page/examples/PageCustomFooter.tsx +59 -0
  179. package/src/components/Page/examples/PageCustomHeader.tsx +17 -0
  180. package/src/components/Page/examples/PagePlain.tsx +9 -0
  181. package/src/components/Page/index.ts +1 -0
  182. package/src/demos/CardView/CardView.md +1 -1
  183. package/src/demos/CardView/examples/CardView.tsx +1 -1
  184. package/src/demos/Drawer/Drawer.md +16 -0
  185. package/src/demos/Drawer/examples/DrawerViewport.tsx +112 -0
  186. package/src/demos/Page.md +24 -11
  187. package/src/demos/examples/Page/PagePlain.tsx +342 -0
  188. package/src/components/Page/examples/PageHeaderContent.tsx +0 -19
  189. package/src/demos/examples/Page/PageStickySectionGroupUsingPageHeader.tsx +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.6.1-prerelease.6","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.6.1-prerelease.8","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.6.1-prerelease.6","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.6.1-prerelease.8","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.6.1-prerelease.6","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.6.1-prerelease.8","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "6.6.1-prerelease.7",
3
+ "version": "6.6.1-prerelease.9",
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.6.1-prerelease.2",
50
- "@patternfly/react-styles": "^6.6.1-prerelease.1",
51
- "@patternfly/react-tokens": "^6.6.1-prerelease.1",
49
+ "@patternfly/react-icons": "^6.6.1-prerelease.4",
50
+ "@patternfly/react-styles": "^6.6.1-prerelease.3",
51
+ "@patternfly/react-tokens": "^6.6.1-prerelease.3",
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.6.0-prerelease.39",
57
+ "@patternfly/patternfly": "6.6.0-prerelease.41",
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": "a2d7f2396e73fb6f9e599dcfcaba23c6fb0d042d"
66
+ "gitHead": "103c595fefeee9e6cf6209cc94154b0551702f18"
67
67
  }
@@ -23,6 +23,8 @@ export interface DrawerProps extends React.HTMLProps<HTMLDivElement>, OUIAProps
23
23
  isInline?: boolean;
24
24
  /** @beta Indicates if the drawer will have pill styles */
25
25
  isPill?: boolean;
26
+ /** @beta Positions the drawer as fixed to fill the viewport. Place the drawer after Page as a sibling. */
27
+ isViewport?: boolean;
26
28
  /** Indicates if the drawer will always show both content and panel. */
27
29
  isStatic?: boolean;
28
30
  /** Position of the drawer panel. left and right are deprecated, use start and end instead. */
@@ -43,6 +45,7 @@ export interface DrawerContextProps {
43
45
  drawerRef?: React.RefObject<HTMLDivElement | null>;
44
46
  drawerContentRef?: React.RefObject<HTMLDivElement | null>;
45
47
  isInline: boolean;
48
+ isViewport: boolean;
46
49
  }
47
50
 
48
51
  export const DrawerContext = createContext<Partial<DrawerContextProps>>({
@@ -52,7 +55,8 @@ export const DrawerContext = createContext<Partial<DrawerContextProps>>({
52
55
  position: 'end',
53
56
  drawerRef: null,
54
57
  drawerContentRef: null,
55
- isInline: false
58
+ isInline: false,
59
+ isViewport: false
56
60
  });
57
61
 
58
62
  export const Drawer: React.FunctionComponent<DrawerProps> = ({
@@ -61,6 +65,7 @@ export const Drawer: React.FunctionComponent<DrawerProps> = ({
61
65
  isExpanded = false,
62
66
  isInline = false,
63
67
  isPill = false,
68
+ isViewport = false,
64
69
  isStatic = false,
65
70
  position = 'end',
66
71
  onExpand = () => {},
@@ -73,13 +78,16 @@ export const Drawer: React.FunctionComponent<DrawerProps> = ({
73
78
  const drawerContentRef = useRef<HTMLDivElement>(undefined);
74
79
 
75
80
  return (
76
- <DrawerContext.Provider value={{ isExpanded, isStatic, onExpand, position, drawerRef, drawerContentRef, isInline }}>
81
+ <DrawerContext.Provider
82
+ value={{ isExpanded, isStatic, onExpand, position, drawerRef, drawerContentRef, isInline, isViewport }}
83
+ >
77
84
  <div
78
85
  className={css(
79
86
  styles.drawer,
80
87
  isExpanded && styles.modifiers.expanded,
81
88
  isInline && styles.modifiers.inline,
82
89
  isPill && styles.modifiers.pill,
90
+ isViewport && styles.modifiers.viewport,
83
91
  isStatic && styles.modifiers.static,
84
92
  (position === 'left' || position === 'start') && styles.modifiers.panelLeft,
85
93
  position === 'bottom' && styles.modifiers.panelBottom,
@@ -13,7 +13,7 @@ export enum DrawerContentColorVariant {
13
13
  export interface DrawerContentProps extends React.HTMLProps<HTMLDivElement> {
14
14
  /** Additional classes added to the Drawer. */
15
15
  className?: string;
16
- /** Content to be rendered in the drawer. */
16
+ /** Content to be rendered in the drawer. Can be omitted to leave the content container empty. */
17
17
  children?: React.ReactNode;
18
18
  /** Content rendered in the drawer panel. */
19
19
  panelContent: React.ReactNode;
@@ -100,7 +100,8 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
100
100
  const panel = useRef<HTMLDivElement>(undefined);
101
101
  const splitterRef = useRef<HTMLDivElement>(undefined);
102
102
  const [separatorValue, setSeparatorValue] = useState(0);
103
- const { position, isExpanded, isStatic, onExpand, drawerRef, drawerContentRef, isInline } = useContext(DrawerContext);
103
+ const { position, isExpanded, isStatic, onExpand, drawerRef, drawerContentRef, isInline, isViewport } =
104
+ useContext(DrawerContext);
104
105
  const hidden = isStatic ? false : !isExpanded;
105
106
  const [isExpandedInternal, setIsExpandedInternal] = useState(!hidden);
106
107
  const [isFocusTrapActive, setIsFocusTrapActive] = useState(false);
@@ -126,9 +127,22 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
126
127
  }
127
128
  }, [isStatic, isExpanded]);
128
129
 
130
+ const getSizeElement = () => {
131
+ if (isViewport) {
132
+ return drawerRef?.current ?? drawerContentRef?.current;
133
+ }
134
+ return drawerContentRef?.current ?? drawerRef?.current;
135
+ };
136
+
129
137
  const calcValueNow = () => {
130
138
  let splitterPos;
131
139
  let drawerSize;
140
+ const sizeEl = getSizeElement();
141
+
142
+ if (!sizeEl || !panel.current || !splitterRef.current || !drawerRef?.current) {
143
+ return 0;
144
+ }
145
+
132
146
  const isRTL = getLanguageDirection(panel.current) === 'rtl';
133
147
 
134
148
  if (isInline && (position === 'end' || position === 'right')) {
@@ -149,37 +163,23 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
149
163
  }
150
164
  } else if (position === 'end' || position === 'right') {
151
165
  if (isRTL) {
152
- splitterPos =
153
- drawerContentRef.current.getBoundingClientRect().left - splitterRef.current.getBoundingClientRect().right;
154
- drawerSize =
155
- drawerContentRef.current.getBoundingClientRect().left -
156
- drawerContentRef.current.getBoundingClientRect().right;
166
+ splitterPos = sizeEl.getBoundingClientRect().left - splitterRef.current.getBoundingClientRect().right;
167
+ drawerSize = sizeEl.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
157
168
  } else {
158
- splitterPos =
159
- drawerContentRef.current.getBoundingClientRect().right - splitterRef.current.getBoundingClientRect().left;
160
- drawerSize =
161
- drawerContentRef.current.getBoundingClientRect().right -
162
- drawerContentRef.current.getBoundingClientRect().left;
169
+ splitterPos = sizeEl.getBoundingClientRect().right - splitterRef.current.getBoundingClientRect().left;
170
+ drawerSize = sizeEl.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
163
171
  }
164
172
  } else if (position === 'start' || position === 'left') {
165
173
  if (isRTL) {
166
- splitterPos =
167
- splitterRef.current.getBoundingClientRect().left - drawerContentRef.current.getBoundingClientRect().right;
168
- drawerSize =
169
- drawerContentRef.current.getBoundingClientRect().left -
170
- drawerContentRef.current.getBoundingClientRect().right;
174
+ splitterPos = splitterRef.current.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
175
+ drawerSize = sizeEl.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
171
176
  } else {
172
- splitterPos =
173
- splitterRef.current.getBoundingClientRect().right - drawerContentRef.current.getBoundingClientRect().left;
174
- drawerSize =
175
- drawerContentRef.current.getBoundingClientRect().right -
176
- drawerContentRef.current.getBoundingClientRect().left;
177
+ splitterPos = splitterRef.current.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
178
+ drawerSize = sizeEl.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
177
179
  }
178
180
  } else if (position === 'bottom') {
179
- splitterPos =
180
- drawerContentRef.current.getBoundingClientRect().bottom - splitterRef.current.getBoundingClientRect().top;
181
- drawerSize =
182
- drawerContentRef.current.getBoundingClientRect().bottom - drawerContentRef.current.getBoundingClientRect().top;
181
+ splitterPos = sizeEl.getBoundingClientRect().bottom - splitterRef.current.getBoundingClientRect().top;
182
+ drawerSize = sizeEl.getBoundingClientRect().bottom - sizeEl.getBoundingClientRect().top;
183
183
  }
184
184
 
185
185
  const newSplitterPos = (splitterPos / drawerSize) * 100;
@@ -231,3 +231,56 @@ test('Renders with ouiaSafe=false when specified', () => {
231
231
  );
232
232
  expect(screen.getByTestId('drawer')).toHaveAttribute('data-ouia-safe', 'false');
233
233
  });
234
+
235
+ test(`Does not render with ${styles.modifiers.viewport} class by default`, () => {
236
+ render(
237
+ <Drawer data-testid="drawer">
238
+ <DrawerContent panelContent={<DrawerPanelContent>panel</DrawerPanelContent>}>
239
+ <DrawerContentBody>content</DrawerContentBody>
240
+ </DrawerContent>
241
+ </Drawer>
242
+ );
243
+
244
+ expect(screen.getByTestId('drawer')).not.toHaveClass(styles.modifiers.viewport);
245
+ });
246
+
247
+ test(`Renders with ${styles.modifiers.viewport} when isViewport is specified`, () => {
248
+ render(
249
+ <Drawer data-testid="drawer" isViewport>
250
+ <DrawerContent panelContent={<DrawerPanelContent>panel</DrawerPanelContent>}>
251
+ <DrawerContentBody>content</DrawerContentBody>
252
+ </DrawerContent>
253
+ </Drawer>
254
+ );
255
+
256
+ expect(screen.getByTestId('drawer')).toHaveClass(styles.modifiers.viewport);
257
+ });
258
+
259
+ test('Resizeable DrawerPanelContent without drawer content does not throw', async () => {
260
+ const consoleError = jest.spyOn(console, 'error').mockImplementation();
261
+
262
+ const panelContent = (
263
+ <DrawerPanelContent isResizable>
264
+ <DrawerHead>
265
+ <span>drawer-panel</span>
266
+ <DrawerActions>
267
+ <DrawerCloseButton />
268
+ </DrawerActions>
269
+ </DrawerHead>
270
+ </DrawerPanelContent>
271
+ );
272
+
273
+ const user = userEvent.setup();
274
+
275
+ render(
276
+ <Drawer isExpanded isViewport>
277
+ <DrawerContent panelContent={panelContent} />
278
+ </Drawer>
279
+ );
280
+
281
+ await user.tab();
282
+ await user.keyboard(`{${KeyTypes.ArrowLeft}}`);
283
+
284
+ expect(consoleError).not.toHaveBeenCalled();
285
+ consoleError.mockRestore();
286
+ });
@@ -13,7 +13,7 @@ propComponents:
13
13
  DrawerCloseButton,
14
14
  DrawerPanelDescription,
15
15
  DrawerPanelBody,
16
- DrawerPanelFocusTrapObject,
16
+ DrawerPanelFocusTrapObject
17
17
  ]
18
18
  section: components
19
19
  ---
@@ -118,6 +118,10 @@ export interface PageProps extends React.HTMLProps<HTMLDivElement> {
118
118
  breadcrumbProps?: PageBreadcrumbProps;
119
119
  /** Enables children to fill the available vertical space. Child page sections or groups that should fill should be passed the isFilled property. */
120
120
  isContentFilled?: boolean;
121
+ /** Flag indicating the page should render without the content area background and overflow scroll. */
122
+ isPlain?: boolean;
123
+ /** Content rendered inside the page footer */
124
+ footer?: React.ReactNode;
121
125
  }
122
126
 
123
127
  export interface PageState {
@@ -141,7 +145,8 @@ class Page extends Component<PageProps, PageState> {
141
145
  mainComponent: 'main',
142
146
  getBreakpoint,
143
147
  getVerticalBreakpoint,
144
- mainRef: undefined
148
+ mainRef: undefined,
149
+ isPlain: false
145
150
  };
146
151
  mainRef = this.props?.mainRef ? this.props.mainRef : createRef<HTMLDivElement>();
147
152
  pageRef = createRef<HTMLDivElement>();
@@ -284,6 +289,8 @@ class Page extends Component<PageProps, PageState> {
284
289
  isContentFilled,
285
290
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
286
291
  mainRef,
292
+ isPlain,
293
+ footer,
287
294
  ...rest
288
295
  } = this.props;
289
296
  const { mobileView, mobileIsSidebarOpen, desktopIsSidebarOpen, width, height } = this.state;
@@ -367,6 +374,7 @@ class Page extends Component<PageProps, PageState> {
367
374
  width !== null && `pf-m-breakpoint-${getBreakpoint(width)}`,
368
375
  height !== null && `pf-m-height-breakpoint-${getVerticalBreakpoint(height)}`,
369
376
  sidebar === null && styles.modifiers.noSidebar,
377
+ isPlain && styles.modifiers.plain,
370
378
  className
371
379
  )}
372
380
  >
@@ -397,6 +405,7 @@ class Page extends Component<PageProps, PageState> {
397
405
  </div>
398
406
  )}
399
407
  {!notificationDrawer && main}
408
+ {footer && footer}
400
409
  </div>
401
410
  </PageContextProvider>
402
411
  );
@@ -0,0 +1,28 @@
1
+ import styles from '@patternfly/react-styles/css/components/Page/page';
2
+ import { css } from '@patternfly/react-styles';
3
+
4
+ export interface PageFooterProps extends React.HTMLProps<HTMLElement> {
5
+ /** Content rendered inside the footer */
6
+ children?: React.ReactNode;
7
+ /** Additional classes added to the footer */
8
+ className?: string;
9
+ /** Sets the base component to render. Defaults to footer */
10
+ component?: keyof React.JSX.IntrinsicElements;
11
+ }
12
+
13
+ export const PageFooter: React.FunctionComponent<PageFooterProps> = ({
14
+ className,
15
+ children,
16
+ component = 'footer',
17
+ ...props
18
+ }: PageFooterProps) => {
19
+ const Component = component as any;
20
+
21
+ return (
22
+ <Component {...props} className={css(styles.pageFooter, className)}>
23
+ {children}
24
+ </Component>
25
+ );
26
+ };
27
+
28
+ PageFooter.displayName = 'PageFooter';
@@ -13,6 +13,7 @@ import { PageHeader } from '../PageHeader';
13
13
  import { Masthead } from '../../Masthead';
14
14
 
15
15
  import styles from '@patternfly/react-styles/css/components/Page/page';
16
+ import { PageFooter } from '../PageFooter';
16
17
 
17
18
  const props = {
18
19
  'aria-label': 'Page layout',
@@ -520,4 +521,49 @@ describe('Page docked variant', () => {
520
521
  expect(header).toHaveClass(styles.pageHeader);
521
522
  expect(header.parentElement).toHaveClass(styles.page);
522
523
  });
524
+
525
+ test('Renders PageFooter when passed to the footer prop', () => {
526
+ render(
527
+ <Page {...props} footer={<PageFooter>Custom footer</PageFooter>}>
528
+ <PageSection>Custom content</PageSection>
529
+ </Page>
530
+ );
531
+
532
+ const footer = screen.getByRole('contentinfo');
533
+ expect(footer).toHaveClass(styles.pageFooter);
534
+ expect(footer.parentElement).toHaveClass(styles.page);
535
+ });
536
+
537
+ test(`Renders with ${styles.modifiers.plain} when isPlain is true`, () => {
538
+ render(
539
+ <Page {...props} isPlain data-testid="page">
540
+ <PageSection>Custom content</PageSection>
541
+ </Page>
542
+ );
543
+
544
+ const page = screen.getByTestId('page');
545
+ expect(page).toHaveClass(styles.modifiers.plain);
546
+ });
547
+
548
+ test(`Does not render with ${styles.modifiers.plain} when isPlain is false`, () => {
549
+ render(
550
+ <Page {...props} isPlain={false} data-testid="page">
551
+ <PageSection>Custom content</PageSection>
552
+ </Page>
553
+ );
554
+
555
+ const page = screen.getByTestId('page');
556
+ expect(page).not.toHaveClass(styles.modifiers.plain);
557
+ });
558
+
559
+ test(`Does not render with ${styles.modifiers.plain} when isPlain is not passed`, () => {
560
+ render(
561
+ <Page {...props} data-testid="page">
562
+ <PageSection>Custom content</PageSection>
563
+ </Page>
564
+ );
565
+
566
+ const page = screen.getByTestId('page');
567
+ expect(page).not.toHaveClass(styles.modifiers.plain);
568
+ });
523
569
  });
@@ -0,0 +1,38 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import styles from '@patternfly/react-styles/css/components/Page/page';
3
+ import { PageFooter } from '../PageFooter';
4
+
5
+ test('Renders children', () => {
6
+ render(<PageFooter>Footer content</PageFooter>);
7
+ expect(screen.getByRole('contentinfo')).toBeVisible();
8
+ });
9
+
10
+ test('Renders without children', () => {
11
+ render(<PageFooter data-testid="footer" />);
12
+ expect(screen.getByTestId('footer')).toBeVisible();
13
+ });
14
+
15
+ test(`Renders with class ${styles.pageFooter} by default`, () => {
16
+ render(<PageFooter>Footer content</PageFooter>);
17
+ expect(screen.getByRole('contentinfo')).toHaveClass(styles.pageFooter, { exact: true });
18
+ });
19
+
20
+ test('Renders as a footer by default', () => {
21
+ render(<PageFooter>Footer content</PageFooter>);
22
+ expect(screen.getByRole('contentinfo').tagName).toBe('FOOTER');
23
+ });
24
+
25
+ test('Renders as a custom component when component is passed', () => {
26
+ render(<PageFooter component="div">Footer content</PageFooter>);
27
+ expect(screen.getByText('Footer content').tagName).toBe('DIV');
28
+ });
29
+
30
+ test('Renders with custom classes when className is passed', () => {
31
+ render(<PageFooter className="custom-class">Footer content</PageFooter>);
32
+ expect(screen.getByRole('contentinfo')).toHaveClass('custom-class');
33
+ });
34
+
35
+ test('Renders with spread props', () => {
36
+ render(<PageFooter id="custom-id">Footer content</PageFooter>);
37
+ expect(screen.getByRole('contentinfo')).toHaveAttribute('id', 'custom-id');
38
+ });
@@ -33,14 +33,6 @@ The `<MastheadMain>` component includes the smaller area that typically contains
33
33
  - 1 or more `<PageSidebarBody>` components inside `<PageSidebar>` for vertical navigation or other sidebar content
34
34
  - 1 or more `<PageSection>` components
35
35
 
36
- ### Page header
37
-
38
- To use a page header instead of passing a [masthead](/components/masthead) directly, pass a `<PageHeader>` to the `masthead` property. `<PageHeader>` should only be used to wrap custom header content.
39
-
40
- ```ts file="./PageHeaderContent.tsx"
41
-
42
- ```
43
-
44
36
  ### Vertical navigation
45
37
 
46
38
  To add a vertical sidebar to a `<Page>`, pass a `<PageSidebar>` component into the `sidebar` property. To render navigation in the sidebar, pass a `<PageSidebarBody>` component to `<PageSidebar>`.
@@ -158,3 +150,27 @@ In this example, a scroll event listener on the scroll parent container toggles
158
150
  ```ts file="./PageDynamicStickySection.tsx"
159
151
 
160
152
  ```
153
+
154
+ ### Custom header
155
+
156
+ To use a page header instead of passing a [masthead](/components/masthead) directly, pass a `<PageHeader>` to the `masthead` property. `<PageHeader>` should only be used to wrap custom header content.
157
+
158
+ ```ts isBeta file="./PageCustomHeader.tsx"
159
+
160
+ ```
161
+
162
+ ### Footer
163
+
164
+ `<PageFooter>` can be passed to the `footer` property, and should be used to wrap custom footer content.
165
+
166
+ ```ts isBeta file="./PageCustomFooter.tsx"
167
+
168
+ ```
169
+
170
+ ### Plain
171
+
172
+ When a page should not have the default content area background or overflow scrolling, pass the `isPlain` property to `<Page>`.
173
+
174
+ ```ts isBeta file="./PagePlain.tsx"
175
+
176
+ ```
@@ -0,0 +1,59 @@
1
+ import { useState } from 'react';
2
+ import {
3
+ Masthead,
4
+ MastheadMain,
5
+ MastheadToggle,
6
+ MastheadBrand,
7
+ MastheadLogo,
8
+ MastheadContent,
9
+ Page,
10
+ PageFooter,
11
+ PageSection,
12
+ PageSidebar,
13
+ PageSidebarBody,
14
+ PageToggleButton
15
+ } from '@patternfly/react-core';
16
+
17
+ export const PageCustomFooter: React.FunctionComponent = () => {
18
+ const [isSidebarOpen, setIsSidebarOpen] = useState(true);
19
+
20
+ const onSidebarToggle = () => {
21
+ setIsSidebarOpen(!isSidebarOpen);
22
+ };
23
+
24
+ const masthead = (
25
+ <Masthead>
26
+ <MastheadMain>
27
+ <MastheadToggle>
28
+ <PageToggleButton
29
+ aria-label="Global navigation"
30
+ isSidebarOpen={isSidebarOpen}
31
+ onSidebarToggle={onSidebarToggle}
32
+ id="custom-footer-body-nav-toggle"
33
+ isHamburgerButton
34
+ />
35
+ </MastheadToggle>
36
+ <MastheadBrand>
37
+ <MastheadLogo href="https://patternfly.org" target="_blank">
38
+ Logo
39
+ </MastheadLogo>
40
+ </MastheadBrand>
41
+ </MastheadMain>
42
+ <MastheadContent>Content</MastheadContent>
43
+ </Masthead>
44
+ );
45
+
46
+ const sidebar = (
47
+ <PageSidebar isSidebarOpen={isSidebarOpen} id="custom-footer-vertical-sidebar">
48
+ <PageSidebarBody>Navigation</PageSidebarBody>
49
+ </PageSidebar>
50
+ );
51
+
52
+ return (
53
+ <Page footer={<PageFooter>Page footer</PageFooter>} masthead={masthead} sidebar={sidebar}>
54
+ <PageSection aria-labelledby="custom-footer-section">
55
+ <h2 id="custom-footer-section">Page content</h2>
56
+ </PageSection>
57
+ </Page>
58
+ );
59
+ };
@@ -0,0 +1,17 @@
1
+ import { Page, PageHeader, PageSection, PageSidebar, PageSidebarBody } from '@patternfly/react-core';
2
+
3
+ export const PageCustomHeader: React.FunctionComponent = () => {
4
+ const sidebar = (
5
+ <PageSidebar isSidebarOpen id="custom-header-vertical-sidebar">
6
+ <PageSidebarBody>Navigation</PageSidebarBody>
7
+ </PageSidebar>
8
+ );
9
+
10
+ return (
11
+ <Page masthead={<PageHeader>Custom page header</PageHeader>} sidebar={sidebar}>
12
+ <PageSection aria-labelledby="custom-header-section">
13
+ <h2 id="custom-header-section">Page content</h2>
14
+ </PageSection>
15
+ </Page>
16
+ );
17
+ };
@@ -0,0 +1,9 @@
1
+ import { Page, PageHeader, PageSection } from '@patternfly/react-core';
2
+
3
+ export const PagePlain: React.FunctionComponent = () => (
4
+ <Page isPlain masthead={<PageHeader>Custom header</PageHeader>} sidebar={null}>
5
+ <PageSection aria-labelledby="plain-page-section">
6
+ <h2 id="plain-page-section">Page content</h2>
7
+ </PageSection>
8
+ </Page>
9
+ );
@@ -1,6 +1,7 @@
1
1
  export * from './Page';
2
2
  export * from './PageBody';
3
3
  export * from './PageBreadcrumb';
4
+ export * from './PageFooter';
4
5
  export * from './PageGroup';
5
6
  export * from './PageHeader';
6
7
  export * from './PageSidebar';
@@ -6,7 +6,7 @@ section: patterns
6
6
  import { Fragment, useState } from 'react';
7
7
  import RhUiTrashFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-trash-fill-icon';
8
8
  import RhUiAddCircleFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-add-circle-fill-icon';
9
- import pfIcon from '../assets/pf-logo-small.svg';
9
+ import pfIcon from '@patternfly/react-core/src/demos/assets/PF-IconLogo.svg';
10
10
  import activeMQIcon from '../assets/activemq-core_200x150.png';
11
11
  import avroIcon from '../assets/camel-avro_200x150.png';
12
12
  import dropBoxIcon from '../assets/camel-dropbox_200x150.png';
@@ -36,7 +36,7 @@ import {
36
36
  } from '@patternfly/react-core';
37
37
  import RhUiTrashFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-trash-fill-icon';
38
38
  import RhUiAddCircleFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-add-circle-fill-icon';
39
- import pfIcon from './assets/pf-logo-small.svg';
39
+ import pfIcon from '@patternfly/react-core/src/demos/assets/PF-IconLogo.svg';
40
40
  import activeMQIcon from './assets/activemq-core_200x150.png';
41
41
  import avroIcon from './assets/camel-avro_200x150.png';
42
42
  import dropBoxIcon from './assets/camel-dropbox_200x150.png';
@@ -0,0 +1,16 @@
1
+ ---
2
+ id: Drawer
3
+ section: components
4
+ ---
5
+
6
+ import { Fragment, useRef, useState } from 'react';
7
+
8
+ ## Demos
9
+
10
+ ### Expanded resizable viewport drawer
11
+
12
+ Use `isViewport` to position the drawer as `fixed` so it fills the viewport. Place the drawer after `<Page>` as a sibling. Omit children of `<DrawerContent>` so the content container stays empty; the empty container is still required so the panel can overlay the page.
13
+
14
+ ```ts file="./examples/DrawerViewport.tsx" isFullscreen isBeta
15
+
16
+ ```