From 8ee57f209d9cf95dba1f35b358fd9e29536d7db0 Mon Sep 17 00:00:00 2001 From: samrudhi Date: Mon, 20 Oct 2025 23:30:19 +0530 Subject: [PATCH 1/6] fix(ToolbarItem): add width and flex-grow props and handle breakpoint classes (#11910) --- .yarnrc.yml | 6 +++ cleanup.js | 40 +++++++++++++++ .../src/components/Toolbar/ToolbarItem.tsx | 51 ++++++++++++++++--- .../Toolbar/__tests__/ToolbarItem.test.tsx | 33 ++++++++++++ .../__snapshots__/Toolbar.test.tsx.snap | 8 +++ .../src/components/demos/ToolbarDemo.tsx | 41 +++++++++++++++ .../demos/ToolbarDemo/ToolbarDemo.tsx | 12 ++++- scripts/build-single-packages.mjs | 28 ++++++---- 8 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 cleanup.js create mode 100644 packages/react-integration/demo-app-ts/src/components/demos/ToolbarDemo.tsx diff --git a/.yarnrc.yml b/.yarnrc.yml index 3186f3f0795..4b49c5be129 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1 +1,7 @@ +httpRetry: 5 + +httpTimeout: 600000 + nodeLinker: node-modules + +npmRegistryServer: "https://registry.npmjs.org" diff --git a/cleanup.js b/cleanup.js new file mode 100644 index 00000000000..ef488fba6a9 --- /dev/null +++ b/cleanup.js @@ -0,0 +1,40 @@ +const fs = require('fs'); +const path = require('path'); + +function deleteFolderRecursive(pathToDelete) { + if (fs.existsSync(pathToDelete)) { + fs.readdirSync(pathToDelete).forEach((file) => { + const curPath = path.join(pathToDelete, file); + if (fs.lstatSync(curPath).isDirectory()) { + deleteFolderRecursive(curPath); + } else { + try { + fs.unlinkSync(curPath); + } catch (err) { + // console.error(`Error deleting file ${curPath}:`, err); + } + } + }); + try { + fs.rmdirSync(pathToDelete); + } catch (err) { + // console.error(`Error deleting directory ${pathToDelete}:`, err); + } + } +} + +['node_modules', '.yarn'].forEach((dir) => { + // console.log(`Removing ${dir}...`); + deleteFolderRecursive(dir); +}); + +['.pnp.cjs', '.pnp.loader.mjs'].forEach((file) => { + if (fs.existsSync(file)) { + try { + fs.unlinkSync(file); + // console.log(`Removed ${file}`); + } catch (err) { + // console.error(`Error removing ${file}:`, err); + } + } +}); diff --git a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx index 687654455ac..24666545699 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -17,6 +17,24 @@ export interface ToolbarItemProps extends React.HTMLProps { className?: string; /** A type modifier which modifies spacing specifically depending on the type of item */ variant?: ToolbarItemVariant | 'pagination' | 'label' | 'label-group' | 'separator' | 'expand-all'; + /** Width modifier at various breakpoints */ + widths?: { + default?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + sm?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + md?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + lg?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + xl?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + '2xl'?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; + }; + /** Flex grow modifier at various breakpoints */ + flexGrow?: { + default?: 'flexGrow'; + sm?: 'flexGrow'; + md?: 'flexGrow'; + lg?: 'flexGrow'; + xl?: 'flexGrow'; + '2xl'?: 'flexGrow'; + }; /** Visibility at various breakpoints. */ visibility?: { default?: 'hidden' | 'visible'; @@ -185,6 +203,8 @@ export const ToolbarItem: React.FunctionComponent = ({ children, isAllExpanded, isOverflowContainer, + widths, + flexGrow, role, ...props }: ToolbarItemProps) => { @@ -210,23 +230,40 @@ export const ToolbarItem: React.FunctionComponent = ({ variant === ToolbarItemVariant['label-group'] && styles.modifiers.labelGroup, isAllExpanded && styles.modifiers.expanded, isOverflowContainer && styles.modifiers.overflowContainer, - formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), - 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)), alignItems === 'start' && styles.modifiers.alignItemsStart, alignItems === 'center' && styles.modifiers.alignItemsCenter, alignItems === 'baseline' && styles.modifiers.alignItemsBaseline, alignSelf === 'start' && styles.modifiers.alignSelfStart, alignSelf === 'center' && styles.modifiers.alignSelfCenter, alignSelf === 'baseline' && styles.modifiers.alignSelfBaseline, - className + formatBreakpointMods(visibility, styles, '', getBreakpoint(width)), + 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)), + className, + widths && + Object.entries(widths).reduce( + (acc, [bp, size]) => ({ + ...acc, + [`pf-m-w-${size}${bp !== 'default' ? `-on-${bp}` : ''}`]: true + }), + {} + ), + flexGrow && + Object.entries(flexGrow).reduce( + (acc, [bp]) => ({ + ...acc, + [`pf-m-flex-grow${bp !== 'default' ? `-on-${bp}` : ''}`]: true + }), + {} + ) )} {...(variant === 'label' && { 'aria-hidden': true })} id={id} role={role} + data-testid="toolbaritem" {...props} > {children} diff --git a/packages/react-core/src/components/Toolbar/__tests__/ToolbarItem.test.tsx b/packages/react-core/src/components/Toolbar/__tests__/ToolbarItem.test.tsx index 5fbd364a87a..b6a9f47ab37 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/ToolbarItem.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/ToolbarItem.test.tsx @@ -37,4 +37,37 @@ describe('ToolbarItem', () => { }); }); }); + + describe('ToolbarItem widths', () => { + const bps = ['default', 'sm', 'md', 'lg', 'xl', '2xl']; + const sizes = ['sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl']; + + describe.each(bps)('widths at various breakpoints', (bp) => { + it.each(sizes)(`should render with pf-m-w-%s when widths is set to %s at ${bp}`, (size) => { + render( + + Test + + ); + const bpWidthClass = bp === 'default' ? `pf-m-w-${size}` : `pf-m-w-${size}-on-${bp}`; + expect(screen.getByTestId('toolbaritem')).toHaveClass(bpWidthClass); + }); + }); + }); + + describe('ToolbarItem flexGrow', () => { + const bps = ['default', 'sm', 'md', 'lg', 'xl', '2xl']; + + describe.each(bps)('flexGrow at various breakpoints', (bp) => { + it(`should render with pf-m-flex-grow when flexGrow is set at ${bp}`, () => { + render( + + Test + + ); + const bpFlexGrowClass = bp === 'default' ? 'pf-m-flex-grow' : `pf-m-flex-grow-on-${bp}`; + expect(screen.getByTestId('toolbaritem')).toHaveClass(bpFlexGrowClass); + }); + }); + }); }); diff --git a/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap b/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap index 9eb38733f4e..88f87e77471 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap +++ b/packages/react-core/src/components/Toolbar/__tests__/__snapshots__/Toolbar.test.tsx.snap @@ -17,11 +17,13 @@ exports[`Toolbar should render inset 1`] = ` >
Test
Test 2
@@ -30,6 +32,7 @@ exports[`Toolbar should render inset 1`] = ` />
Test 3
@@ -87,6 +90,7 @@ exports[`Toolbar should render with custom label content 1`] = ` >
test content
@@ -104,6 +108,7 @@ exports[`Toolbar should render with custom label content 1`] = ` >
2 filters applied
+ + + + + + + + + + + +
+ ); +}; diff --git a/packages/react-integration/demo-app-ts/src/components/demos/ToolbarDemo/ToolbarDemo.tsx b/packages/react-integration/demo-app-ts/src/components/demos/ToolbarDemo/ToolbarDemo.tsx index 515f0023e0d..d37a1c99ee3 100644 --- a/packages/react-integration/demo-app-ts/src/components/demos/ToolbarDemo/ToolbarDemo.tsx +++ b/packages/react-integration/demo-app-ts/src/components/demos/ToolbarDemo/ToolbarDemo.tsx @@ -1,4 +1,4 @@ -import { Component } from 'react'; +import * as React from 'react'; import { Button, ButtonVariant, @@ -45,7 +45,7 @@ interface ToolbarState { kebabIsOpen: boolean; } -class ToolbarDemo extends Component { +class ToolbarDemo extends React.Component { static displayName = 'ToolbarDemo'; state = { isOpen: false, @@ -252,6 +252,14 @@ class ToolbarDemo extends Component { } breakpoint="xl" id="demo-toggle-group"> {toggleGroupItems} + {/* Example of new width and flexGrow props */} + + + - - - - - - - - - - - -
- ); -}; From 44fe0963c452e84b204a5069ebd7683ab5a0b967 Mon Sep 17 00:00:00 2001 From: samrudhi Date: Sun, 9 Nov 2025 18:29:46 +0530 Subject: [PATCH 6/6] Toolbar: apply responsive width styles via setBreakpointCssVars; keep flexGrow via formatBreakpointMods; update JSDoc and tests --- .../src/components/Toolbar/ToolbarGroup.tsx | 11 ++----- .../src/components/Toolbar/ToolbarItem.tsx | 29 +++++++------------ 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx index 01e1e3528d6..65432b31a66 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx @@ -166,7 +166,7 @@ export interface ToolbarGroupProps extends Omit, xl?: 'wrap' | 'nowrap'; '2xl'?: 'wrap' | 'nowrap'; }; - /** Flex grow modifier at various breakpoints */ + /** Indicates whether a flex grow modifier of 1 is applied at various breakpoints */ flexGrow?: { default?: 'flexGrow'; sm?: 'flexGrow'; @@ -231,14 +231,7 @@ class ToolbarGroupWithRef extends Component { alignSelf === 'center' && styles.modifiers.alignSelfCenter, alignSelf === 'baseline' && styles.modifiers.alignSelfBaseline, isOverflowContainer && styles.modifiers.overflowContainer, - flexGrow && - Object.entries(flexGrow).reduce( - (acc, [bp]) => ({ - ...acc, - [`pf-m-flex-grow${bp !== 'default' ? `-on-${bp}` : ''}`]: true - }), - {} - ), + formatBreakpointMods(flexGrow, styles, '', getBreakpoint(width)), className )} {...props} diff --git a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx index 0d4f22817e5..e9c9ed97c43 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -2,13 +2,6 @@ import styles from '@patternfly/react-styles/css/components/Toolbar/toolbar'; import { css } from '@patternfly/react-styles'; import { formatBreakpointMods, setBreakpointCssVars, toCamel } from '../../helpers/util'; import c_toolbar__item_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_Width'; -import c_toolbar__item_m_w_sm_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_sm_Width'; -import c_toolbar__item_m_w_md_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_md_Width'; -import c_toolbar__item_m_w_lg_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_lg_Width'; -import c_toolbar__item_m_w_xl_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_xl_Width'; -import c_toolbar__item_m_w_2xl_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_2xl_Width'; -import c_toolbar__item_m_w_3xl_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_3xl_Width'; -import c_toolbar__item_m_w_4xl_Width from '@patternfly/react-tokens/dist/esm/c_toolbar__item_m_w_4xl_Width'; import { Divider } from '../Divider'; import { PageContext } from '../Page/PageContext'; @@ -34,7 +27,7 @@ export interface ToolbarItemProps extends React.HTMLProps { xl?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; '2xl'?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'; }; - /** Flex grow modifier at various breakpoints */ + /** Indicates whether a flex grow modifier of 1 is applied at various breakpoints */ flexGrow?: { default?: 'flexGrow'; sm?: 'flexGrow'; @@ -263,17 +256,17 @@ export const ToolbarItem: React.FunctionComponent = ({ if (!size) { return acc; } - const valueMap: Record = { - sm: (c_toolbar__item_m_w_sm_Width as any)?.value, - md: (c_toolbar__item_m_w_md_Width as any)?.value, - lg: (c_toolbar__item_m_w_lg_Width as any)?.value, - xl: (c_toolbar__item_m_w_xl_Width as any)?.value, - '2xl': (c_toolbar__item_m_w_2xl_Width as any)?.value, - '3xl': (c_toolbar__item_m_w_3xl_Width as any)?.value, - '4xl': (c_toolbar__item_m_w_4xl_Width as any)?.value + const cssVarValueMap: Record = { + sm: 'var(--pf-c-toolbar__item--m-w-sm--Width)', + md: 'var(--pf-c-toolbar__item--m-w-md--Width)', + lg: 'var(--pf-c-toolbar__item--m-w-lg--Width)', + xl: 'var(--pf-c-toolbar__item--m-w-xl--Width)', + '2xl': 'var(--pf-c-toolbar__item--m-w-2xl--Width)', + '3xl': 'var(--pf-c-toolbar__item--m-w-3xl--Width)', + '4xl': 'var(--pf-c-toolbar__item--m-w-4xl--Width)' }; - const tokenValue = valueMap[size as keyof typeof valueMap]; - return tokenValue ? { ...acc, [bp]: tokenValue } : acc; + const value = cssVarValueMap[size as keyof typeof cssVarValueMap]; + return value ? { ...acc, [bp]: value } : acc; }, {} as Record ),