Skip to content

Commit 6bcdcaf

Browse files
feat(Compass): updated mainheader structure to be composable (#12135)
* feat(Compass): updated mainheader structure to be composable * Use new components instead of divs * Missed package version in rebase * Added tests for compassPanelProps --------- Co-authored-by: Eric Olkowski <git.eric@thatblindgeye.dev>
1 parent b1ae7eb commit 6bcdcaf

21 files changed

+309
-30
lines changed

packages/react-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"tslib": "^2.8.1"
5555
},
5656
"devDependencies": {
57-
"@patternfly/patternfly": "6.5.0-prerelease.21",
57+
"@patternfly/patternfly": "6.5.0-prerelease.22",
5858
"case-anything": "^3.1.2",
5959
"css": "^3.0.0",
6060
"fs-extra": "^11.3.0"

packages/react-core/src/components/Compass/CompassMainHeader.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,44 @@
1-
import { Flex, FlexItem } from '../../layouts/Flex';
2-
import { CompassPanel } from './CompassPanel';
1+
import { CompassPanel, CompassPanelProps } from './CompassPanel';
2+
import { CompassMainHeaderContent } from './CompassMainHeaderContent';
3+
import { CompassMainHeaderTitle } from './CompassMainHeaderTitle';
4+
import { CompassMainHeaderToolbar } from './CompassMainHeaderToolbar';
35
import styles from '@patternfly/react-styles/css/components/Compass/compass';
46
import { css } from '@patternfly/react-styles';
57

8+
/** The wrapper component for header content in the main compass area. When building out a custom implementation,
9+
* you should ensure any content within the main header is rendered inside a compass panel and main header content wrappers.
10+
*/
11+
612
export interface CompassMainHeaderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'title'> {
13+
/** Custom main header content. To opt into a default styling, use the title and toolbar props instead. */
14+
children?: React.ReactNode;
715
/** Additional classes added to the main header */
816
className?: string;
917
/** Styled title. If title or toolbar is provided, the children will be ignored. */
1018
title?: React.ReactNode;
1119
/** Styled toolbar. If title or toolbar is provided, the children will be ignored. */
1220
toolbar?: React.ReactNode;
13-
/** Custom main header content. To opt into a default styling, use the title and toolbar props instead. */
14-
children?: React.ReactNode;
21+
/** Additional props passed to the compass panel that wraps the main header content when using the title or toolbar props. When using the
22+
* children prop, you should pass your own compass panel.
23+
*/
24+
compassPanelProps?: Omit<CompassPanelProps, 'children'>;
1525
}
1626

1727
export const CompassMainHeader: React.FunctionComponent<CompassMainHeaderProps> = ({
1828
className,
1929
title,
2030
toolbar,
2131
children,
32+
compassPanelProps,
2233
...props
2334
}) => {
2435
const _content =
2536
title !== undefined || toolbar !== undefined ? (
26-
<CompassPanel>
27-
<Flex alignItems={{ default: 'alignItemsCenter' }}>
28-
<FlexItem grow={{ default: 'grow' }}>{title}</FlexItem>
29-
{toolbar && <FlexItem>{toolbar}</FlexItem>}
30-
</Flex>
37+
<CompassPanel {...compassPanelProps}>
38+
<CompassMainHeaderContent>
39+
{title && <CompassMainHeaderTitle>{title}</CompassMainHeaderTitle>}
40+
{toolbar && <CompassMainHeaderToolbar>{toolbar}</CompassMainHeaderToolbar>}
41+
</CompassMainHeaderContent>
3142
</CompassPanel>
3243
) : (
3344
children
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
/** A wrapper component to be passed as custom content for the compass main header. This should also be wrapped
5+
* in a compass panel component.
6+
*/
7+
8+
export interface CompassMainHeaderContentProps extends React.HTMLProps<HTMLDivElement> {
9+
/** Content of the main header content. */
10+
children: React.ReactNode;
11+
/** Additional classes added to the main header content. */
12+
className?: string;
13+
}
14+
15+
export const CompassMainHeaderContent: React.FunctionComponent<CompassMainHeaderContentProps> = ({
16+
children,
17+
className,
18+
...props
19+
}) => (
20+
<div className={css(styles.compassMainHeaderContent, className)} {...props}>
21+
{children}
22+
</div>
23+
);
24+
25+
CompassMainHeaderContent.displayName = 'CompassMainHeaderContent';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
/** A wrapper component for custom title content to be passed into a compass main header. This should also be wrapped
5+
* by a compass main header content component.
6+
*/
7+
8+
export interface CompassMainHeaderTitleProps extends React.HTMLProps<HTMLDivElement> {
9+
/** Content of the main header title. */
10+
children: React.ReactNode;
11+
/** Additional classes added to the main header title. */
12+
className?: string;
13+
}
14+
15+
export const CompassMainHeaderTitle: React.FunctionComponent<CompassMainHeaderTitleProps> = ({
16+
children,
17+
className,
18+
...props
19+
}) => (
20+
<div className={css(`${styles.compass}__main-header-title`, className)} {...props}>
21+
{children}
22+
</div>
23+
);
24+
25+
CompassMainHeaderTitle.displayName = 'CompassMainHeaderTitle';
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
2+
import { css } from '@patternfly/react-styles';
3+
4+
/** A wrapper component for custom toolbar content to be passed into a compass main header. This should also be wrapped
5+
* by a compass main header content component.
6+
*/
7+
8+
export interface CompassMainHeaderToolbarProps extends React.HTMLProps<HTMLDivElement> {
9+
/** Content of the main header toolbar. */
10+
children: React.ReactNode;
11+
/** Additional classes added to the main header toolbar. */
12+
className?: string;
13+
}
14+
15+
export const CompassMainHeaderToolbar: React.FunctionComponent<CompassMainHeaderToolbarProps> = ({
16+
children,
17+
className,
18+
...props
19+
}) => (
20+
<div className={css(`${styles.compass}__main-header-toolbar`, className)} {...props}>
21+
{children}
22+
</div>
23+
);
24+
25+
CompassMainHeaderToolbar.displayName = 'CompassMainHeaderToolbar';

packages/react-core/src/components/Compass/__tests__/CompassMainHeader.test.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,52 @@ test('Renders children when neither title nor toolbar are provided', () => {
7474
expect(screen.getByText('Custom children content')).toBeVisible();
7575
});
7676

77+
test('Renders CompassPanel when title is passed', () => {
78+
render(<CompassMainHeader data-testid="test-id" title="Title text" />);
79+
80+
const panel = screen.getByTestId('test-id').firstChild;
81+
expect(panel).toHaveClass(styles.compassPanel);
82+
});
83+
84+
test('Renders CompassPanel when toolbar is passed', () => {
85+
render(<CompassMainHeader data-testid="test-id" toolbar="Toolbar text" />);
86+
87+
const panel = screen.getByTestId('test-id').firstChild;
88+
expect(panel).toHaveClass(styles.compassPanel);
89+
});
90+
91+
test('Does not render CompassPanel when children are passed', () => {
92+
render(
93+
<CompassMainHeader data-testid="test-id">
94+
<div>Children content</div>
95+
</CompassMainHeader>
96+
);
97+
98+
const content = screen.getByTestId('test-id').firstChild;
99+
expect(content).not.toHaveClass(styles.compassPanel);
100+
});
101+
102+
test('Passes props to CompassPanel when title and compassPanelProps is passed', () => {
103+
render(
104+
<CompassMainHeader data-testid="test-id" compassPanelProps={{ className: 'panel-class' }} title="Title text" />
105+
);
106+
107+
const panel = screen.getByTestId('test-id').firstChild;
108+
expect(panel).toHaveClass('panel-class');
109+
});
110+
111+
test('Passes props to CompassPanel when toolbar and compassPanelProps is passed', () => {
112+
render(
113+
<CompassMainHeader data-testid="test-id" compassPanelProps={{ className: 'panel-class' }} toolbar="Toolbar text" />
114+
);
115+
116+
const panel = screen.getByTestId('test-id').firstChild;
117+
expect(panel).toHaveClass('panel-class');
118+
});
119+
77120
test('Renders with additional props spread to the component', () => {
78-
render(<CompassMainHeader aria-label="Test label">Test</CompassMainHeader>);
79-
expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
121+
render(<CompassMainHeader id="custom-id">Test</CompassMainHeader>);
122+
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
80123
});
81124

82125
test('Matches the snapshot with both title and toolbar', () => {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { CompassMainHeaderContent } from '../CompassMainHeaderContent';
3+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
4+
5+
test('Renders with children', () => {
6+
render(<CompassMainHeaderContent>Custom content</CompassMainHeaderContent>);
7+
expect(screen.getByText('Custom content')).toBeVisible();
8+
});
9+
10+
test(`Renders with default ${styles.compass}__main-header-content class`, () => {
11+
render(<CompassMainHeaderContent>Test</CompassMainHeaderContent>);
12+
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-content`);
13+
});
14+
15+
test('Renders with custom class name when className prop is provided', () => {
16+
render(<CompassMainHeaderContent className="custom-class">Test</CompassMainHeaderContent>);
17+
expect(screen.getByText('Test')).toHaveClass('custom-class');
18+
});
19+
20+
test('Renders with additional props spread to the component', () => {
21+
render(<CompassMainHeaderContent id="custom-id">Test</CompassMainHeaderContent>);
22+
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
23+
});
24+
25+
test('Matches the snapshot', () => {
26+
const { asFragment } = render(<CompassMainHeaderContent>Content</CompassMainHeaderContent>);
27+
expect(asFragment()).toMatchSnapshot();
28+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { CompassMainHeaderTitle } from '../CompassMainHeaderTitle';
3+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
4+
5+
test('Renders with children', () => {
6+
render(<CompassMainHeaderTitle>Custom content</CompassMainHeaderTitle>);
7+
expect(screen.getByText('Custom content')).toBeVisible();
8+
});
9+
10+
test(`Renders with default ${styles.compass}__main-header-title class`, () => {
11+
render(<CompassMainHeaderTitle>Test</CompassMainHeaderTitle>);
12+
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-title`);
13+
});
14+
15+
test('Renders with custom class name when className prop is provided', () => {
16+
render(<CompassMainHeaderTitle className="custom-class">Test</CompassMainHeaderTitle>);
17+
expect(screen.getByText('Test')).toHaveClass('custom-class');
18+
});
19+
20+
test('Renders with additional props spread to the component', () => {
21+
render(<CompassMainHeaderTitle id="custom-id">Test</CompassMainHeaderTitle>);
22+
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
23+
});
24+
25+
test('Matches the snapshot', () => {
26+
const { asFragment } = render(<CompassMainHeaderTitle>Content</CompassMainHeaderTitle>);
27+
expect(asFragment()).toMatchSnapshot();
28+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { CompassMainHeaderToolbar } from '../CompassMainHeaderToolbar';
3+
import styles from '@patternfly/react-styles/css/components/Compass/compass';
4+
5+
test('Renders with children', () => {
6+
render(<CompassMainHeaderToolbar>Custom content</CompassMainHeaderToolbar>);
7+
expect(screen.getByText('Custom content')).toBeVisible();
8+
});
9+
10+
test(`Renders with default ${styles.compass}__main-header-toolbar class`, () => {
11+
render(<CompassMainHeaderToolbar>Test</CompassMainHeaderToolbar>);
12+
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-toolbar`);
13+
});
14+
15+
test('Renders with custom class name when className prop is provided', () => {
16+
render(<CompassMainHeaderToolbar className="custom-class">Test</CompassMainHeaderToolbar>);
17+
expect(screen.getByText('Test')).toHaveClass('custom-class');
18+
});
19+
20+
test('Renders with additional props spread to the component', () => {
21+
render(<CompassMainHeaderToolbar id="custom-id">Test</CompassMainHeaderToolbar>);
22+
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
23+
});
24+
25+
test('Matches the snapshot', () => {
26+
const { asFragment } = render(<CompassMainHeaderToolbar>Content</CompassMainHeaderToolbar>);
27+
expect(asFragment()).toMatchSnapshot();
28+
});

packages/react-core/src/components/Compass/__tests__/__snapshots__/CompassMainHeader.test.tsx.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ exports[`Matches the snapshot with both title and toolbar 1`] = `
99
class="pf-v6-c-compass__panel"
1010
>
1111
<div
12-
class="pf-v6-l-flex pf-m-align-items-center"
12+
class="pf-v6-c-compass__main-header-content"
1313
>
1414
<div
15-
class="pf-m-grow"
15+
class="pf-v6-c-compass__main-header-title"
1616
>
1717
<div>
1818
Title
1919
</div>
2020
</div>
2121
<div
22-
class=""
22+
class="pf-v6-c-compass__main-header-toolbar"
2323
>
2424
<div>
2525
Toolbar

0 commit comments

Comments
 (0)