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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/react-core/src/components/Toolbar/ToolbarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ export interface ToolbarGroupProps extends Omit<React.HTMLProps<HTMLDivElement>,
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. */
Expand All @@ -185,6 +194,7 @@ class ToolbarGroupWithRef extends Component<ToolbarGroupProps> {
columnGap,
rowGap,
rowWrap,
flexGrow,
className,
variant,
children,
Expand Down Expand Up @@ -214,6 +224,7 @@ class ToolbarGroupWithRef extends Component<ToolbarGroupProps> {
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,
Expand Down
28 changes: 27 additions & 1 deletion packages/react-core/src/components/Toolbar/ToolbarItem.tsx
Original file line number Diff line number Diff line change
@@ -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',
Expand Down Expand Up @@ -160,6 +161,24 @@ export interface ToolbarItemProps extends React.HTMLProps<HTMLDivElement> {
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 */
Expand All @@ -178,6 +197,8 @@ export const ToolbarItem: React.FunctionComponent<ToolbarItemProps> = ({
columnGap,
rowGap,
rowWrap,
flexGrow,
widths,
align,
alignSelf,
alignItems,
Expand All @@ -186,6 +207,7 @@ export const ToolbarItem: React.FunctionComponent<ToolbarItemProps> = ({
isAllExpanded,
isOverflowContainer,
role,
style,
...props
}: ToolbarItemProps) => {
if (variant === ToolbarItemVariant.separator) {
Expand All @@ -200,6 +222,8 @@ export const ToolbarItem: React.FunctionComponent<ToolbarItemProps> = ({
);
}

const responsiveWidths = widths ? setBreakpointCssVars(widths, cssToolbarItemWidth.name) : {};

return (
<PageContext.Consumer>
{({ width, getBreakpoint }) => (
Expand All @@ -216,6 +240,7 @@ export const ToolbarItem: React.FunctionComponent<ToolbarItemProps> = ({
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,
Expand All @@ -227,6 +252,7 @@ export const ToolbarItem: React.FunctionComponent<ToolbarItemProps> = ({
{...(variant === 'label' && { 'aria-hidden': true })}
id={id}
role={role}
style={{ ...style, ...responsiveWidths }}
{...props}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<ToolbarGroup data-testid="toolbargroup" flexGrow={{ [bp]: 'flexGrow' }}>
Test
</ToolbarGroup>
);
const bpFlexGrowClass = bp === 'default' ? 'pf-m-flex-grow' : `pf-m-flex-grow-on-${bp}`;
expect(screen.getByTestId('toolbargroup')).toHaveClass(bpFlexGrowClass);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<ToolbarItem data-testid="toolbaritem" flexGrow={{ [bp]: 'flexGrow' }}>
Test
</ToolbarItem>
);
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(
<ToolbarItem data-testid="toolbaritem" widths={{ default: '200px' }}>
Test
</ToolbarItem>
);
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(
<ToolbarItem data-testid="toolbaritem" widths={{ default: '100px', md: '200px', xl: '300px' }}>
Test
</ToolbarItem>
);
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'
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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"

```
Original file line number Diff line number Diff line change
@@ -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 = (
<Fragment>
<ToolbarItem>
<Button variant="secondary">Item 1</Button>
</ToolbarItem>
<ToolbarItem flexGrow={{ default: 'flexGrow' }}>
<SearchInput aria-label="Flex grow search input" />
</ToolbarItem>
<ToolbarItem>
<Button variant="secondary">Item 2</Button>
</ToolbarItem>
</Fragment>
);

const flexGrowGroupItems = (
<Fragment>
<ToolbarGroup>
<ToolbarItem>
<Button variant="secondary">Item 1</Button>
</ToolbarItem>
<ToolbarItem>
<Button variant="secondary">Item 2</Button>
</ToolbarItem>
</ToolbarGroup>
<ToolbarGroup flexGrow={{ default: 'flexGrow' }}>
<ToolbarItem>
<Button variant="secondary">Flex grow group item 1</Button>
</ToolbarItem>
<ToolbarItem>
<Button variant="secondary">Flex grow group item 2</Button>
</ToolbarItem>
</ToolbarGroup>
<ToolbarGroup>
<ToolbarItem>
<Button variant="secondary">Item 3</Button>
</ToolbarItem>
</ToolbarGroup>
</Fragment>
);

const widthItems = (
<Fragment>
<ToolbarItem widths={{ default: '200px' }}>
<SearchInput aria-label="Search input with fixed width" />
</ToolbarItem>
<ToolbarItem>
<Button variant="secondary">Regular item</Button>
</ToolbarItem>
<ToolbarItem widths={{ default: '300px' }}>
<SearchInput aria-label="Search input with wider fixed width" />
</ToolbarItem>
</Fragment>
);

const responsiveWidthItems = (
<Fragment>
<ToolbarItem widths={{ default: '100px', md: '200px', xl: '300px' }}>
<SearchInput aria-label="Search input with responsive width" />
</ToolbarItem>
<ToolbarItem>
<Button variant="secondary">Regular item</Button>
</ToolbarItem>
</Fragment>
);

return (
<>
Using flexGrow on ToolbarItem
<br />
<br />
<Toolbar id="toolbar-flex-grow-item">
<ToolbarContent>{flexGrowItems}</ToolbarContent>
</Toolbar>
<br />
<br />
Using flexGrow on ToolbarGroup
<br />
<br />
<Toolbar id="toolbar-flex-grow-group">
<ToolbarContent>{flexGrowGroupItems}</ToolbarContent>
</Toolbar>
<br />
<br />
Using widths on ToolbarItem
<br />
<br />
<Toolbar id="toolbar-widths">
<ToolbarContent>{widthItems}</ToolbarContent>
</Toolbar>
<br />
<br />
Using responsive widths on ToolbarItem
<br />
<br />
<Toolbar id="toolbar-responsive-widths">
<ToolbarContent>{responsiveWidthItems}</ToolbarContent>
</Toolbar>
</>
);
};
Loading