Skip to content

feat(Toolbar): dynamic sticky#12375

Open
kmcfaul wants to merge 4 commits intopatternfly:mainfrom
kmcfaul:12332-toolbar-dynamic-sticky
Open

feat(Toolbar): dynamic sticky#12375
kmcfaul wants to merge 4 commits intopatternfly:mainfrom
kmcfaul:12332-toolbar-dynamic-sticky

Conversation

@kmcfaul
Copy link
Copy Markdown
Contributor

@kmcfaul kmcfaul commented Apr 23, 2026

What: Closes #12332

  • Adds isStickyBase and isStickyStuck
  • Adds example using scroll event to dynamically apply isStickyStuck
  • Adds unit tests for new props
  • Bumps to core v.77 for styles

Waiting on patternfly/patternfly#8321

Summary by CodeRabbit

  • New Features

    • Toolbar gains two beta sticky controls for finer sticky behavior and styling.
  • Tests

    • Added tests ensuring sticky styling modifiers apply correctly.
  • Documentation

    • New example showing a dynamic sticky toolbar driven by scrolling with interactive controls; docs updated to clarify sticky behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Walkthrough

Toolbar adds two new beta props, isStickyBase and isStickyStuck, to apply distinct sticky CSS modifiers. A dynamic example and a scroll-parent hook demonstrate toggling isStickyStuck; tests and docs were added and several devDependency versions were bumped.

Changes

Cohort / File(s) Summary
Core Component
packages/react-core/src/components/Toolbar/Toolbar.tsx
Added isStickyBase?: boolean and isStickyStuck?: boolean to ToolbarProps; component conditionally applies stickyBase/stickyStuck modifier classes; updated isSticky JSDoc.
Tests
packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx
Added tests asserting absence/presence of stickyBase and stickyStuck modifier classes for default and prop-enabled cases.
Examples & Docs
packages/react-core/src/components/Toolbar/examples/Toolbar.md, packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx
Added "Dynamic sticky toolbar" docs, new ToolbarDynamicSticky example, and useIsStuckFromScrollParent hook demonstrating scroll-parent-driven sticky state.
DevDependency bumps
packages/react-core/package.json, packages/react-docs/package.json, packages/react-icons/package.json, packages/react-styles/package.json, packages/react-tokens/package.json
Updated @patternfly/patternfly devDependency versions to 6.5.0-prerelease.77 across multiple packages.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant ScrollContainer as ScrollContainer
    participant Hook as useIsStuckFromScrollParent
    participant Toolbar as Toolbar

    User->>ScrollContainer: scroll
    ScrollContainer->>Hook: passive scroll event
    Hook->>Hook: compute isStuck (scrollTop > 0)
    Hook->>Toolbar: update prop isStickyStuck
    Toolbar->>Toolbar: apply/remove stickyStuck modifier
    Toolbar->>User: render updated toolbar
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

  • #12332: Toolbar - add updated sticky props & example — same objectives: add sticky-unstuck/stuck props, example, and docs (PF-3933).
  • patternfly/patternfly-react#12331: Adds sticky-stuck/unstuck-style props and a generic hook/example; appears aligned with these changes.

Possibly related PRs

  • patternfly/patternfly-react#12175 — modifies Toolbar props and root element class modifiers; related at the prop/class level.

Suggested labels

Needs design review

Suggested reviewers

  • mcoker
  • thatblindgeye
  • wise-king-sullyman
  • nicolethoen
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(Toolbar): dynamic sticky' clearly and concisely describes the main change—adding dynamic sticky functionality to the Toolbar component.
Linked Issues check ✅ Passed All three requirements from #12332 are met: new sticky props (isStickyBase, isStickyStuck) are added, an example with scroll-based dynamic application is implemented, and documentation is included.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing dynamic sticky functionality. Dependency version updates align with the PatternFly CSS requirement noted in PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/react-core/src/components/Toolbar/examples/Toolbar.md`:
- Around line 47-53: Update the Dynamic sticky toolbar docs to note
scroll-parent guidance: explain that isStickyBase controls the CSS sticky
positioning but isStickyStuck must be driven by scroll state from the same
scrollable ancestor that creates the sticky context, and add a short sentence in
the Toolbar.md example (and mention ToolbarDynamicSticky.tsx) instructing
consumers to attach their scroll listener or intersection observer to the
scrollable container (not window) so isStickyStuck reflects that container's
scroll position.

