Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/react-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@patternfly/patternfly": "6.5.0-prerelease.21",
"@patternfly/patternfly": "6.5.0-prerelease.22",
"case-anything": "^3.1.2",
"css": "^3.0.0",
"fs-extra": "^11.3.0"
Expand Down
29 changes: 20 additions & 9 deletions packages/react-core/src/components/Compass/CompassMainHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import { Flex, FlexItem } from '../../layouts/Flex';
import { CompassPanel } from './CompassPanel';
import { CompassPanel, CompassPanelProps } from './CompassPanel';
import { CompassMainHeaderContent } from './CompassMainHeaderContent';
import { CompassMainHeaderTitle } from './CompassMainHeaderTitle';
import { CompassMainHeaderToolbar } from './CompassMainHeaderToolbar';
import styles from '@patternfly/react-styles/css/components/Compass/compass';
import { css } from '@patternfly/react-styles';

/** The wrapper component for header content in the main compass area. When building out a custom implementation,
* you should ensure any content within the main header is rendered inside a compass panel and main header content wrappers.
*/

export interface CompassMainHeaderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'title'> {
/** Custom main header content. To opt into a default styling, use the title and toolbar props instead. */
children?: React.ReactNode;
/** Additional classes added to the main header */
className?: string;
/** Styled title. If title or toolbar is provided, the children will be ignored. */
title?: React.ReactNode;
/** Styled toolbar. If title or toolbar is provided, the children will be ignored. */
toolbar?: React.ReactNode;
/** Custom main header content. To opt into a default styling, use the title and toolbar props instead. */
children?: React.ReactNode;
/** Additional props passed to the compass panel that wraps the main header content when using the title or toolbar props. When using the
* children prop, you should pass your own compass panel.
*/
compassPanelProps?: Omit<CompassPanelProps, 'children'>;
}

