Skip to content

Commit 2c3404e

Browse files
author
Eric Olkowski
committed
feat(Banner): add support for status icons
1 parent 44af730 commit 2c3404e

File tree

6 files changed

+153
-59
lines changed

6 files changed

+153
-59
lines changed
Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,68 @@
11
import * as React from 'react';
22
import styles from '@patternfly/react-styles/css/components/Banner/banner';
33
import { css } from '@patternfly/react-styles';
4+
import { variantIcons } from '../Alert/AlertIcon';
5+
import { Flex, FlexItem } from '../../layouts';
46

57
export interface BannerProps extends React.HTMLProps<HTMLDivElement> {
6-
/** Content rendered inside the banner */
8+
/** Content rendered inside the banner. */
79
children?: React.ReactNode;
8-
/** Additional classes added to the banner */
10+
/** Additional classes added to the banner. */
911
className?: string;
10-
/** Variant styles for the banner */
11-
variant?: 'default' | 'info' | 'danger' | 'success' | 'warning';
12+
/** A custom icon for the banner. This property will override the icon that is set based on
13+
* the variant property.
14+
*/
15+
customIcon?: React.ReactNode;
16+
/** Flag for indicating whether the banner has a status icon. When set to "true", the icon
17+
* will be set based on the variant property.
18+
*/
19+
hasStatusIcon?: boolean;
1220
/** If set to true, the banner sticks to the top of its container */
1321
isSticky?: boolean;
14-
/** Text announced by screen readers to indicate the type of banner. Defaults to "${variant} banner" if this prop is not passed in */
22+
/** Text announced by screen readers to indicate the type of banner when the hasStatusIcon property
23+
* is passed in. Defaults to "${variant} banner" if this property is not passed in.
24+
*/
1525
screenReaderText?: string;
26+
/** Variant styles for the banner. */
27+
variant?: 'default' | 'info' | 'danger' | 'success' | 'warning';
1628
}
1729

1830
export const Banner: React.FunctionComponent<BannerProps> = ({
1931
children,
2032
className,
33+
customIcon,
34+
hasStatusIcon = false,
2135
variant = 'default',
2236
screenReaderText,
2337
isSticky = false,
2438
...props
25-
}: BannerProps) => (
26-
<div
27-
className={css(
28-
styles.banner,
29-
styles.modifiers[variant as 'success' | 'danger' | 'warning' | 'info'],
30-
isSticky && styles.modifiers.sticky,
31-
className
32-
)}
33-
{...props}
34-
>
35-
{children}
36-
<span className="pf-u-screen-reader">{screenReaderText || `${variant} banner`}</span>
37-
</div>
38-
);
39+
}: BannerProps) => {
40+
const StatusIcon = variantIcons[variant];
41+
42+
return (
43+
<div
44+
className={css(
45+
styles.banner,
46+
styles.modifiers[variant as 'success' | 'danger' | 'warning' | 'info'],
47+
isSticky && styles.modifiers.sticky,
48+
className
49+
)}
50+
{...props}
51+
>
52+
{hasStatusIcon ? (
53+
<Flex spaceItems={{ default: 'spaceItemsSm' }}>
54+
<FlexItem>
55+
<span className="pf-u-screen-reader">{screenReaderText || `${variant} banner`}</span>
56+
{customIcon || <StatusIcon />}
57+
</FlexItem>
58+
<FlexItem>
59+
<div className="pf-l-flex__item">{children}</div>
60+
</FlexItem>
61+
</Flex>
62+
) : (
63+
children
64+
)}
65+
</div>
66+
);
67+
};
3968
Banner.displayName = 'Banner';

packages/react-core/src/components/Banner/__tests__/Banner.test.tsx

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import { Banner } from '../Banner';
22
import React from 'react';
3-
import { render } from '@testing-library/react';
3+
import { render, screen } from '@testing-library/react';
4+
5+
jest.mock('@patternfly/react-icons/dist/esm/icons/check-circle-icon', () => () => 'Check circle icon mock');
6+
jest.mock('@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon', () => () => 'Exclamation circle icon mock');
7+
jest.mock('@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon', () => () =>
8+
'Exclamation triangle icon mock'
9+
);
10+
jest.mock('@patternfly/react-icons/dist/esm/icons/info-circle-icon', () => () => 'Info circle icon mock');
11+
jest.mock('@patternfly/react-icons/dist/esm/icons/bell-icon', () => () => 'Bell icon mock');
412