In
`@packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx`:
- Around line 43-44: The demo array currently produces 0-29 so the UI shows
"item 0"; change the array generation used by the constants array and numbers so
it yields 1-30 instead of 0-29 (adjust the mapping in the Array.from call used
to create array), keeping the existing showEvenOnly filter logic (numbers =
showEvenOnly ? array.filter(number => number % 2 === 0) : array) unchanged so
even-only behavior still works with the new 1-30 range.
- Line 47: The scroll container div using scrollParentRef is not
keyboard-focusable or labeled; update the element referenced by scrollParentRef
to be reachable and announced by assistive tech by adding a focusable attribute
(e.g., tabIndex={0}) and an accessible name (e.g., role="region" with aria-label
or aria-labelledby) so keyboard users can focus and scroll the example content;
ensure these attributes are applied to the same div that has style={{ overflowY:
'scroll', height: '200px' }} and preserve existing refs and styles.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b742df8a-4376-410d-9592-1f4f41533973

📥 Commits

Reviewing files that changed from the base of the PR and between a426464 and 08eb1ce.

📒 Files selected for processing (4)
  • packages/react-core/src/components/Toolbar/Toolbar.tsx
  • packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx
  • packages/react-core/src/components/Toolbar/examples/Toolbar.md
  • packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx

Comment thread packages/react-core/src/components/Toolbar/examples/Toolbar.md
const numbers = showEvenOnly ? array.filter((number) => number % 2 === 0) : array;

return (
<div ref={scrollParentRef} style={{ overflowY: 'scroll', height: '200px' }}>
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 | 🟠 Major

Make the scroll container keyboard accessible.

This fixed-height overflow container is the primary interaction target for demonstrating sticky behavior, but it is not focusable or named. Keyboard users may not be able to scroll the example content.

♿ Proposed fix
-    <div ref={scrollParentRef} style={{ overflowY: 'scroll', height: '200px' }}>
+    <div
+      ref={scrollParentRef}
+      role="region"
+      aria-label="Scrollable dynamic sticky toolbar example"
+      tabIndex={0}
+      style={{ overflowY: 'scroll', height: '200px' }}
+    >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div ref={scrollParentRef} style={{ overflowY: 'scroll', height: '200px' }}>
<div
ref={scrollParentRef}
role="region"
aria-label="Scrollable dynamic sticky toolbar example"
tabIndex={0}
style={{ overflowY: 'scroll', height: '200px' }}
>
🤖 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`
at line 47, The scroll container div using scrollParentRef is not
keyboard-focusable or labeled; update the element referenced by scrollParentRef
to be reachable and announced by assistive tech by adding a focusable attribute
(e.g., tabIndex={0}) and an accessible name (e.g., role="region" with aria-label
or aria-labelledby) so keyboard users can focus and scroll the example content;
ensure these attributes are applied to the same div that has style={{ overflowY:
'scroll', height: '200px' }} and preserve existing refs and styles.

@patternfly-build
Copy link
Copy Markdown
Collaborator

patternfly-build commented Apr 23, 2026

@kmcfaul kmcfaul force-pushed the 12332-toolbar-dynamic-sticky branch from 08eb1ce to b38a419 Compare April 24, 2026 16:51
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx (1)

11-11: Tighten the scrollParentRef type.

React.RefObject<any> forgoes type safety. Since the consumer passes an HTMLDivElement ref and the hook only reads scrollTop/addEventListener, typing it as HTMLElement (or making the hook generic) would better document intent and catch misuse.

♻️ Proposed refactor
-  scrollParentRef: React.RefObject<any>;
+  scrollParentRef: React.RefObject<HTMLElement | null>;
🤖 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`
at line 11, The scrollParentRef is currently typed as React.RefObject<any>;
tighten it to a more specific DOM type to improve safety — change
scrollParentRef: React.RefObject<any> to React.RefObject<HTMLElement> (or
React.RefObject<HTMLDivElement> if the consumer always passes a div), since the
code only reads scrollTop and uses addEventListener; alternatively make the hook
generic (e.g., <T extends HTMLElement>) and use React.RefObject<T> so callers
can supply the precise element type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx`:
- Around line 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.

---

Nitpick comments:
In
`@packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx`:
- Line 11: The scrollParentRef is currently typed as React.RefObject<any>;
tighten it to a more specific DOM type to improve safety — change
scrollParentRef: React.RefObject<any> to React.RefObject<HTMLElement> (or
React.RefObject<HTMLDivElement> if the consumer always passes a div), since the
code only reads scrollTop and uses addEventListener; alternatively make the hook
generic (e.g., <T extends HTMLElement>) and use React.RefObject<T> so callers
can supply the precise element type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7887a63e-110c-415d-88c5-d2c3da0c31d8

📥 Commits

Reviewing files that changed from the base of the PR and between b38a419 and 57df9be.

📒 Files selected for processing (2)
  • packages/react-core/src/components/Toolbar/examples/Toolbar.md
  • packages/react-core/src/components/Toolbar/examples/ToolbarDynamicSticky.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/react-core/src/components/Toolbar/examples/Toolbar.md

Comment on lines +42 to +44
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;
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Toolbar - add updated sticky props & example

2 participants