Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 9 additions & 1 deletion packages/react-core/src/components/Toolbar/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ export interface ToolbarProps extends React.HTMLProps<HTMLDivElement>, 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. */
Expand Down Expand Up @@ -144,6 +148,8 @@ class Toolbar extends Component<ToolbarProps, ToolbarState> {
children,
isFullHeight,
isStatic,
isStickyBase,
isStickyStuck,
inset,
isSticky,
isVertical,
Expand Down Expand Up @@ -171,6 +177,8 @@ class Toolbar extends Component<ToolbarProps, ToolbarState> {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
<Fragment>
<ToolbarItem>Test</ToolbarItem>
Expand All @@ -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(
<Toolbar data-testid="toolbar-sticky-default">
<ToolbarContent>
<ToolbarItem>Test</ToolbarItem>
</ToolbarContent>
</Toolbar>
);
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(
<Toolbar data-testid="toolbar-sticky-base" isStickyBase>
<ToolbarContent>
<ToolbarItem>Test</ToolbarItem>
</ToolbarContent>
</Toolbar>
);
expect(screen.getByTestId('toolbar-sticky-base')).toHaveClass(styles.modifiers.stickyBase);
});

it(`Adds ${styles.modifiers.stickyStuck} when isStickyStuck is true`, () => {
render(
<Toolbar data-testid="toolbar-sticky-stuck" isStickyStuck>
<ToolbarContent>
<ToolbarItem>Test</ToolbarItem>
</ToolbarContent>
</Toolbar>
);
expect(screen.getByTestId('toolbar-sticky-stuck')).toHaveClass(styles.modifiers.stickyStuck);
});
});
12 changes: 11 additions & 1 deletion packages/react-core/src/components/Toolbar/examples/Toolbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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"

```
Comment thread
kmcfaul marked this conversation as resolved.

### 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.
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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<any>;
}): 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<HTMLDivElement>(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;
Comment thread
kmcfaul marked this conversation as resolved.
Comment on lines +42 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

searchValue has no effect on the rendered list.

The SearchInput is wired to searchValue/setSearchValue, but numbers is filtered only by showEvenOnly — typing in the search input does nothing visible. For a docs example this is likely to confuse readers about how the toolbar controls relate to the content. Either drop the SearchInput state plumbing or apply it to the rendered list.

🐛 Proposed fix
-  const numbers = showEvenOnly ? array.filter((number) => number % 2 === 0) : array;
+  const filteredByEven = showEvenOnly ? array.filter((number) => number % 2 === 0) : array;
+  const numbers = searchValue
+    ? filteredByEven.filter((number) => `item ${number}`.includes(searchValue))
+    : filteredByEven;

Also applies to: 51-56, 69-71

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx`
around lines 42 - 44, The SearchInput state (searchValue / setSearchValue) is
not used to filter the rendered list: numbers is computed only from array and
showEvenOnly so typing does nothing; fix by applying searchValue when computing
numbers (e.g., update the numbers definition in this file to include a filter
like .filter(n => n.toString().includes(searchValue)) after the showEvenOnly
filter) or remove the SearchInput state and control if you prefer; ensure you
update the symbols searchValue, setSearchValue and numbers (and the initial
array) so the rendered list reflects the search input.


return (
<div id="dynamic-sticky-scroll-parent" ref={scrollParentRef} style={{ overflowY: 'scroll', height: '200px' }}>
<Toolbar id="toolbar-sticky" inset={{ default: 'insetNone' }} isStickyBase isStickyStuck={isStickyStuck}>
<ToolbarContent>
<ToolbarItem>
<SearchInput
aria-label="Sticky example search input"
value={searchValue}
onChange={(_event, value) => setSearchValue(value)}
onClear={() => setSearchValue('')}
/>
</ToolbarItem>
<ToolbarItem alignSelf="center">
<Checkbox
label="Show only even number items"
isChecked={showEvenOnly}
onChange={(_event, checked) => setShowEvenOnly(checked)}
id="showOnlyEvenCheckbox"
/>
</ToolbarItem>
</ToolbarContent>
</Toolbar>
<ul>
{numbers.map((number) => (
<li key={number}>{`item ${number}`}</li>
))}
</ul>
</div>
);
};
2 changes: 1 addition & 1 deletion packages/react-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:^",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-icons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion packages/react-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react-tokens/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
25 changes: 9 additions & 16 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:^"
Expand All @@ -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:^"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading