diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 66c3c96bad8..316af25fd83 100644 --- a/packages/react-core/package.json +++ b/packages/react-core/package.json @@ -54,7 +54,7 @@ "tslib": "^2.8.1" }, "devDependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.72", + "@patternfly/patternfly": "6.5.0-prerelease.77", "case-anything": "^3.1.2", "css": "^3.0.0", "fs-extra": "^11.3.3" diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index 78f21c60ce0..5a0cf48de75 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -38,8 +38,12 @@ export interface ToolbarProps extends React.HTMLProps, OUIAProps isFullHeight?: boolean; /** Flag indicating the toolbar is static */ isStatic?: boolean; - /** Flag indicating the toolbar should stick to the top of its container */ + /** Flag indicating the toolbar should stick to the top of its container. This property applies both the sticky position and styling. */ isSticky?: boolean; + /** @beta Flag indicating the toolbar should have sticky positioning to the top of its container */ + isStickyBase?: boolean; + /** @beta Flag indicating the toolbar should have stuck styling, when the toolbar is not at the top of the scroll container */ + isStickyStuck?: boolean; /** @beta Flag indicating the toolbar has a vertical orientation */ isVertical?: boolean; /** Insets at various breakpoints. */ @@ -144,6 +148,8 @@ class Toolbar extends Component { children, isFullHeight, isStatic, + isStickyBase, + isStickyStuck, inset, isSticky, isVertical, @@ -171,6 +177,8 @@ class Toolbar extends Component { isFullHeight && styles.modifiers.fullHeight, isStatic && styles.modifiers.static, isSticky && styles.modifiers.sticky, + isStickyBase && styles.modifiers.stickyBase, + isStickyStuck && styles.modifiers.stickyStuck, isVertical && styles.modifiers.vertical, formatBreakpointMods(inset, styles, '', getBreakpoint(width)), colorVariant === 'primary' && styles.modifiers.primary, diff --git a/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx b/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx index fcd5c5b6219..b515b858436 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx @@ -220,7 +220,7 @@ describe('Toolbar', () => { expect(screen.getByTestId('Toolbar-test-is-not-vertical')).not.toHaveClass(styles.modifiers.vertical); }); - it('Renders with class ${styles.modifiers.vertical} when isVertical is true', () => { + it(`Renders with class ${styles.modifiers.vertical} when isVertical is true`, () => { const items = ( Test @@ -238,4 +238,39 @@ describe('Toolbar', () => { expect(screen.getByTestId('Toolbar-test-is-vertical')).toHaveClass(styles.modifiers.vertical); }); + + it(`Does not add ${styles.modifiers.stickyBase} and ${styles.modifiers.stickyStuck} classes by default`, () => { + render( + + + Test + + + ); + const el = screen.getByTestId('toolbar-sticky-default'); + expect(el).not.toHaveClass(styles.modifiers.stickyBase); + expect(el).not.toHaveClass(styles.modifiers.stickyStuck); + }); + + it(`Adds ${styles.modifiers.stickyBase} when isStickyBase is true`, () => { + render( + + + Test + + + ); + expect(screen.getByTestId('toolbar-sticky-base')).toHaveClass(styles.modifiers.stickyBase); + }); + + it(`Adds ${styles.modifiers.stickyStuck} when isStickyStuck is true`, () => { + render( + + + Test + + + ); + expect(screen.getByTestId('toolbar-sticky-stuck')).toHaveClass(styles.modifiers.stickyStuck); + }); }); diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index 89639b516c7..8554fcb83a2 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -5,7 +5,7 @@ propComponents: ['Toolbar', 'ToolbarContent', 'ToolbarGroup', 'ToolbarItem', 'To section: components --- -import { Fragment, useState } from 'react'; +import { Fragment, useState, useLayoutEffect, useRef } from 'react'; import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; @@ -44,6 +44,14 @@ In the following example, toggle the "is toolbar sticky" checkbox to see the dif ``` +### Dynamic sticky toolbar + +A toolbar may alternatively be made sticky with two properties: `isStickyBase` and `isStickyStuck` - which allows separate control of the sticky position and sticky styling respectively. In this example, `isStickyStuck` is only applied when the sticky element is not at the top of the scroll parent container. + +```ts file="./ToolbarDynamicSticky.tsx" + +``` + ### With groups of items You can group similar items together to create desired associations and to enable items to respond to changes in viewport width together. @@ -114,11 +122,13 @@ When all of a toolbar's required elements cannot fit in a single line, you can s ``` ## Examples with spacers and wrapping + You may adjust the space between toolbar items to arrange them into groups. Read our spacers documentation to learn more about using spacers. Items are spaced “16px” apart by default and can be modified by changing their or their parents' `gap`, `columnGap`, and `rowGap` properties. You can set the property values at multiple breakpoints, including "default", "md", "lg", "xl", and "2xl". ### Toolbar content wrapping + The toolbar content section will wrap by default, but you can set the `rowRap` property to `noWrap` to make it not wrap. ```ts file="./ToolbarContentWrap.tsx" diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx new file mode 100644 index 00000000000..89272966a43 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx @@ -0,0 +1,75 @@ +import { useLayoutEffect, useState, useRef } from 'react'; +import { Toolbar, ToolbarItem, ToolbarContent, SearchInput, Checkbox } from '@patternfly/react-core'; + +const useIsStuckFromScrollParent = ({ + shouldTrack, + scrollParentRef +}: { + /** Indicates whether to track the scroll top position of the scroll parent element */ + shouldTrack: boolean; + /** Reference to the scroll parent element */ + scrollParentRef: React.RefObject; +}): boolean => { + const [isStuck, setIsStuck] = useState(false); + + useLayoutEffect(() => { + if (!shouldTrack) { + setIsStuck(false); + return; + } + + const scrollElement = scrollParentRef.current; + if (!scrollElement) { + setIsStuck(false); + return; + } + + const syncFromScroll = () => { + setIsStuck(scrollElement.scrollTop > 0); + }; + syncFromScroll(); + scrollElement.addEventListener('scroll', syncFromScroll, { passive: true }); + return () => scrollElement.removeEventListener('scroll', syncFromScroll); + }, [shouldTrack, scrollParentRef]); + + return isStuck; +}; + +export const ToolbarDynamicSticky = () => { + const scrollParentRef = useRef(null); + const isStickyStuck = useIsStuckFromScrollParent({ shouldTrack: true, scrollParentRef }); + const [showEvenOnly, setShowEvenOnly] = useState(true); + const [searchValue, setSearchValue] = useState(''); + const array = Array.from(Array(30), (_, x) => x); // create array of numbers from 1-30 for demo purposes + const numbers = showEvenOnly ? array.filter((number) => number % 2 === 0) : array; + + return ( +
+ + + + setSearchValue(value)} + onClear={() => setSearchValue('')} + /> + + + setShowEvenOnly(checked)} + id="showOnlyEvenCheckbox" + /> + + + +
    + {numbers.map((number) => ( +
  • {`item ${number}`}
  • + ))} +
+
+ ); +}; diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index d0c59f47275..6163b12700d 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -23,7 +23,7 @@ "test:a11y": "patternfly-a11y --config patternfly-a11y.config" }, "dependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.72", + "@patternfly/patternfly": "6.5.0-prerelease.77", "@patternfly/react-charts": "workspace:^", "@patternfly/react-code-editor": "workspace:^", "@patternfly/react-core": "workspace:^", diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json index 0c1b3776c46..5302b05f4ee 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -35,7 +35,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-regular-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@patternfly/patternfly": "6.5.0-prerelease.71", + "@patternfly/patternfly": "6.5.0-prerelease.77", "@rhds/icons": "^2.1.0", "fs-extra": "^11.3.3", "tslib": "^2.8.1" diff --git a/packages/react-styles/package.json b/packages/react-styles/package.json index 9a8d7f8b9d0..bc2161351fe 100644 --- a/packages/react-styles/package.json +++ b/packages/react-styles/package.json @@ -19,7 +19,7 @@ "clean": "rimraf dist css" }, "devDependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.72", + "@patternfly/patternfly": "6.5.0-prerelease.77", "change-case": "^5.4.4", "fs-extra": "^11.3.3" }, diff --git a/packages/react-tokens/package.json b/packages/react-tokens/package.json index f42ba24a8a2..2979ed84bd4 100644 --- a/packages/react-tokens/package.json +++ b/packages/react-tokens/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@adobe/css-tools": "^4.4.4", - "@patternfly/patternfly": "6.5.0-prerelease.72", + "@patternfly/patternfly": "6.5.0-prerelease.77", "fs-extra": "^11.3.3" } } diff --git a/yarn.lock b/yarn.lock index 202af09e172..db67cae33af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,17 +5070,10 @@ __metadata: languageName: node linkType: hard -"@patternfly/patternfly@npm:6.5.0-prerelease.71": - version: 6.5.0-prerelease.71 - resolution: "@patternfly/patternfly@npm:6.5.0-prerelease.71" - checksum: 10c0/08ab7878527666704ae99f5250e1a0446959143f744d4f0f9508aeb7e2a143dd088b8dc6bb4b0ca6679e9923d5c7cd8068d176070bce7b0c7992c48a865864d9 - languageName: node - linkType: hard - -"@patternfly/patternfly@npm:6.5.0-prerelease.72": - version: 6.5.0-prerelease.72 - resolution: "@patternfly/patternfly@npm:6.5.0-prerelease.72" - checksum: 10c0/b8d92a11b287d06efad3f410eb356ad60cbbdac3fbcb1e58a99c792253f81e7fcf154f57b0e5a40fbb9d7361364dab02925ecf21db2877e643a23101b8b0745a +"@patternfly/patternfly@npm:6.5.0-prerelease.77": + version: 6.5.0-prerelease.77 + resolution: "@patternfly/patternfly@npm:6.5.0-prerelease.77" + checksum: 10c0/0311dff0addde8da28ba9245cbffb230bfa7571d38b4a5e3c6a366aed8009a6774f5097c33b453312722afbdddb31ddb0d11659d4a6f56c294912e81487d8fe2 languageName: node linkType: hard @@ -5178,7 +5171,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-core@workspace:packages/react-core" dependencies: - "@patternfly/patternfly": "npm:6.5.0-prerelease.72" + "@patternfly/patternfly": "npm:6.5.0-prerelease.77" "@patternfly/react-icons": "workspace:^" "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" @@ -5199,7 +5192,7 @@ __metadata: resolution: "@patternfly/react-docs@workspace:packages/react-docs" dependencies: "@patternfly/documentation-framework": "npm:^6.36.8" - "@patternfly/patternfly": "npm:6.5.0-prerelease.72" + "@patternfly/patternfly": "npm:6.5.0-prerelease.77" "@patternfly/patternfly-a11y": "npm:5.1.0" "@patternfly/react-charts": "workspace:^" "@patternfly/react-code-editor": "workspace:^" @@ -5239,7 +5232,7 @@ __metadata: "@fortawesome/free-brands-svg-icons": "npm:^5.15.4" "@fortawesome/free-regular-svg-icons": "npm:^5.15.4" "@fortawesome/free-solid-svg-icons": "npm:^5.15.4" - "@patternfly/patternfly": "npm:6.5.0-prerelease.71" + "@patternfly/patternfly": "npm:6.5.0-prerelease.77" "@rhds/icons": "npm:^2.1.0" fs-extra: "npm:^11.3.3" tslib: "npm:^2.8.1" @@ -5326,7 +5319,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-styles@workspace:packages/react-styles" dependencies: - "@patternfly/patternfly": "npm:6.5.0-prerelease.72" + "@patternfly/patternfly": "npm:6.5.0-prerelease.77" change-case: "npm:^5.4.4" fs-extra: "npm:^11.3.3" languageName: unknown @@ -5368,7 +5361,7 @@ __metadata: resolution: "@patternfly/react-tokens@workspace:packages/react-tokens" dependencies: "@adobe/css-tools": "npm:^4.4.4" - "@patternfly/patternfly": "npm:6.5.0-prerelease.72" + "@patternfly/patternfly": "npm:6.5.0-prerelease.77" fs-extra: "npm:^11.3.3" languageName: unknown linkType: soft