From de34997e7ac406d483ac35c1153403dd2a8b891f Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Mon, 28 Sep 2026 14:02:43 -0400 Subject: [PATCH 01/12] feat(toolbar): container query support --- .../src/components/Toolbar/Toolbar.tsx | 4 + .../src/components/Toolbar/ToolbarContent.tsx | 6 +- .../src/components/Toolbar/ToolbarGroup.tsx | 16 +- .../src/components/Toolbar/ToolbarItem.tsx | 16 +- .../components/Toolbar/ToolbarToggleGroup.tsx | 173 +++++++++--------- .../Toolbar/__tests__/Toolbar.test.tsx | 38 ++++ .../components/Toolbar/examples/Toolbar.md | 16 ++ .../examples/ToolbarContainerQuery.css | 15 ++ .../examples/ToolbarContainerQuery.tsx | 35 ++++ 9 files changed, 211 insertions(+), 108 deletions(-) create mode 100644 packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css create mode 100644 packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.tsx diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index 5a0cf48de75..d9c5aa8619e 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -34,6 +34,8 @@ export interface ToolbarProps extends React.HTMLProps, OUIAProps children?: React.ReactNode; /** Id of the data toolbar */ id?: string; + /** Flag indicating if the toolbar is a container */ + isContainer?: boolean; /** Flag indicating the toolbar height should expand to the full height of the container */ isFullHeight?: boolean; /** Flag indicating the toolbar is static */ @@ -146,6 +148,7 @@ class Toolbar extends Component { toggleIsExpanded, className, children, + isContainer, isFullHeight, isStatic, isStickyBase, @@ -174,6 +177,7 @@ class Toolbar extends Component { className={css( styles.toolbar, hasNoPadding && styles.modifiers.noPadding, + isContainer && styles.modifiers.container, isFullHeight && styles.modifiers.fullHeight, isStatic && styles.modifiers.static, isSticky && styles.modifiers.sticky, diff --git a/packages/react-core/src/components/Toolbar/ToolbarContent.tsx b/packages/react-core/src/components/Toolbar/ToolbarContent.tsx index ee547c58acf..9e723934f1d 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarContent.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarContent.tsx @@ -78,11 +78,11 @@ class ToolbarContent extends Component { return ( - {({ width, getBreakpoint, height, getVerticalBreakpoint }) => ( + {({ height, getVerticalBreakpoint }) => (
{
{ return ( - {({ width, getBreakpoint, height, getVerticalBreakpoint }) => ( + {({ height, getVerticalBreakpoint }) => (
{ | 'actionGroupPlain' | 'labelGroup' ], - formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), + formatBreakpointMods(visibility, styles), formatBreakpointMods(visibilityAtHeight, styles, '', getVerticalBreakpoint(height), true), - formatBreakpointMods(align, styles, '', getBreakpoint(width)), - formatBreakpointMods(gap, styles, '', getBreakpoint(width)), - formatBreakpointMods(columnGap, styles, '', getBreakpoint(width)), - formatBreakpointMods(rowGap, styles, '', getBreakpoint(width)), - formatBreakpointMods(rowWrap, styles, '', getBreakpoint(width)), - formatBreakpointMods(flexGrow, styles, '', getBreakpoint(width)), + formatBreakpointMods(align, styles), + formatBreakpointMods(gap, styles), + formatBreakpointMods(columnGap, styles), + formatBreakpointMods(rowGap, styles), + formatBreakpointMods(rowWrap, styles), + formatBreakpointMods(flexGrow, styles), alignItems === 'start' && styles.modifiers.alignItemsStart, alignItems === 'center' && styles.modifiers.alignItemsCenter, alignItems === 'baseline' && styles.modifiers.alignItemsBaseline, diff --git a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx index 66778c13451..69d3519997c 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -236,7 +236,7 @@ export const ToolbarItem: React.FunctionComponent = ({ return ( - {({ width, getBreakpoint, height, getVerticalBreakpoint }) => ( + {({ height, getVerticalBreakpoint }) => (
= ({ variant === ToolbarItemVariant['label-group'] && styles.modifiers.labelGroup, isAllExpanded && styles.modifiers.expanded, isOverflowContainer && styles.modifiers.overflowContainer, - formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), + formatBreakpointMods(visibility, styles), formatBreakpointMods(visibilityAtHeight, styles, '', getVerticalBreakpoint(height), true), - formatBreakpointMods(align, styles, '', getBreakpoint(width)), - formatBreakpointMods(gap, styles, '', getBreakpoint(width)), - formatBreakpointMods(columnGap, styles, '', getBreakpoint(width)), - formatBreakpointMods(rowGap, styles, '', getBreakpoint(width)), - formatBreakpointMods(rowWrap, styles, '', getBreakpoint(width)), - formatBreakpointMods(flexGrow, styles, '', getBreakpoint(width)), + formatBreakpointMods(align, styles), + formatBreakpointMods(gap, styles), + formatBreakpointMods(columnGap, styles), + formatBreakpointMods(rowGap, styles), + formatBreakpointMods(rowWrap, styles), + formatBreakpointMods(flexGrow, styles), alignItems === 'start' && styles.modifiers.alignItemsStart, alignItems === 'center' && styles.modifiers.alignItemsCenter, alignItems === 'baseline' && styles.modifiers.alignItemsBaseline, diff --git a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx index d5caed8a187..5bfc1b6decf 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx @@ -7,7 +7,6 @@ import { ToolbarContext, ToolbarContentContext } from './ToolbarUtils'; import { Button } from '../Button'; import globalBreakpointLg from '@patternfly/react-tokens/dist/esm/t_global_breakpoint_lg'; import { formatBreakpointMods, toCamel } from '../../helpers/util'; -import { PageContext } from '../Page/PageContext'; import { ToolbarExpandableContent } from './ToolbarExpandableContent'; export interface ToolbarToggleGroupProps extends ToolbarGroupProps { @@ -188,99 +187,95 @@ class ToolbarToggleGroup extends Component { } return ( - - {({ width, getBreakpoint }) => ( - - {({ toggleIsExpanded: managedOnToggle }) => { - const _onToggle = onToggle !== undefined ? onToggle : managedOnToggle; + + {({ toggleIsExpanded: managedOnToggle }) => { + const _onToggle = onToggle !== undefined ? onToggle : managedOnToggle; - return ( - - {({ - expandableContentRef, - expandableContentId, - labelContainerRef: managedLabelContainerRef, - isExpanded: managedIsExpanded, - clearAllFilters: clearAllFiltersContext, - clearFiltersButtonText: clearFiltersButtonContext, - showClearFiltersButton: showClearFiltersButtonContext - }) => { - const _isExpanded = isExpanded !== undefined ? isExpanded : managedIsExpanded; - const _labelContainerRef = - labelContainerRef !== undefined ? labelContainerRef : managedLabelContainerRef; + return ( + + {({ + expandableContentRef, + expandableContentId, + labelContainerRef: managedLabelContainerRef, + isExpanded: managedIsExpanded, + clearAllFilters: clearAllFiltersContext, + clearFiltersButtonText: clearFiltersButtonContext, + showClearFiltersButton: showClearFiltersButtonContext + }) => { + const _isExpanded = isExpanded !== undefined ? isExpanded : managedIsExpanded; + const _labelContainerRef = + labelContainerRef !== undefined ? labelContainerRef : managedLabelContainerRef; - const breakpointMod: { - md?: 'show'; - lg?: 'show'; - xl?: 'show'; - '2xl'?: 'show'; - } = {}; - breakpointMod[breakpoint] = 'show'; + const breakpointMod: { + md?: 'show'; + lg?: 'show'; + xl?: 'show'; + '2xl'?: 'show'; + } = {}; + breakpointMod[breakpoint] = 'show'; - const expandableContent = ( - - {children} - - ); + const expandableContent = ( + + {children} + + ); - const toggleButton = ( -
-
- ); + const toggleButton = ( +
+
+ ); - return ( -
- {toggleButton} - {_isExpanded && ReactDOM.createPortal(expandableContent, expandableContentRef.current)} - {!_isExpanded && children} -
- ); - }} -
- ); - }} -
- )} -
+ return ( +
+ {toggleButton} + {_isExpanded && ReactDOM.createPortal(expandableContent, expandableContentRef.current)} + {!_isExpanded && children} +
+ ); + }} + + ); + }} + ); } } 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 7e7494e525b..1339d63385f 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx @@ -299,4 +299,42 @@ describe('Toolbar', () => { ); expect(screen.getByTestId('toolbar-sticky-stuck')).toHaveClass(styles.modifiers.stickyStuck); }); + + it('renders the container modifier when isContainer is true', () => { + render(); + + expect(screen.getByTestId('toolbar-container')).toHaveClass('pf-m-container'); + }); + + it('renders all responsive visibility modifiers', () => { + render(); + + const item = screen.getByTestId('toolbar-item'); + + expect(item).toHaveClass('pf-m-hidden-on-md'); + expect(item).toHaveClass('pf-m-visible-on-xl'); + }); + + it('renders all responsive row wrap modifiers', () => { + render(); + + const contentSection = screen.getByTestId('toolbar-content').querySelector('div'); + + expect(contentSection).toHaveClass('pf-m-nowrap-on-md'); + expect(contentSection).toHaveClass('pf-m-wrap-on-xl'); + }); + + it('renders the toggle group breakpoint modifier', () => { + render( + + + }> + Content + + + + ); + + expect(screen.getByTestId('toolbar-toggle-group')).toHaveClass('pf-m-show-on-xl'); + }); }); diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index 9b00cd5132f..f9e46b684fe 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -6,6 +6,7 @@ section: components --- import { Fragment, useState, useLayoutEffect, useRef } from 'react'; +import './ToolbarContainerQuery.css'; import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; import RhUiCopyFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-copy-fill-icon'; @@ -78,6 +79,21 @@ To change the background color of a toolbar, use the `colorVariant` property on ``` +## Responsive toolbar behavior + +Toolbar width-based responsive modifiers use `@container` queries with the global PatternFly breakpoints. Responsive inset modifiers (`inset`) are an exception and use viewport media queries. + +By default, the container `pf-v6-contain-toolbar` is established on `:root`, so the breakpoints behave like viewport breakpoints. To make a toolbar respond to its own available width, set the `isContainer` property on ``. Note: Alternatively, if a wrapping element creates an `inline-size` or `size` container with the container name `pf-v6-contain-toolbar`, the breakpoints will apply to that container's width instead. + +### Toolbar as container +Add `isContainer` to the toolbar so width-based modifiers respond to the toolbar's width. Resize the dashed box to see items and groups show and hide at the global breakpoints. + +_Debugging note: `xl` and `2xl` may not trigger when the content area is narrower than those breakpoints (view in full screen)._ + +```ts file="./ToolbarContainerQuery.tsx" + +``` + ## Examples with toggle groups and filters The following examples use toggle groups to allow for more responsive and complex toolbars with multiple items and groups of items. To visualize responsive toolbar behavior in the following examples, resize the browser to a smaller screen width. diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css new file mode 100644 index 00000000000..dc089a5f5c7 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css @@ -0,0 +1,15 @@ +.toolbar-container-query-resize { + width: 800px; + min-width: 300px; + max-width: 100%; + padding: var(--pf-t--global--spacer--sm); + overflow: auto; + resize: horizontal; + border: var(--pf-t--global--border--width--extra-strong) dashed var(--pf-t--global--border--color--default); +} + +.toolbar-container-query-resize.pf-v6-c-toolbar, +.toolbar-container-query-resize .pf-v6-c-toolbar__group { + border: var(--pf-t--global--border--width--control--default) dashed var(--pf-t--global--border--color--default); + padding: var(--pf-t--global--spacer--sm); +} diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.tsx new file mode 100644 index 00000000000..728edd14492 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.tsx @@ -0,0 +1,35 @@ +import { Button, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/react-core'; + +export const ToolbarContainerQuery: React.FunctionComponent = () => ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+); From c09dccf87ca53a9a3241d757938104527ed018af Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Mon, 28 Sep 2026 14:24:01 -0400 Subject: [PATCH 02/12] feat(toolbar): cleanup a little css --- .../src/components/Toolbar/examples/ToolbarContainerQuery.css | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css index dc089a5f5c7..d9314b0c2ff 100644 --- a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css @@ -8,7 +8,6 @@ border: var(--pf-t--global--border--width--extra-strong) dashed var(--pf-t--global--border--color--default); } -.toolbar-container-query-resize.pf-v6-c-toolbar, .toolbar-container-query-resize .pf-v6-c-toolbar__group { border: var(--pf-t--global--border--width--control--default) dashed var(--pf-t--global--border--color--default); padding: var(--pf-t--global--spacer--sm); From 64de96e8690b61d34122f4e2d39405d767091dc3 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Mon, 28 Sep 2026 16:19:52 -0400 Subject: [PATCH 03/12] fix(toolbar): mismatched visibility breakpoints --- packages/react-core/src/components/Toolbar/ToolbarItem.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx index 69d3519997c..4328f9888f4 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -21,6 +21,7 @@ export interface ToolbarItemProps extends React.HTMLProps { /** Visibility at various width breakpoints. */ visibility?: { default?: 'hidden' | 'visible'; + sm?: 'hidden' | 'visible'; md?: 'hidden' | 'visible'; lg?: 'hidden' | 'visible'; xl?: 'hidden' | 'visible'; From ae3aefc85cbc13d2390fb39c92971a5b6bc57251 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Mon, 28 Sep 2026 16:29:00 -0400 Subject: [PATCH 04/12] fix(toolbar): close managed expandable content --- .../src/components/Toolbar/Toolbar.tsx | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index d9c5aa8619e..5ddb7e71a99 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -3,7 +3,8 @@ import styles from '@patternfly/react-styles/css/components/Toolbar/toolbar'; import { css } from '@patternfly/react-styles'; import { ToolbarContext } from './ToolbarUtils'; import { ToolbarLabelGroupContent } from './ToolbarLabelGroupContent'; -import { formatBreakpointMods, canUseDOM } from '../../helpers/util'; +import { formatBreakpointMods, canUseDOM, getBreakpoint } from '../../helpers/util'; +import { getResizeObserver } from '../../helpers/resizeObserver'; import { getOUIAProps, OUIAProps } from '../../helpers'; import { SSRSafeIds } from '../../helpers/SSRSafeIds/SSRSafeIds'; import { PageContext } from '../Page/PageContext'; @@ -86,6 +87,9 @@ interface FilterInfo { class Toolbar extends Component { static displayName = 'Toolbar'; labelGroupContentRef = createRef(); + toolbarRef = createRef(); + resizeObserver: () => void = () => {}; + containerBreakpoint: ReturnType; staticFilterInfo = {}; hasNoPadding = false; state = { @@ -111,18 +115,45 @@ class Toolbar extends Component { } }; + closeExpandableContentOnContainerResize = () => { + const containerWidth = this.toolbarRef.current?.clientWidth; + if (!containerWidth) { + return; + } + + const breakpoint = getBreakpoint(containerWidth); + if (breakpoint !== this.containerBreakpoint) { + this.containerBreakpoint = breakpoint; + if (this.state.isManagedToggleExpanded) { + this.setState({ isManagedToggleExpanded: false }); + } + } + }; + componentDidMount() { if (canUseDOM) { this.setState({ windowWidth: window.innerWidth }); } if (this.isToggleManaged() && canUseDOM) { - window.addEventListener('resize', this.closeExpandableContent); + if (this.props.isContainer && this.toolbarRef.current) { + this.resizeObserver = getResizeObserver( + this.toolbarRef.current, + this.closeExpandableContentOnContainerResize, + true + ); + } else { + window.addEventListener('resize', this.closeExpandableContent); + } } } componentWillUnmount() { if (this.isToggleManaged() && canUseDOM) { - window.removeEventListener('resize', this.closeExpandableContent); + if (this.props.isContainer) { + this.resizeObserver(); + } else { + window.removeEventListener('resize', this.closeExpandableContent); + } } } @@ -191,6 +222,7 @@ class Toolbar extends Component { className )} id={randomId} + ref={this.toolbarRef} {...getOUIAProps(Toolbar.displayName, ouiaId !== undefined ? ouiaId : generatedOuiaId)} {...props} > From b1517255296bc68f60b184a33c2d0070d4e3df85 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Wed, 30 Sep 2026 08:51:42 -0400 Subject: [PATCH 05/12] chore(deps): bump --- packages/react-core/package.json | 2 +- packages/react-docs/package.json | 2 +- packages/react-icons/package.json | 2 +- packages/react-styles/package.json | 2 +- packages/react-tokens/package.json | 2 +- yarn.lock | 18 +++++++++--------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/react-core/package.json b/packages/react-core/package.json index c62b4eee4de..0a668e3db3d 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "case-anything": "^3.1.2", "css": "^3.0.0", "fs-extra": "^11.3.3" diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index bc009e76fe3..2c07d22e5c6 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "@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 7ae49e4c7d4..50d99deef06 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -38,7 +38,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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "@rhds/icons": "^2.3.1", "fs-extra": "^11.3.3" }, diff --git a/packages/react-styles/package.json b/packages/react-styles/package.json index d786889fb7e..20c63052060 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "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 47cb52aa4e0..e4a4aa8d954 100644 --- a/packages/react-tokens/package.json +++ b/packages/react-tokens/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@adobe/css-tools": "^4.5.0", - "@patternfly/patternfly": "6.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "fs-extra": "^11.3.3" } } diff --git a/yarn.lock b/yarn.lock index 189bdeb6339..56cb21c653a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,10 +5070,10 @@ __metadata: languageName: node linkType: hard -"@patternfly/patternfly@npm:6.6.0-prerelease.50": - version: 6.6.0-prerelease.50 - resolution: "@patternfly/patternfly@npm:6.6.0-prerelease.50" - checksum: 10c0/f4d711a87c0094dd9d0115f21f7c2e4ff027ea68b439a342e0331b6b90a38cd0cb24d1b9f002409fe30e271be79588fdb760927c5e008e299603eeb56f299a16 +"@patternfly/patternfly@npm:6.7.0-prerelease.1": + version: 6.7.0-prerelease.1 + resolution: "@patternfly/patternfly@npm:6.7.0-prerelease.1" + checksum: 10c0/d96430d6e95e500029a9b1eeb12fca980e9ccd65b4cae6f27efa3c01bdf33fb75355222c825ca1d7bac1477754703815e99931cb50b4931223f552cb86bdeb7c languageName: node linkType: hard @@ -5171,7 +5171,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-core@workspace:packages/react-core" dependencies: - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@patternfly/react-icons": "workspace:^" "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" @@ -5192,7 +5192,7 @@ __metadata: resolution: "@patternfly/react-docs@workspace:packages/react-docs" dependencies: "@patternfly/documentation-framework": "npm:^6.40.0" - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@patternfly/patternfly-a11y": "npm:5.2.1" "@patternfly/react-charts": "workspace:^" "@patternfly/react-code-editor": "workspace:^" @@ -5232,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.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@rhds/icons": "npm:^2.3.1" fs-extra: "npm:^11.3.3" tslib: "npm:^2.8.1" @@ -5319,7 +5319,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-styles@workspace:packages/react-styles" dependencies: - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" change-case: "npm:^5.4.4" fs-extra: "npm:^11.3.3" languageName: unknown @@ -5361,7 +5361,7 @@ __metadata: resolution: "@patternfly/react-tokens@workspace:packages/react-tokens" dependencies: "@adobe/css-tools": "npm:^4.5.0" - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" fs-extra: "npm:^11.3.3" languageName: unknown linkType: soft From c3ad5e776c202e068a7da0e620924ab29e0e3287 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Wed, 30 Sep 2026 13:08:36 -0400 Subject: [PATCH 06/12] fix(toolbar): rebase --- packages/react-core/package.json | 2 +- packages/react-docs/package.json | 2 +- packages/react-icons/package.json | 2 +- packages/react-styles/package.json | 2 +- packages/react-tokens/package.json | 2 +- yarn.lock | 18 +++++++++--------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 0a668e3db3d..c62b4eee4de 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.7.0-prerelease.1", + "@patternfly/patternfly": "6.6.0-prerelease.50", "case-anything": "^3.1.2", "css": "^3.0.0", "fs-extra": "^11.3.3" diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 2c07d22e5c6..bc009e76fe3 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.7.0-prerelease.1", + "@patternfly/patternfly": "6.6.0-prerelease.50", "@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 50d99deef06..7ae49e4c7d4 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -38,7 +38,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.7.0-prerelease.1", + "@patternfly/patternfly": "6.6.0-prerelease.50", "@rhds/icons": "^2.3.1", "fs-extra": "^11.3.3" }, diff --git a/packages/react-styles/package.json b/packages/react-styles/package.json index 20c63052060..d786889fb7e 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.7.0-prerelease.1", + "@patternfly/patternfly": "6.6.0-prerelease.50", "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 e4a4aa8d954..47cb52aa4e0 100644 --- a/packages/react-tokens/package.json +++ b/packages/react-tokens/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@adobe/css-tools": "^4.5.0", - "@patternfly/patternfly": "6.7.0-prerelease.1", + "@patternfly/patternfly": "6.6.0-prerelease.50", "fs-extra": "^11.3.3" } } diff --git a/yarn.lock b/yarn.lock index 56cb21c653a..189bdeb6339 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,10 +5070,10 @@ __metadata: languageName: node linkType: hard -"@patternfly/patternfly@npm:6.7.0-prerelease.1": - version: 6.7.0-prerelease.1 - resolution: "@patternfly/patternfly@npm:6.7.0-prerelease.1" - checksum: 10c0/d96430d6e95e500029a9b1eeb12fca980e9ccd65b4cae6f27efa3c01bdf33fb75355222c825ca1d7bac1477754703815e99931cb50b4931223f552cb86bdeb7c +"@patternfly/patternfly@npm:6.6.0-prerelease.50": + version: 6.6.0-prerelease.50 + resolution: "@patternfly/patternfly@npm:6.6.0-prerelease.50" + checksum: 10c0/f4d711a87c0094dd9d0115f21f7c2e4ff027ea68b439a342e0331b6b90a38cd0cb24d1b9f002409fe30e271be79588fdb760927c5e008e299603eeb56f299a16 languageName: node linkType: hard @@ -5171,7 +5171,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-core@workspace:packages/react-core" dependencies: - "@patternfly/patternfly": "npm:6.7.0-prerelease.1" + "@patternfly/patternfly": "npm:6.6.0-prerelease.50" "@patternfly/react-icons": "workspace:^" "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" @@ -5192,7 +5192,7 @@ __metadata: resolution: "@patternfly/react-docs@workspace:packages/react-docs" dependencies: "@patternfly/documentation-framework": "npm:^6.40.0" - "@patternfly/patternfly": "npm:6.7.0-prerelease.1" + "@patternfly/patternfly": "npm:6.6.0-prerelease.50" "@patternfly/patternfly-a11y": "npm:5.2.1" "@patternfly/react-charts": "workspace:^" "@patternfly/react-code-editor": "workspace:^" @@ -5232,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.7.0-prerelease.1" + "@patternfly/patternfly": "npm:6.6.0-prerelease.50" "@rhds/icons": "npm:^2.3.1" fs-extra: "npm:^11.3.3" tslib: "npm:^2.8.1" @@ -5319,7 +5319,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-styles@workspace:packages/react-styles" dependencies: - "@patternfly/patternfly": "npm:6.7.0-prerelease.1" + "@patternfly/patternfly": "npm:6.6.0-prerelease.50" change-case: "npm:^5.4.4" fs-extra: "npm:^11.3.3" languageName: unknown @@ -5361,7 +5361,7 @@ __metadata: resolution: "@patternfly/react-tokens@workspace:packages/react-tokens" dependencies: "@adobe/css-tools": "npm:^4.5.0" - "@patternfly/patternfly": "npm:6.7.0-prerelease.1" + "@patternfly/patternfly": "npm:6.6.0-prerelease.50" fs-extra: "npm:^11.3.3" languageName: unknown linkType: soft From 02c82c91d6469fcc9a693dd479cab4d25d7ef1c3 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Wed, 30 Sep 2026 13:15:23 -0400 Subject: [PATCH 07/12] fix(toolbar): bump again --- packages/react-core/package.json | 2 +- packages/react-docs/package.json | 2 +- packages/react-icons/package.json | 2 +- packages/react-styles/package.json | 2 +- packages/react-tokens/package.json | 2 +- yarn.lock | 18 +++++++++--------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/react-core/package.json b/packages/react-core/package.json index c62b4eee4de..0a668e3db3d 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "case-anything": "^3.1.2", "css": "^3.0.0", "fs-extra": "^11.3.3" diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index bc009e76fe3..2c07d22e5c6 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "@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 7ae49e4c7d4..50d99deef06 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -38,7 +38,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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "@rhds/icons": "^2.3.1", "fs-extra": "^11.3.3" }, diff --git a/packages/react-styles/package.json b/packages/react-styles/package.json index d786889fb7e..20c63052060 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.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "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 47cb52aa4e0..e4a4aa8d954 100644 --- a/packages/react-tokens/package.json +++ b/packages/react-tokens/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@adobe/css-tools": "^4.5.0", - "@patternfly/patternfly": "6.6.0-prerelease.50", + "@patternfly/patternfly": "6.7.0-prerelease.1", "fs-extra": "^11.3.3" } } diff --git a/yarn.lock b/yarn.lock index 189bdeb6339..56cb21c653a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5070,10 +5070,10 @@ __metadata: languageName: node linkType: hard -"@patternfly/patternfly@npm:6.6.0-prerelease.50": - version: 6.6.0-prerelease.50 - resolution: "@patternfly/patternfly@npm:6.6.0-prerelease.50" - checksum: 10c0/f4d711a87c0094dd9d0115f21f7c2e4ff027ea68b439a342e0331b6b90a38cd0cb24d1b9f002409fe30e271be79588fdb760927c5e008e299603eeb56f299a16 +"@patternfly/patternfly@npm:6.7.0-prerelease.1": + version: 6.7.0-prerelease.1 + resolution: "@patternfly/patternfly@npm:6.7.0-prerelease.1" + checksum: 10c0/d96430d6e95e500029a9b1eeb12fca980e9ccd65b4cae6f27efa3c01bdf33fb75355222c825ca1d7bac1477754703815e99931cb50b4931223f552cb86bdeb7c languageName: node linkType: hard @@ -5171,7 +5171,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-core@workspace:packages/react-core" dependencies: - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@patternfly/react-icons": "workspace:^" "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" @@ -5192,7 +5192,7 @@ __metadata: resolution: "@patternfly/react-docs@workspace:packages/react-docs" dependencies: "@patternfly/documentation-framework": "npm:^6.40.0" - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@patternfly/patternfly-a11y": "npm:5.2.1" "@patternfly/react-charts": "workspace:^" "@patternfly/react-code-editor": "workspace:^" @@ -5232,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.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" "@rhds/icons": "npm:^2.3.1" fs-extra: "npm:^11.3.3" tslib: "npm:^2.8.1" @@ -5319,7 +5319,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-styles@workspace:packages/react-styles" dependencies: - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" change-case: "npm:^5.4.4" fs-extra: "npm:^11.3.3" languageName: unknown @@ -5361,7 +5361,7 @@ __metadata: resolution: "@patternfly/react-tokens@workspace:packages/react-tokens" dependencies: "@adobe/css-tools": "npm:^4.5.0" - "@patternfly/patternfly": "npm:6.6.0-prerelease.50" + "@patternfly/patternfly": "npm:6.7.0-prerelease.1" fs-extra: "npm:^11.3.3" languageName: unknown linkType: soft From 89f088a6c890e67df6e5e8f34f81799c250dc03a Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Thu, 1 Oct 2026 10:37:52 -0400 Subject: [PATCH 08/12] fix(toolbar): resize handling fix, test update, css updates, doc updates --- .../src/components/Toolbar/Toolbar.tsx | 54 +++++++++++-------- .../Toolbar/__tests__/Toolbar.test.tsx | 2 +- .../components/Toolbar/examples/Toolbar.md | 10 ++-- .../examples/ToolbarContainerQuery.css | 10 +--- 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index 5ddb7e71a99..e6c86cb90b6 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -35,7 +35,7 @@ export interface ToolbarProps extends React.HTMLProps, OUIAProps children?: React.ReactNode; /** Id of the data toolbar */ id?: string; - /** Flag indicating if the toolbar is a container */ + /** Flag indicating if the toolbar is a container for CSS container queries */ isContainer?: boolean; /** Flag indicating the toolbar height should expand to the full height of the container */ isFullHeight?: boolean; @@ -106,8 +106,8 @@ class Toolbar extends Component { })); }; - closeExpandableContent = (e: any) => { - if (e.target.innerWidth !== this.state.windowWidth) { + closeExpandableContent = (e?: any) => { + if (e && e.target.innerWidth !== this.state.windowWidth) { this.setState(() => ({ isManagedToggleExpanded: false, windowWidth: e.target.innerWidth @@ -122,39 +122,51 @@ class Toolbar extends Component { } const breakpoint = getBreakpoint(containerWidth); + const isInitialMeasurement = this.containerBreakpoint === undefined; if (breakpoint !== this.containerBreakpoint) { this.containerBreakpoint = breakpoint; - if (this.state.isManagedToggleExpanded) { + if (!isInitialMeasurement && this.state.isManagedToggleExpanded) { this.setState({ isManagedToggleExpanded: false }); } } }; + setupResizeHandling = () => { + if (!this.isToggleManaged() || !canUseDOM) { + return; + } + + this.containerBreakpoint = undefined; + const reference = this.props.isContainer ? this.toolbarRef.current : undefined; + const handler = this.props.isContainer ? this.closeExpandableContentOnContainerResize : this.closeExpandableContent; + + this.resizeObserver = getResizeObserver(reference, handler, true); + }; + + cleanupResizeHandling = () => { + this.resizeObserver(); + this.resizeObserver = () => {}; + }; + componentDidMount() { if (canUseDOM) { this.setState({ windowWidth: window.innerWidth }); } - if (this.isToggleManaged() && canUseDOM) { - if (this.props.isContainer && this.toolbarRef.current) { - this.resizeObserver = getResizeObserver( - this.toolbarRef.current, - this.closeExpandableContentOnContainerResize, - true - ); - } else { - window.addEventListener('resize', this.closeExpandableContent); - } + this.setupResizeHandling(); + } + + componentDidUpdate(prevProps: ToolbarProps) { + const wasToggleManaged = !(prevProps.isExpanded || !!prevProps.toggleIsExpanded); + const isToggleManaged = this.isToggleManaged(); + + if (prevProps.isContainer !== this.props.isContainer || wasToggleManaged !== isToggleManaged) { + this.cleanupResizeHandling(); + this.setupResizeHandling(); } } componentWillUnmount() { - if (this.isToggleManaged() && canUseDOM) { - if (this.props.isContainer) { - this.resizeObserver(); - } else { - window.removeEventListener('resize', this.closeExpandableContent); - } - } + this.cleanupResizeHandling(); } updateNumberFilters = (categoryName: string, numberOfFilters: number) => { 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 1339d63385f..49d398ee745 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/Toolbar.test.tsx @@ -303,7 +303,7 @@ describe('Toolbar', () => { it('renders the container modifier when isContainer is true', () => { render(); - expect(screen.getByTestId('toolbar-container')).toHaveClass('pf-m-container'); + expect(screen.getByTestId('toolbar-container')).toHaveClass(styles.modifiers.container); }); it('renders all responsive visibility modifiers', () => { diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index f9e46b684fe..9ac4956341a 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -81,14 +81,14 @@ To change the background color of a toolbar, use the `colorVariant` property on ## Responsive toolbar behavior -Toolbar width-based responsive modifiers use `@container` queries with the global PatternFly breakpoints. Responsive inset modifiers (`inset`) are an exception and use viewport media queries. +Toolbar width-based responsive modifiers use `@container` CSS queries with the global PatternFly breakpoints. Responsive inset modifiers (via the `inset` property) are an exception and use viewport media queries. -By default, the container `pf-v6-contain-toolbar` is established on `:root`, so the breakpoints behave like viewport breakpoints. To make a toolbar respond to its own available width, set the `isContainer` property on ``. Note: Alternatively, if a wrapping element creates an `inline-size` or `size` container with the container name `pf-v6-contain-toolbar`, the breakpoints will apply to that container's width instead. +By default, the PatternFly CSS establishes the `container-name` property with a value of `pf-v6-contain-toolbar` on `:root`, so the breakpoints behave like viewport breakpoints. -### Toolbar as container -Add `isContainer` to the toolbar so width-based modifiers respond to the toolbar's width. Resize the dashed box to see items and groups show and hide at the global breakpoints. +Alternatively, if another wrapping element creates a container in the CSS with a `container-name` property value of `pf-v6-contain-toolbar` and `container-type` value of `inline-size` or `size` , the breakpoints will apply to that container's width instead. -_Debugging note: `xl` and `2xl` may not trigger when the content area is narrower than those breakpoints (view in full screen)._ +### Toolbar as container +Add `isContainer` to the toolbar so width-based modifiers respond to the toolbar's width. Resize the dashed box in this example to see items and groups show and hide at the global breakpoints. ```ts file="./ToolbarContainerQuery.tsx" diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css index d9314b0c2ff..f802399cc1a 100644 --- a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css @@ -1,14 +1,8 @@ .toolbar-container-query-resize { - width: 800px; + width: 100%; min-width: 300px; - max-width: 100%; padding: var(--pf-t--global--spacer--sm); overflow: auto; resize: horizontal; border: var(--pf-t--global--border--width--extra-strong) dashed var(--pf-t--global--border--color--default); -} - -.toolbar-container-query-resize .pf-v6-c-toolbar__group { - border: var(--pf-t--global--border--width--control--default) dashed var(--pf-t--global--border--color--default); - padding: var(--pf-t--global--spacer--sm); -} +} \ No newline at end of file From 12a712426ea3d8d3a92a278a2e9b859a19bf3f0c Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Thu, 1 Oct 2026 11:22:46 -0400 Subject: [PATCH 09/12] fix(toolbar): isContentPopup update --- .../react-core/src/components/Toolbar/Toolbar.tsx | 4 +++- .../src/components/Toolbar/ToolbarToggleGroup.tsx | 12 +++++++----- .../src/components/Toolbar/ToolbarUtils.tsx | 4 ++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index e6c86cb90b6..f6533abeb7a 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -249,7 +249,9 @@ class Toolbar extends Component { clearFiltersButtonText, showClearFiltersButton, toolbarId: randomId, - customLabelGroupContent + customLabelGroupContent, + isContainer, + toolbarRef: this.toolbarRef }} > {children} diff --git a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx index 5bfc1b6decf..e433bd02b04 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx @@ -155,10 +155,10 @@ class ToolbarToggleGroup extends Component { toggleRef = createRef(); expandableContentRef = createRef(); - isContentPopup = () => { - const viewportSize = typeof window !== 'undefined' ? window.innerWidth : 1200; + isContentPopup = (width?: number) => { + const size = width ?? (typeof window !== 'undefined' ? window.innerWidth : 1200); const lgBreakpointValue = parseInt(globalBreakpointLg.value); - return viewportSize < lgBreakpointValue; + return size < lgBreakpointValue; }; render() { @@ -188,7 +188,7 @@ class ToolbarToggleGroup extends Component { return ( - {({ toggleIsExpanded: managedOnToggle }) => { + {({ toggleIsExpanded: managedOnToggle, isContainer, toolbarRef }) => { const _onToggle = onToggle !== undefined ? onToggle : managedOnToggle; return ( @@ -235,7 +235,9 @@ class ToolbarToggleGroup extends Component { onClick={_onToggle} aria-label="Show Filters" {...(_isExpanded && { 'aria-expanded': true })} - aria-haspopup={_isExpanded && this.isContentPopup()} + aria-haspopup={ + _isExpanded && this.isContentPopup(isContainer ? toolbarRef?.current?.clientWidth : undefined) + } aria-controls={_isExpanded ? expandableContentId : undefined} ref={this.toggleRef} icon={toggleIcon} diff --git a/packages/react-core/src/components/Toolbar/ToolbarUtils.tsx b/packages/react-core/src/components/Toolbar/ToolbarUtils.tsx index fbddb1ee6cd..7445da7cf27 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarUtils.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarUtils.tsx @@ -15,6 +15,10 @@ export interface ToolbarContextProps { showClearFiltersButton?: boolean; toolbarId?: string; customLabelGroupContent?: React.ReactNode; + /** Flag indicating if the toolbar is a container */ + isContainer?: boolean; + /** Ref to the toolbar root element */ + toolbarRef?: RefObject; } export const ToolbarContext = createContext({ From 471c0ff8e93c7f45eb290d51c61492f1888ab715 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Thu, 1 Oct 2026 12:23:18 -0400 Subject: [PATCH 10/12] fix(toolbar): inset exception wording and trailing whitespace --- packages/react-core/src/components/Toolbar/examples/Toolbar.md | 2 +- .../src/components/Toolbar/examples/ToolbarContainerQuery.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index 9ac4956341a..8355501c234 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -81,7 +81,7 @@ To change the background color of a toolbar, use the `colorVariant` property on ## Responsive toolbar behavior -Toolbar width-based responsive modifiers use `@container` CSS queries with the global PatternFly breakpoints. Responsive inset modifiers (via the `inset` property) are an exception and use viewport media queries. +Toolbar width-based responsive modifiers use `@container` CSS queries with the global PatternFly breakpoints. Responsive inset modifiers (via the `inset` property) are an exception and use viewport media queries. Insets are an exception and rely on viewport media queries. By default, the PatternFly CSS establishes the `container-name` property with a value of `pf-v6-contain-toolbar` on `:root`, so the breakpoints behave like viewport breakpoints. diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css index f802399cc1a..8ee1c2e4c29 100644 --- a/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarContainerQuery.css @@ -5,4 +5,4 @@ overflow: auto; resize: horizontal; border: var(--pf-t--global--border--width--extra-strong) dashed var(--pf-t--global--border--color--default); -} \ No newline at end of file +} From 129da042e7a390bb95b0a886771055f5f18b2c21 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Thu, 1 Oct 2026 15:21:06 -0400 Subject: [PATCH 11/12] fix(toolbar): revert getBreakpoint changes, reprovide context and define null width conditionally --- .../src/components/Toolbar/Toolbar.tsx | 119 ++++++------ .../src/components/Toolbar/ToolbarContent.tsx | 6 +- .../src/components/Toolbar/ToolbarGroup.tsx | 16 +- .../src/components/Toolbar/ToolbarItem.tsx | 16 +- .../components/Toolbar/ToolbarToggleGroup.tsx | 178 +++++++++--------- 5 files changed, 177 insertions(+), 158 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/Toolbar.tsx b/packages/react-core/src/components/Toolbar/Toolbar.tsx index f6533abeb7a..5ee2246ee60 100644 --- a/packages/react-core/src/components/Toolbar/Toolbar.tsx +++ b/packages/react-core/src/components/Toolbar/Toolbar.tsx @@ -215,60 +215,73 @@ class Toolbar extends Component { return ( - {({ width, getBreakpoint }) => ( -
- { + const toolbarContent = ( +
- {children} - - -
- )} + + {children} + + +
+ ); + + return isContainer ? ( + {toolbarContent} + ) : ( + toolbarContent + ); + }}
); }; diff --git a/packages/react-core/src/components/Toolbar/ToolbarContent.tsx b/packages/react-core/src/components/Toolbar/ToolbarContent.tsx index 9e723934f1d..ee547c58acf 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarContent.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarContent.tsx @@ -78,11 +78,11 @@ class ToolbarContent extends Component { return ( - {({ height, getVerticalBreakpoint }) => ( + {({ width, getBreakpoint, height, getVerticalBreakpoint }) => (
{
{ return ( - {({ height, getVerticalBreakpoint }) => ( + {({ width, getBreakpoint, height, getVerticalBreakpoint }) => (
{ | 'actionGroupPlain' | 'labelGroup' ], - formatBreakpointMods(visibility, styles), + formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), formatBreakpointMods(visibilityAtHeight, styles, '', getVerticalBreakpoint(height), true), - formatBreakpointMods(align, styles), - formatBreakpointMods(gap, styles), - formatBreakpointMods(columnGap, styles), - formatBreakpointMods(rowGap, styles), - formatBreakpointMods(rowWrap, styles), - formatBreakpointMods(flexGrow, styles), + formatBreakpointMods(align, styles, '', getBreakpoint(width)), + formatBreakpointMods(gap, styles, '', getBreakpoint(width)), + formatBreakpointMods(columnGap, styles, '', getBreakpoint(width)), + formatBreakpointMods(rowGap, styles, '', getBreakpoint(width)), + formatBreakpointMods(rowWrap, styles, '', getBreakpoint(width)), + formatBreakpointMods(flexGrow, styles, '', getBreakpoint(width)), alignItems === 'start' && styles.modifiers.alignItemsStart, alignItems === 'center' && styles.modifiers.alignItemsCenter, alignItems === 'baseline' && styles.modifiers.alignItemsBaseline, diff --git a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx index 4328f9888f4..beec76f4272 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -237,7 +237,7 @@ export const ToolbarItem: React.FunctionComponent = ({ return ( - {({ height, getVerticalBreakpoint }) => ( + {({ width, getBreakpoint, height, getVerticalBreakpoint }) => (
= ({ variant === ToolbarItemVariant['label-group'] && styles.modifiers.labelGroup, isAllExpanded && styles.modifiers.expanded, isOverflowContainer && styles.modifiers.overflowContainer, - formatBreakpointMods(visibility, styles), + formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), formatBreakpointMods(visibilityAtHeight, styles, '', getVerticalBreakpoint(height), true), - formatBreakpointMods(align, styles), - formatBreakpointMods(gap, styles), - formatBreakpointMods(columnGap, styles), - formatBreakpointMods(rowGap, styles), - formatBreakpointMods(rowWrap, styles), - formatBreakpointMods(flexGrow, styles), + formatBreakpointMods(align, styles, '', getBreakpoint(width)), + formatBreakpointMods(gap, styles, '', getBreakpoint(width)), + formatBreakpointMods(columnGap, styles, '', getBreakpoint(width)), + formatBreakpointMods(rowGap, styles, '', getBreakpoint(width)), + formatBreakpointMods(rowWrap, styles, '', getBreakpoint(width)), + formatBreakpointMods(flexGrow, styles, '', getBreakpoint(width)), alignItems === 'start' && styles.modifiers.alignItemsStart, alignItems === 'center' && styles.modifiers.alignItemsCenter, alignItems === 'baseline' && styles.modifiers.alignItemsBaseline, diff --git a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx index e433bd02b04..58e166790ff 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx @@ -8,6 +8,7 @@ import { Button } from '../Button'; import globalBreakpointLg from '@patternfly/react-tokens/dist/esm/t_global_breakpoint_lg'; import { formatBreakpointMods, toCamel } from '../../helpers/util'; import { ToolbarExpandableContent } from './ToolbarExpandableContent'; +import { PageContext } from '../Page/PageContext'; export interface ToolbarToggleGroupProps extends ToolbarGroupProps { /** Flag indicating when toggle group is expanded for non-managed toolbar toggle groups. */ @@ -187,97 +188,102 @@ class ToolbarToggleGroup extends Component { } return ( - - {({ toggleIsExpanded: managedOnToggle, isContainer, toolbarRef }) => { - const _onToggle = onToggle !== undefined ? onToggle : managedOnToggle; + + {({ width, getBreakpoint }) => ( + + {({ toggleIsExpanded: managedOnToggle, isContainer, toolbarRef }) => { + const _onToggle = onToggle !== undefined ? onToggle : managedOnToggle; - return ( - - {({ - expandableContentRef, - expandableContentId, - labelContainerRef: managedLabelContainerRef, - isExpanded: managedIsExpanded, - clearAllFilters: clearAllFiltersContext, - clearFiltersButtonText: clearFiltersButtonContext, - showClearFiltersButton: showClearFiltersButtonContext - }) => { - const _isExpanded = isExpanded !== undefined ? isExpanded : managedIsExpanded; - const _labelContainerRef = - labelContainerRef !== undefined ? labelContainerRef : managedLabelContainerRef; + return ( + + {({ + expandableContentRef, + expandableContentId, + labelContainerRef: managedLabelContainerRef, + isExpanded: managedIsExpanded, + clearAllFilters: clearAllFiltersContext, + clearFiltersButtonText: clearFiltersButtonContext, + showClearFiltersButton: showClearFiltersButtonContext + }) => { + const _isExpanded = isExpanded !== undefined ? isExpanded : managedIsExpanded; + const _labelContainerRef = + labelContainerRef !== undefined ? labelContainerRef : managedLabelContainerRef; - const breakpointMod: { - md?: 'show'; - lg?: 'show'; - xl?: 'show'; - '2xl'?: 'show'; - } = {}; - breakpointMod[breakpoint] = 'show'; + const breakpointMod: { + md?: 'show'; + lg?: 'show'; + xl?: 'show'; + '2xl'?: 'show'; + } = {}; + breakpointMod[breakpoint] = 'show'; - const expandableContent = ( - - {children} - - ); + const expandableContent = ( + + {children} + + ); - const toggleButton = ( -
-
- ); + const toggleButton = ( +
+
+ ); - return ( -
- {toggleButton} - {_isExpanded && ReactDOM.createPortal(expandableContent, expandableContentRef.current)} - {!_isExpanded && children} -
- ); - }} -
- ); - }} -
+ return ( +
+ {toggleButton} + {_isExpanded && ReactDOM.createPortal(expandableContent, expandableContentRef.current)} + {!_isExpanded && children} +
+ ); + }} + + ); + }} +
+ )} + ); } } From dfb54ef16b75634be2a09d0887af52d5ba05ead2 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Fri, 2 Oct 2026 13:55:12 -0400 Subject: [PATCH 12/12] fix(ToolbarToogleGroup): Fix aria-haspopup logic Breakpoint calculation size was slightly off, so aria-haspopup didn't always trigger. Assisted-by: OpenCode --- .../components/Toolbar/ToolbarToggleGroup.tsx | 6 ++--- .../__tests__/ToolbarToggleGroup.test.tsx | 23 ++++++++++++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx index 58e166790ff..27c83c5745b 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarToggleGroup.tsx @@ -3,9 +3,8 @@ import * as ReactDOM from 'react-dom'; import styles from '@patternfly/react-styles/css/components/Toolbar/toolbar'; import { css } from '@patternfly/react-styles'; import { ToolbarGroupProps } from './ToolbarGroup'; -import { ToolbarContext, ToolbarContentContext } from './ToolbarUtils'; +import { globalBreakpoints, ToolbarContext, ToolbarContentContext } from './ToolbarUtils'; import { Button } from '../Button'; -import globalBreakpointLg from '@patternfly/react-tokens/dist/esm/t_global_breakpoint_lg'; import { formatBreakpointMods, toCamel } from '../../helpers/util'; import { ToolbarExpandableContent } from './ToolbarExpandableContent'; import { PageContext } from '../Page/PageContext'; @@ -158,8 +157,7 @@ class ToolbarToggleGroup extends Component { isContentPopup = (width?: number) => { const size = width ?? (typeof window !== 'undefined' ? window.innerWidth : 1200); - const lgBreakpointValue = parseInt(globalBreakpointLg.value); - return size < lgBreakpointValue; + return size < globalBreakpoints.lg; }; render() { diff --git a/packages/react-core/src/components/Toolbar/__tests__/ToolbarToggleGroup.test.tsx b/packages/react-core/src/components/Toolbar/__tests__/ToolbarToggleGroup.test.tsx index da837642654..b2169bb5cb8 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/ToolbarToggleGroup.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/ToolbarToggleGroup.test.tsx @@ -1,5 +1,6 @@ import { Fragment } from 'react'; -import { render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; import { ToolbarToggleGroup } from '../ToolbarToggleGroup'; import { Toolbar } from '../Toolbar'; import { ToolbarContent } from '../ToolbarContent'; @@ -25,4 +26,24 @@ describe('ToolbarToggleGroup', () => { expect(myMock).toHaveBeenCalled(); }); + + it('sets aria-haspopup when expandable content is a popup in a narrow toolbar container', () => { + const clientWidthMock = jest.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockReturnValue(600); + + render( + + + }> + Filter controls + + + + ); + + const toggle = screen.getByRole('button', { name: 'Show Filters' }); + fireEvent.click(toggle); + + expect(toggle).toHaveAttribute('aria-haspopup', 'true'); + clientWidthMock.mockRestore(); + }); });