Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
feat(ContentHeader): add cypress test and adjust icon sizing and plac…
…ement
  • Loading branch information
aferd committed Jun 7, 2024
commit 6af78525966c4f037c9b54449f86bce9d40302ab
6 changes: 3 additions & 3 deletions cypress/component/ContentHeader.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import ContentHeader from '../../packages/module/dist/dynamic/ContentHeader';

describe('ContentHeader', () => {
it('should render ContentHeader title and subtitle', () => {
cy.mount(<ContentHeader title={'My title'} subtitle={'This is a subtitle for your content header'} />);
cy.mount(<ContentHeader title='My title' subtitle='This is a subtitle for your content header' />);
cy.get('title').should('exist')
cy.get('div h1').should('have.text', 'My title')
cy.get('div p').should('have.text', 'This is a subtitle for your content header')
cy.get('[data-ouia-component-id="ContentHeader-title"]').should('have.text', 'My title')
cy.get('[data-ouia-component-id="ContentHeader-subtitle"]').should('have.text', 'This is a subtitle for your content header')
})
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,54 @@ section: extensions
subsection: Component groups
id: Content header
source: react
propComponents: ['ContentHeader', 'ActionMenu']
propComponents: ['ContentHeader']
sourceLink: https://github.com/patternfly/react-component-groups/blob/main/packages/module/patternfly-docs/content/extensions/component-groups/examples/ContentHeader/ContentHeader.md
---

import ContentHeader from "@patternfly/react-component-groups/dist/dynamic/ContentHeader"
import { EllipsisVIcon } from '@patternfly/react-icons';
import contentHeaderIcon from '../../assets/icons/content-header-icon.svg'

The **content header** component displays a page title and description with a possibility of defining additional labels, links, or a page icon separated by a divider.
The **content header** component displays a page header section with a title, subtitle and other optional content.

## Examples

### Basic content header

In order to display a basic content header, pass the `title` and `subtitle`.

```js file="./ContentHeaderExample.tsx"

```

### Content header with breadcrumbs

You can display breadcrumbs above the title using the `breadcrumbs` property.

```js file="./ContentHeaderBreadCrumbExample.tsx"

```

### Content header with icon

Use the `icon` property to display your custom page icon separated with a [divider](/components/divider).

```js file="./ContentHeaderIconExample.tsx"

```

### Content header with label and link

To add specific element captions for user clarity and convenience, you can use the `label` property together with [label](/components/label) or your custom component. The `linkProps` can be used to define a link displayed under the subtitle.

```js file="./ContentHeaderLabelLinkExample.tsx"

```

### Content header with actions menu

In case you want to display actions in your header, you can use the `actionsMenu` property.

```js file="./ContentHeaderActionsExample.tsx"

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';
import { ActionList, ActionListItem, Dropdown, DropdownItem, DropdownList, MenuToggle, MenuToggleElement } from '@patternfly/react-core';
import { EllipsisVIcon } from '@patternfly/react-icons';

export const ActionsExample: React.FunctionComponent = () => {
const [ isOpen, setIsOpen ] = React.useState(false);

const onToggle = () => {
setIsOpen(!isOpen);
};

const onSelect = (event: React.MouseEvent<Element, MouseEvent> | undefined) => {
event?.stopPropagation();
setIsOpen(!isOpen);
};

const dropdownItems = (
<>
<DropdownItem to="#" key="link">
Link
</DropdownItem>
<DropdownItem key="action">Action</DropdownItem>
<DropdownItem to="#" key="disabled link" isDisabled>
Disabled Link
</DropdownItem>
</>
);

return (
<React.Fragment>
<ContentHeader
title='My Title'
subtitle='This is a subtitle for your content header'
actionMenu={
<ActionList>
<ActionListItem>
<Dropdown
popperProps={{ position: 'right' }}
onSelect={onSelect}
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
onClick={onToggle}
variant="plain"
isExpanded={isOpen}
aria-label="Action list single group kebab"
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={isOpen}
onOpenChange={(isOpen: boolean) => setIsOpen(isOpen)}
>
<DropdownList>{dropdownItems}</DropdownList>
</Dropdown>
</ActionListItem>
</ActionList>
}
/>
</React.Fragment>
)
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-console */
import React from 'react';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';
import { Breadcrumb, BreadcrumbItem } from '@patternfly/react-core';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable no-console */
import React from 'react';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* eslint-disable no-console */
import React from 'react';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';
import contentHeaderIcon from '../../assets/icons/content-header-icon.svg';


export const IconExample: React.FunctionComponent = () => (
<ContentHeader
title='My Title'
subtitle='This is a subtitle for your content header'
icon={<img src='https://console.redhat.com/apps/frontend-assets/rbac-landing/rbac-landing-icon.svg'/>}
icon={<img src={contentHeaderIcon} alt="content-header-icon" />}
/>
);
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable no-console */
import React from 'react';
import ContentHeader from '@patternfly/react-component-groups/dist/dynamic/ContentHeader';
import { Label } from '@patternfly/react-core';

export const BasicExample: React.FunctionComponent = () => (
<ContentHeader
title='My Title'
subtitle='This is a subtitle for your content header'
label='Org. Administrator'
label={<Label className="pf-v5-u-align-content-center">Org. Administrator</Label>}
linkProps={{
label: 'Go to this link',
isExternal: true,
Expand Down
114 changes: 60 additions & 54 deletions packages/module/src/ContentHeader/ContentHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
ButtonVariant,
ButtonProps,
Divider,
Label,
Icon,
} from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { createUseStyles } from 'react-jss';

export interface PageHeaderLinkProps extends ButtonProps {
/** Title for the link */
Expand Down Expand Up @@ -42,6 +41,12 @@ export interface ContentHeaderProps {
ouiaId?: string | number;
}

const useStyles = createUseStyles({
iconMinWidth: {
minWidth: '48px',
}
});

export const ContentHeader: React.FunctionComponent<React.PropsWithChildren<ContentHeaderProps>> = ({
title,
subtitle,
Expand All @@ -51,60 +56,61 @@ export const ContentHeader: React.FunctionComponent<React.PropsWithChildren<Cont
breadcrumbs = null,
actionMenu,
ouiaId = 'ContentHeader',
}: ContentHeaderProps) => (
<PageSection variant="light" className='pf-v5-u-p-md'>
<div className="pf-v5-u-mb-md">
{breadcrumbs}
</div>
<Flex>
{icon && (
<>
<FlexItem>
<Icon size="lg">
{icon}
</Icon>
</FlexItem>
<Divider orientation={{
default: 'vertical',
}} />
</>
}: ContentHeaderProps) => {
const classes = useStyles();

return (
<PageSection variant="light">
{ breadcrumbs && (
<div className="pf-v5-u-mb-md">
{breadcrumbs}
</div>
)}
<FlexItem flex={{ default: 'flex_1' }}>
<Split hasGutter>
<SplitItem>
<TextContent>
<Text component="h1" ouiaId={`${ouiaId}-title`}>
{title}
</Text>
</TextContent>
</SplitItem>
{label && (
<SplitItem>
<Label className='pf-v5-u-mb-md' isCompact>
{label}
</Label>
</SplitItem>
)}
<SplitItem isFilled />
{actionMenu && (
<Flex>
{icon && (
<>
<FlexItem alignSelf={{ default: 'alignSelfCenter' }} className={`${classes.iconMinWidth}`}>
{icon}
</FlexItem>
<Divider orientation={{
default: 'vertical',
}} />
</>
)}
<FlexItem flex={{ default: 'flex_1' }}>
<Split hasGutter>
<SplitItem>
{actionMenu}
<TextContent>
<Text className="pf-v5-u-mb-sm" component="h1" ouiaId={`${ouiaId}-title`}>
{title}
</Text>
</TextContent>
</SplitItem>
)}
</Split>
<TextContent>
<Text component="p" ouiaId={`${ouiaId}-subtitle`}>
{subtitle}
</Text>
{linkProps && (
<Button variant={ButtonVariant.link} ouiaId={`${ouiaId}-link-button`} isInline icon={linkProps.isExternal ? <ExternalLinkAltIcon /> : null} iconPosition="end" {...linkProps}>
{linkProps.label}
</Button>
)}
</TextContent>
</FlexItem>
</Flex>
</PageSection>
);
{label && (
<SplitItem>
{label}
</SplitItem>
)}
<SplitItem isFilled />
{actionMenu && (
<SplitItem>
{actionMenu}
</SplitItem>
)}
</Split>
<TextContent>
<Text component="p" ouiaId={`${ouiaId}-subtitle`}>
{subtitle}
</Text>
{linkProps && (
<Button variant={ButtonVariant.link} ouiaId={`${ouiaId}-link-button`} isInline icon={linkProps.isExternal ? <ExternalLinkAltIcon /> : null} iconPosition="end" {...linkProps}>
{linkProps.label}
</Button>
)}
</TextContent>
</FlexItem>
</Flex>
</PageSection>
)};

export default ContentHeader;
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ exports[`Contentheader component should render 1`] = `
"baseElement": <body>
<div>
<section
class="pf-v5-c-page__main-section pf-m-light pf-v5-u-p-md"
class="pf-v5-c-page__main-section pf-m-light"
>
<div
class="pf-v5-u-mb-md"
/>
<div
class="pf-v5-l-flex"
>
Expand All @@ -27,7 +24,7 @@ exports[`Contentheader component should render 1`] = `
class="pf-v5-c-content"
>
<h1
class=""
class="pf-v5-u-mb-sm"
data-ouia-component-id="ContentHeader-title"
data-ouia-component-type="PF5/Text"
data-ouia-safe="true"
Expand Down Expand Up @@ -61,11 +58,8 @@ exports[`Contentheader component should render 1`] = `
</body>,
"container": <div>
<section
class="pf-v5-c-page__main-section pf-m-light pf-v5-u-p-md"
class="pf-v5-c-page__main-section pf-m-light"
>
<div
class="pf-v5-u-mb-md"
/>
<div
class="pf-v5-l-flex"
>
Expand All @@ -82,7 +76,7 @@ exports[`Contentheader component should render 1`] = `
class="pf-v5-c-content"
>
<h1
class=""
class="pf-v5-u-mb-sm"
data-ouia-component-id="ContentHeader-title"
data-ouia-component-type="PF5/Text"
data-ouia-safe="true"
Expand Down