export const CompassMainHeader: React.FunctionComponent<CompassMainHeaderProps> = ({
className,
title,
toolbar,
children,
compassPanelProps,
...props
}) => {
const _content =
title !== undefined || toolbar !== undefined ? (
<CompassPanel>
<Flex alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem grow={{ default: 'grow' }}>{title}</FlexItem>
{toolbar && <FlexItem>{toolbar}</FlexItem>}
</Flex>
<CompassPanel {...compassPanelProps}>
<CompassMainHeaderContent>
{title && <CompassMainHeaderTitle>{title}</CompassMainHeaderTitle>}
{toolbar && <CompassMainHeaderToolbar>{toolbar}</CompassMainHeaderToolbar>}
</CompassMainHeaderContent>
</CompassPanel>
) : (
children
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styles from '@patternfly/react-styles/css/components/Compass/compass';
import { css } from '@patternfly/react-styles';

/** A wrapper component to be passed as custom content for the compass main header. This should also be wrapped
* in a compass panel component.
*/

export interface CompassMainHeaderContentProps extends React.HTMLProps<HTMLDivElement> {
/** Content of the main header content. */
children: React.ReactNode;
/** Additional classes added to the main header content. */
className?: string;
}

export const CompassMainHeaderContent: React.FunctionComponent<CompassMainHeaderContentProps> = ({
children,
className,
...props
}) => (
<div className={css(styles.compassMainHeaderContent, className)} {...props}>
{children}
</div>
);

CompassMainHeaderContent.displayName = 'CompassMainHeaderContent';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styles from '@patternfly/react-styles/css/components/Compass/compass';
import { css } from '@patternfly/react-styles';

/** A wrapper component for custom title content to be passed into a compass main header. This should also be wrapped
* by a compass main header content component.
*/

export interface CompassMainHeaderTitleProps extends React.HTMLProps<HTMLDivElement> {
/** Content of the main header title. */
children: React.ReactNode;
/** Additional classes added to the main header title. */
className?: string;
}

export const CompassMainHeaderTitle: React.FunctionComponent<CompassMainHeaderTitleProps> = ({
children,
className,
...props
}) => (
<div className={css(`${styles.compass}__main-header-title`, className)} {...props}>
{children}
</div>
);

CompassMainHeaderTitle.displayName = 'CompassMainHeaderTitle';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styles from '@patternfly/react-styles/css/components/Compass/compass';
import { css } from '@patternfly/react-styles';

/** A wrapper component for custom toolbar content to be passed into a compass main header. This should also be wrapped
* by a compass main header content component.
*/

export interface CompassMainHeaderToolbarProps extends React.HTMLProps<HTMLDivElement> {
/** Content of the main header toolbar. */
children: React.ReactNode;
/** Additional classes added to the main header toolbar. */
className?: string;
}

export const CompassMainHeaderToolbar: React.FunctionComponent<CompassMainHeaderToolbarProps> = ({
children,
className,
...props
}) => (
<div className={css(`${styles.compass}__main-header-toolbar`, className)} {...props}>
{children}
</div>
);

CompassMainHeaderToolbar.displayName = 'CompassMainHeaderToolbar';
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,52 @@ test('Renders children when neither title nor toolbar are provided', () => {
expect(screen.getByText('Custom children content')).toBeVisible();
});

test('Renders CompassPanel when title is passed', () => {
render(<CompassMainHeader data-testid="test-id" title="Title text" />);

const panel = screen.getByTestId('test-id').firstChild;
expect(panel).toHaveClass(styles.compassPanel);
});

test('Renders CompassPanel when toolbar is passed', () => {
render(<CompassMainHeader data-testid="test-id" toolbar="Toolbar text" />);

const panel = screen.getByTestId('test-id').firstChild;
expect(panel).toHaveClass(styles.compassPanel);
});

test('Does not render CompassPanel when children are passed', () => {
render(
<CompassMainHeader data-testid="test-id">
<div>Children content</div>
</CompassMainHeader>
);

const content = screen.getByTestId('test-id').firstChild;
expect(content).not.toHaveClass(styles.compassPanel);
});

test('Passes props to CompassPanel when title and compassPanelProps is passed', () => {
render(
<CompassMainHeader data-testid="test-id" compassPanelProps={{ className: 'panel-class' }} title="Title text" />
);

const panel = screen.getByTestId('test-id').firstChild;
expect(panel).toHaveClass('panel-class');
});

test('Passes props to CompassPanel when toolbar and compassPanelProps is passed', () => {
render(
<CompassMainHeader data-testid="test-id" compassPanelProps={{ className: 'panel-class' }} toolbar="Toolbar text" />
);

const panel = screen.getByTestId('test-id').firstChild;
expect(panel).toHaveClass('panel-class');
});

test('Renders with additional props spread to the component', () => {
render(<CompassMainHeader aria-label="Test label">Test</CompassMainHeader>);
expect(screen.getByText('Test')).toHaveAccessibleName('Test label');
render(<CompassMainHeader id="custom-id">Test</CompassMainHeader>);
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
});

test('Matches the snapshot with both title and toolbar', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { render, screen } from '@testing-library/react';
import { CompassMainHeaderContent } from '../CompassMainHeaderContent';
import styles from '@patternfly/react-styles/css/components/Compass/compass';

test('Renders with children', () => {
render(<CompassMainHeaderContent>Custom content</CompassMainHeaderContent>);
expect(screen.getByText('Custom content')).toBeVisible();
});

test(`Renders with default ${styles.compass}__main-header-content class`, () => {
render(<CompassMainHeaderContent>Test</CompassMainHeaderContent>);
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-content`);
});

test('Renders with custom class name when className prop is provided', () => {
render(<CompassMainHeaderContent className="custom-class">Test</CompassMainHeaderContent>);
expect(screen.getByText('Test')).toHaveClass('custom-class');
});

test('Renders with additional props spread to the component', () => {
render(<CompassMainHeaderContent id="custom-id">Test</CompassMainHeaderContent>);
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
});

test('Matches the snapshot', () => {
const { asFragment } = render(<CompassMainHeaderContent>Content</CompassMainHeaderContent>);
expect(asFragment()).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { render, screen } from '@testing-library/react';
import { CompassMainHeaderTitle } from '../CompassMainHeaderTitle';
import styles from '@patternfly/react-styles/css/components/Compass/compass';

test('Renders with children', () => {
render(<CompassMainHeaderTitle>Custom content</CompassMainHeaderTitle>);
expect(screen.getByText('Custom content')).toBeVisible();
});

test(`Renders with default ${styles.compass}__main-header-title class`, () => {
render(<CompassMainHeaderTitle>Test</CompassMainHeaderTitle>);
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-title`);
});

test('Renders with custom class name when className prop is provided', () => {
render(<CompassMainHeaderTitle className="custom-class">Test</CompassMainHeaderTitle>);
expect(screen.getByText('Test')).toHaveClass('custom-class');
});

test('Renders with additional props spread to the component', () => {
render(<CompassMainHeaderTitle id="custom-id">Test</CompassMainHeaderTitle>);
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
});

test('Matches the snapshot', () => {
const { asFragment } = render(<CompassMainHeaderTitle>Content</CompassMainHeaderTitle>);
expect(asFragment()).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { render, screen } from '@testing-library/react';
import { CompassMainHeaderToolbar } from '../CompassMainHeaderToolbar';
import styles from '@patternfly/react-styles/css/components/Compass/compass';

test('Renders with children', () => {
render(<CompassMainHeaderToolbar>Custom content</CompassMainHeaderToolbar>);
expect(screen.getByText('Custom content')).toBeVisible();
});

test(`Renders with default ${styles.compass}__main-header-toolbar class`, () => {
render(<CompassMainHeaderToolbar>Test</CompassMainHeaderToolbar>);
expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__main-header-toolbar`);
});

test('Renders with custom class name when className prop is provided', () => {
render(<CompassMainHeaderToolbar className="custom-class">Test</CompassMainHeaderToolbar>);
expect(screen.getByText('Test')).toHaveClass('custom-class');
});

test('Renders with additional props spread to the component', () => {
render(<CompassMainHeaderToolbar id="custom-id">Test</CompassMainHeaderToolbar>);
expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
});

test('Matches the snapshot', () => {
const { asFragment } = render(<CompassMainHeaderToolbar>Content</CompassMainHeaderToolbar>);
expect(asFragment()).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ exports[`Matches the snapshot with both title and toolbar 1`] = `
class="pf-v6-c-compass__panel"
>
<div
class="pf-v6-l-flex pf-m-align-items-center"
class="pf-v6-c-compass__main-header-content"
>
<div
class="pf-m-grow"
class="pf-v6-c-compass__main-header-title"
>
<div>
Title
</div>
</div>
<div
class=""
class="pf-v6-c-compass__main-header-toolbar"
>
<div>
Toolbar
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Matches the snapshot 1`] = `
<DocumentFragment>
<div
class="pf-v6-c-compass__main-header-content"
>
Content
</div>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Matches the snapshot 1`] = `
<DocumentFragment>
<div
class="pf-v6-c-compass__main-header-title"
>
Content
</div>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Matches the snapshot 1`] = `
<DocumentFragment>
<div
class="pf-v6-c-compass__main-header-toolbar"
>
Content
</div>
</DocumentFragment>
`;
18 changes: 18 additions & 0 deletions packages/react-core/src/components/Compass/examples/Compass.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,21 @@ The background image of the `Compass` and `CompassHero` may be customized by usi
```ts isFullscreen file="CompassDemo.tsx"

```

## Composable structure

When building a more custom implementation using Compass components, there are some intended or expected structures that must be present.

### CompassMainHeader structure

When using the `children` property in the `<CompassMainHeader>` component, you should ensure that the expected sub-components are used. The following code block shows a general structure to follow.

```noLive
<CompassMainHeader>
<CompassPanel>
<CompassMainHeaderContent>
{Your custom content goes here, which can include the CompassMainHeaderTitle and/or CompassMainHeaderToolbar sub-components}
</CompassMainHeaderContent>
</CompassPanel>
</CompassMainHeader>
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Compass, CompassHeader, CompassHero, CompassContent, CompassMainHeader } from '@patternfly/react-core';
import {
Compass,
CompassHeader,
CompassHero,
CompassContent,
CompassMainHeader,
CompassPanel,
CompassMainHeaderContent
} from '@patternfly/react-core';
import './compass.css';

export const CompassBasic: React.FunctionComponent = () => {
Expand All @@ -12,7 +20,11 @@ export const CompassBasic: React.FunctionComponent = () => {
</CompassHero>
<CompassContent>
<CompassMainHeader>
<div>Content title</div>
<CompassPanel>
<CompassMainHeaderContent>
<div>Content title</div>
</CompassMainHeaderContent>
</CompassPanel>
</CompassMainHeader>
<div>Content</div>
</CompassContent>
Expand Down
3 changes: 3 additions & 0 deletions packages/react-core/src/components/Compass/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ export * from './CompassContent';
export * from './CompassHeader';
export * from './CompassHero';
export * from './CompassMainHeader';
export * from './CompassMainHeaderContent';
export * from './CompassMainHeaderTitle';
export * from './CompassMainHeaderToolbar';
export * from './CompassMessageBar';
export * from './CompassPanel';
Loading
Loading