diff --git a/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx b/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx index ae2156b052d..7bb6712394b 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarGroup.tsx @@ -166,6 +166,15 @@ export interface ToolbarGroupProps extends Omit, xl?: 'wrap' | 'nowrap'; '2xl'?: 'wrap' | 'nowrap'; }; + /** Sets flex-grow at various breakpoints to allow the group to consume available main-axis space */ + flexGrow?: { + default?: 'flexGrow'; + sm?: 'flexGrow'; + md?: 'flexGrow'; + lg?: 'flexGrow'; + xl?: 'flexGrow'; + '2xl'?: 'flexGrow'; + }; /** Content to be rendered inside the data toolbar group */ children?: React.ReactNode; /** Flag that modifies the toolbar group to hide overflow and respond to available space. Used for horizontal navigation. */ @@ -185,6 +194,7 @@ class ToolbarGroupWithRef extends Component { columnGap, rowGap, rowWrap, + flexGrow, className, variant, children, @@ -214,6 +224,7 @@ class ToolbarGroupWithRef extends Component { 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 687654455ac..fbd54574efe 100644 --- a/packages/react-core/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react-core/src/components/Toolbar/ToolbarItem.tsx @@ -1,8 +1,9 @@ import styles from '@patternfly/react-styles/css/components/Toolbar/toolbar'; import { css } from '@patternfly/react-styles'; -import { formatBreakpointMods, toCamel } from '../../helpers/util'; +import { formatBreakpointMods, setBreakpointCssVars, toCamel } from '../../helpers/util'; import { Divider } from '../Divider'; import { PageContext } from '../Page/PageContext'; +import cssToolbarItemWidth from '@patternfly/react-tokens/dist/esm/c_toolbar__item_Width'; export enum ToolbarItemVariant { separator = 'separator', @@ -160,6 +161,24 @@ export interface ToolbarItemProps extends React.HTMLProps { xl?: 'wrap' | 'nowrap'; '2xl'?: 'wrap' | 'nowrap'; }; + /** Sets flex-grow at various breakpoints to allow the item to consume available main-axis space */ + flexGrow?: { + default?: 'flexGrow'; + sm?: 'flexGrow'; + md?: 'flexGrow'; + lg?: 'flexGrow'; + xl?: 'flexGrow'; + '2xl'?: 'flexGrow'; + }; + /** Width at various breakpoints. */ + widths?: { + default?: string; + sm?: string; + md?: string; + lg?: string; + xl?: string; + '2xl'?: string; + }; /** id for this data toolbar item */ id?: string; /** Flag indicating if the expand-all variant is expanded or not */ @@ -178,6 +197,8 @@ export const ToolbarItem: React.FunctionComponent = ({ columnGap, rowGap, rowWrap, + flexGrow, + widths, align, alignSelf, alignItems, @@ -186,6 +207,7 @@ export const ToolbarItem: React.FunctionComponent = ({ isAllExpanded, isOverflowContainer, role, + style, ...props }: ToolbarItemProps) => { if (variant === ToolbarItemVariant.separator) { @@ -200,6 +222,8 @@ export const ToolbarItem: React.FunctionComponent = ({ ); } + const responsiveWidths = widths ? setBreakpointCssVars(widths, cssToolbarItemWidth.name) : {}; + return ( {({ width, getBreakpoint }) => ( @@ -216,6 +240,7 @@ export const ToolbarItem: React.FunctionComponent = ({ 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, @@ -227,6 +252,7 @@ export const ToolbarItem: React.FunctionComponent = ({ {...(variant === 'label' && { 'aria-hidden': true })} id={id} role={role} + style={{ ...style, ...responsiveWidths }} {...props} > {children} diff --git a/packages/react-core/src/components/Toolbar/__tests__/ToolbarGroup.test.tsx b/packages/react-core/src/components/Toolbar/__tests__/ToolbarGroup.test.tsx index 935a5480ee1..8c68b959e46 100644 --- a/packages/react-core/src/components/Toolbar/__tests__/ToolbarGroup.test.tsx +++ b/packages/react-core/src/components/Toolbar/__tests__/ToolbarGroup.test.tsx @@ -37,4 +37,20 @@ describe('ToolbarGroup', () => { }); }); }); + + describe('ToolbarGroup 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('toolbargroup')).toHaveClass(bpFlexGrowClass); + }); + }); + }); }); 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..9281c2297d2 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,46 @@ describe('ToolbarItem', () => { }); }); }); + + 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); + }); + }); + }); + + describe('ToolbarItem widths', () => { + it('should apply width CSS variable when widths prop is set', () => { + render( + + Test + + ); + const item = screen.getByTestId('toolbaritem'); + expect(item).toHaveStyle('--pf-v6-c-toolbar__item--Width: 200px'); + }); + + it('should apply responsive width CSS variables when widths prop has breakpoint values', () => { + render( + + Test + + ); + const item = screen.getByTestId('toolbaritem'); + expect(item).toHaveStyle({ + '--pf-v6-c-toolbar__item--Width': '100px', + '--pf-v6-c-toolbar__item--Width-on-md': '200px', + '--pf-v6-c-toolbar__item--Width-on-xl': '300px' + }); + }); + }); }); diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index 89639b516c7..dbe308c2688 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -136,3 +136,11 @@ The toolbar content section will wrap by default, but you can set the `rowRap` p ```ts file="./ToolbarItemSpacers.tsx" ``` + +### FlexGrow and widths + +You can use the `flexGrow` prop to make toolbar items or groups expand to fill available space. The `widths` prop allows you to set custom widths at various breakpoints. + +```ts file="./ToolbarFlexGrowAndWidths.tsx" + +``` diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarFlexGrowAndWidths.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarFlexGrowAndWidths.tsx new file mode 100644 index 00000000000..7c4581c8517 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarFlexGrowAndWidths.tsx @@ -0,0 +1,104 @@ +import { Fragment } from 'react'; +import { Toolbar, ToolbarItem, ToolbarGroup, ToolbarContent, Button, SearchInput } from '@patternfly/react-core'; + +export const ToolbarFlexGrowAndWidths: React.FunctionComponent = () => { + const flexGrowItems = ( + + + + + + + + + + + + ); + + const flexGrowGroupItems = ( + + + + + + + + + + + + + + + + + + + + + + + + ); + + const widthItems = ( + + + + + + + + + + + + ); + + const responsiveWidthItems = ( + + + + + + + + + ); + + return ( + <> + Using flexGrow on ToolbarItem +
+
+ + {flexGrowItems} + +
+
+ Using flexGrow on ToolbarGroup +
+
+ + {flexGrowGroupItems} + +
+
+ Using widths on ToolbarItem +
+
+ + {widthItems} + +
+
+ Using responsive widths on ToolbarItem +
+
+ + {responsiveWidthItems} + + + ); +};