513
['default', 'info', 'success', 'warning', 'danger'].forEach((variant: string) => {
614
test(`${variant} banner`, () => {
@@ -25,3 +33,59 @@ test(`sticky banner`, () => {
2533
);
2634
expect(asFragment()).toMatchSnapshot();
2735
});
36+
37+
test('Renders with the bell icon when variant is not passed and hasStatusIcon is passed', () => {
38+
render(<Banner hasStatusIcon>Default banner</Banner>);
39+
40+
expect(screen.getByText('Bell icon mock')).toBeVisible();
41+
});
42+
43+
test('Renders with the bell icon when variant = "default" and hasStatusIcon is passed', () => {
44+
render(
45+
<Banner variant="default" hasStatusIcon>
46+
Default banner
47+
</Banner>
48+
);
49+
50+
expect(screen.getByText('Bell icon mock')).toBeVisible();
51+
});
52+
53+
test('Renders with the info circle icon when variant = "info" and hasStatusIcon is passed', () => {
54+
render(
55+
<Banner variant="info" hasStatusIcon>
56+
Info banner
57+
</Banner>
58+
);
59+
60+
expect(screen.getByText('Info circle icon mock')).toBeVisible();
61+
});
62+
63+
test('Renders with the exclamation circle icon when variant = "danger" and hasStatusIcon is passed', () => {
64+
render(
65+
<Banner variant="danger" hasStatusIcon>
66+
Danger banner
67+
</Banner>
68+
);
69+
70+
expect(screen.getByText('Exclamation circle icon mock')).toBeVisible();
71+
});
72+
73+
test('Renders with the check circle icon when variant = "success" and hasStatusIcon is passed', () => {
74+
render(
75+
<Banner variant="success" hasStatusIcon>
76+
Success banner
77+
</Banner>
78+
);
79+
80+
expect(screen.getByText('Check circle icon mock')).toBeVisible();
81+
});
82+
83+
test('Renders with the exclamation triangle icon when variant = "warning" and hasStatusIcon is passed', () => {
84+
render(
85+
<Banner variant="warning" hasStatusIcon>
86+
Warning banner
87+
</Banner>
88+
);
89+
90+
expect(screen.getByText('Exclamation triangle icon mock')).toBeVisible();
91+
});

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ exports[`danger banner 1`] = `
77
class="pf-c-banner pf-m-danger"
88
>
99
danger Banner
10-
<span
11-
class="pf-u-screen-reader"
12-
>
13-
danger banner
14-
</span>
1510
</div>
1611
</DocumentFragment>
1712
`;
@@ -23,11 +18,6 @@ exports[`default banner 1`] = `
2318
class="pf-c-banner"
2419
>
2520
default Banner
26-
<span
27-
class="pf-u-screen-reader"
28-
>
29-
default banner
30-
</span>
3121
</div>
3222
</DocumentFragment>
3323
`;
@@ -39,11 +29,6 @@ exports[`info banner 1`] = `
3929
class="pf-c-banner pf-m-info"
4030
>
4131
info Banner
42-
<span
43-
class="pf-u-screen-reader"
44-
>
45-
info banner
46-
</span>
4732
</div>
4833
</DocumentFragment>
4934
`;
@@ -55,11 +40,6 @@ exports[`sticky banner 1`] = `
5540
class="pf-c-banner pf-m-sticky"
5641
>
5742
Sticky Banner
58-
<span
59-
class="pf-u-screen-reader"
60-
>
61-
default banner
62-
</span>
6343
</div>
6444
</DocumentFragment>
6545
`;
@@ -71,11 +51,6 @@ exports[`success banner 1`] = `
7151
class="pf-c-banner pf-m-success"
7252
>
7353
success Banner
74-
<span
75-
class="pf-u-screen-reader"
76-
>
77-
success banner
78-
</span>
7954
</div>
8055
</DocumentFragment>
8156
`;
@@ -87,11 +62,6 @@ exports[`warning banner 1`] = `
8762
class="pf-c-banner pf-m-warning"
8863
>
8964
warning Banner
90-
<span
91-
class="pf-u-screen-reader"
92-
>
93-
warning banner
94-
</span>
9565
</div>
9666
</DocumentFragment>
9767
`;

packages/react-core/src/components/Banner/examples/Banner.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,14 @@ propComponents: ['Banner']
99

1010
### Basic
1111

12+
Banners can be styled with one of 5 different colors. A basic banner should only be used when the banner color does not represent status or severity.
13+
1214
```ts file="./BannerBasic.tsx"
1315
```
16+
17+
### Status
18+
19+
When a banner is used to convey status, it is advised to pass in either the `hasStatusIcon` or `customIcon` property to render an icon inside the banner. This icon should convey the banner variant beyond just the banner color.
20+
21+
```ts file="./BannerStatus.tsx"
22+
```

packages/react-core/src/components/Banner/examples/BannerBasic.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ import React from 'react';
22
import { Banner } from '@patternfly/react-core';
33

44
export const BannerBasic: React.FunctionComponent = () => (
5-
<React.Fragment>
6-
<Banner screenReaderText="This is a default Banner">Default banner</Banner>
5+
<>
6+
<Banner>Default banner</Banner>
77
<br />
8-
<Banner variant="info" screenReaderText="This is an info Banner">
9-
Info banner
10-
</Banner>
8+
<Banner variant="info">Blue banner</Banner>
119
<br />
12-
<Banner variant="danger">Danger banner</Banner>
10+
<Banner variant="danger">Red banner</Banner>
1311
<br />
14-
<Banner variant="success">Success banner</Banner>
12+
<Banner variant="success">Green banner</Banner>
1513
<br />
16-
<Banner variant="warning">Warning banner</Banner>
17-
</React.Fragment>
14+
<Banner variant="warning">Gold banner</Banner>
15+
</>
1816
);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { Banner } from '@patternfly/react-core';
3+
4+
export const BannerStatus: React.FunctionComponent = () => (
5+
<>
6+
<Banner hasStatusIcon>Default banner</Banner>
7+
<br />
8+
<Banner hasStatusIcon variant="info">
9+
Info banner
10+
</Banner>
11+
<br />
12+
<Banner hasStatusIcon variant="danger">
13+
Danger banner
14+
</Banner>
15+
<br />
16+
<Banner hasStatusIcon variant="success">
17+
Success banner
18+
</Banner>
19+
<br />
20+
<Banner hasStatusIcon variant="warning">
21+
Warning banner
22+
</Banner>
23+
</>
24+
);

0 commit comments

Comments
 (0)