Skip to content

Commit f1bdcc7

Browse files
authored
fix(ServiceCard): Add horizontal layout to the ServiceCard (#260)
* Add horizontal layout to the ServiceCard * fix(ServiceCard): Update tests * fix(ServiceCard): Support isFullHeight
1 parent c4f7314 commit f1bdcc7

File tree

7 files changed

+143
-75
lines changed

7 files changed

+143
-75
lines changed

packages/module/patternfly-docs/content/extensions/component-groups/examples/ServiceCard/ServiceCard.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,25 @@ The **service card** component displays a card representing a service with an ic
1515

1616
## Examples
1717

18-
### Service Card
18+
### Basic service card
1919

20-
This shows a basic service card with an `icon`, `title`, `description`, and optional footer passed in.
20+
This shows a basic service card with an `icon`, `title`, `description`, and optional footer passed in. You can also pass all props of the [card component](/components/card).
2121

2222
```js file="./ServiceCardExample.tsx"
2323

2424
```
2525

26-
### Service Card with Gallery example
26+
### Stacked service card
2727

28-
This shows how cards can look side by side in a [gallery layout](/layouts/gallery).
28+
If you set `isStacked` property to `true`, the header layout changes to stacked.
29+
30+
```js file="./ServiceCardStackedExample.tsx"
31+
32+
```
33+
34+
### Service card in a gallery
35+
36+
This shows how cards can look side by side in a [gallery layout](/layouts/gallery). If you set `isFullHeight` property to `true`, the card height will fill the available space.
2937

3038
```js file="./ServiceCardGalleryExample.tsx"
3139

packages/module/patternfly-docs/content/extensions/component-groups/examples/ServiceCard/ServiceCardExample.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ export const BasicExample: React.FunctionComponent = () => (
1313
helperText='Here is helper text'
1414
footer={<>
1515
<Button
16-
variant={ButtonVariant.secondary}
16+
variant={ButtonVariant.primary}
1717
isInline
18-
className='pf-v5-u-pr-md'
18+
className='pf-v5-u-mr-md'
1919
component="a"
2020
href='www.google.com'>
2121
Launch
@@ -26,7 +26,7 @@ export const BasicExample: React.FunctionComponent = () => (
2626
isInline
2727
href='www.google.com'
2828
>
29-
Learn More
29+
Learn more
3030
</Button></>
3131
}
3232
/>

packages/module/patternfly-docs/content/extensions/component-groups/examples/ServiceCard/ServiceCardGalleryExample.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import contentHeaderIcon from '../../assets/icons/content-header-icon.svg';
88
export const ServiceCardGalleryExample: React.FunctionComponent = () => (
99
<Gallery hasGutter minWidths={{ default: '330px' }}>
1010
<GalleryItem>
11-
<ServiceCard
11+
<ServiceCard
12+
isStacked
13+
isFullHeight
1214
title='Example1'
1315
subtitle='A basic example'
1416
description='This is a basic ServiceCard Example'
@@ -18,16 +20,19 @@ export const ServiceCardGalleryExample: React.FunctionComponent = () => (
1820
/>
1921
</GalleryItem>
2022
<GalleryItem>
21-
<ServiceCard
23+
<ServiceCard
24+
isStacked
25+
isFullHeight
2226
title='Example2'
2327
subtitle='A second example'
2428
description='This is another basic ServiceCard Example'
2529
icon={<img src={contentHeaderIcon} alt="content-header-icon" />}
2630
helperText=''
2731
footer={<>
2832
<Button
29-
variant={ButtonVariant.secondary}
33+
variant={ButtonVariant.primary}
3034
isInline
35+
className='pf-v5-u-mr-md'
3136
component="a"
3237
href='www.google.com'>
3338
Launch
@@ -38,7 +43,7 @@ export const ServiceCardGalleryExample: React.FunctionComponent = () => (
3843
isInline
3944
href='www.google.com'
4045
>
41-
Learn More
46+
Learn more
4247
</Button></>
4348
}
4449
/>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import ServiceCard from "@patternfly/react-component-groups/dist/dynamic/ServiceCard";
3+
import contentHeaderIcon from '../../assets/icons/content-header-icon.svg';
4+
import { Button, ButtonVariant } from '@patternfly/react-core';
5+
6+
7+
export const BasicExample: React.FunctionComponent = () => (
8+
<ServiceCard
9+
isStacked
10+
title='Example'
11+
subtitle='A basic example'
12+
description='This is a basic ServiceCard Example'
13+
icon={<img src={contentHeaderIcon} alt="content-header-icon" />}
14+
helperText='Here is helper text'
15+
footer={<>
16+
<Button
17+
variant={ButtonVariant.primary}
18+
isInline
19+
className='pf-v5-u-mr-md'
20+
component="a"
21+
href='www.google.com'>
22+
Launch
23+
</Button>
24+
<Button
25+
variant={ButtonVariant.link}
26+
component="a"
27+
isInline
28+
href='www.google.com'
29+
>
30+
Learn more
31+
</Button></>
32+
}
33+
/>
34+
);

packages/module/src/ServiceCard/ServiceCard.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import React from 'react';
22
import { render } from '@testing-library/react';
33
import ServiceCard from './ServiceCard';
44

5-
describe('LogSnippet component', () => {
6-
it('should render LogSnippet component', () => {
5+
describe('ServiceCard component', () => {
6+
it('should render ServiceCard component', () => {
77
expect(render(<ServiceCard
88
title='Example'
99
subtitle='A basic example'

packages/module/src/ServiceCard/ServiceCard.tsx

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React from 'react';
2-
import { Card, CardBody, CardFooter, CardHeader, Text, TextContent, TextVariants } from '@patternfly/react-core';
2+
import { Card, CardBody, CardFooter, CardHeader, Text, TextContent, TextVariants, Flex, FlexItem, CardProps } from '@patternfly/react-core';
33
import { HelperText } from '@patternfly/react-core/dist/dynamic/components/HelperText';
44
import { HelperTextItem } from '@patternfly/react-core/dist/dynamic/components/HelperText';
55
import { createUseStyles } from 'react-jss';
6+
import clsx from 'clsx';
67

7-
export interface ServiceCardProps {
8+
export interface ServiceCardProps extends CardProps {
89
/** Service card title */
910
title: string;
1011
/** Service card subtitle */
@@ -19,11 +20,15 @@ export interface ServiceCardProps {
1920
footer?: React.ReactElement | null;
2021
/** Optional custom OUIA ID */
2122
ouiaId?: string | number;
23+
/** Optional flag modifying the card header layout */
24+
isStacked?: boolean;
25+
/** Optional flag indicating if the card height fills the available space */
26+
isFullHeight?: boolean;
2227
}
2328

2429
const useStyles = createUseStyles({
25-
card: {
26-
height: 'var(--pf-v5-u-h-100)'
30+
fullHeightCard: {
31+
height: '100%'
2732
},
2833
image: {
2934
marginRight: 'var(--pf-v5-global--spacer--md)',
@@ -38,32 +43,40 @@ const ServiceCard: React.FunctionComponent<ServiceCardProps> = ({
3843
icon,
3944
helperText,
4045
footer = null,
41-
ouiaId='ServiceCard'
46+
ouiaId='ServiceCard',
47+
isStacked = false,
48+
isFullHeight = false,
49+
...props
4250
}: ServiceCardProps) => {
4351
const classes = useStyles();
4452

4553
return (
46-
<Card className={classes.card} ouiaId={`${ouiaId}-card`}>
54+
<Card className={clsx({ [classes.fullHeightCard]: isFullHeight })} ouiaId={`${ouiaId}-card`} {...props}>
4755
<CardHeader>
48-
<div className={classes.image}>
49-
{icon}
50-
</div>
51-
<TextContent>
52-
<Text component={TextVariants.h2} ouiaId={`${ouiaId}-title`}>{title}</Text>
53-
{subtitle}
54-
</TextContent>
56+
<Flex direction={{ default: isStacked ? 'column' : 'row' }} alignItems={{ default: isStacked ? 'alignItemsFlexStart' : 'alignItemsCenter' }}>
57+
<FlexItem className={classes.image}>
58+
{icon}
59+
</FlexItem>
60+
<FlexItem>
61+
<TextContent>
62+
<Text component={TextVariants.h2} ouiaId={`${ouiaId}-title`}>{title}</Text>
63+
{subtitle}
64+
</TextContent>
65+
</FlexItem>
66+
</Flex>
5567
</CardHeader>
5668
<CardBody data-ouia-component-id={`${ouiaId}-description`}>{description}</CardBody>
57-
<CardFooter data-ouia-component-id={`${ouiaId}-footer`}>
58-
{ helperText ?
59-
( <HelperText data-ouia-component-id={`${ouiaId}-helper-text`}>
60-
<HelperTextItem variant="indeterminate" className="pf-v5-u-mb-lg">
61-
{helperText}
62-
</HelperTextItem>
63-
</HelperText>) : null
64-
}
65-
{ footer }
66-
</CardFooter>
69+
{ footer || helperText ? (
70+
<CardFooter data-ouia-component-id={`${ouiaId}-footer`}>
71+
{ helperText ?
72+
( <HelperText data-ouia-component-id={`${ouiaId}-helper-text`}>
73+
<HelperTextItem variant="indeterminate" className="pf-v5-u-mb-lg">
74+
{helperText}
75+
</HelperTextItem>
76+
</HelperText>) : null
77+
}
78+
{ footer }
79+
</CardFooter>) : null}
6780
</Card>
6881
)
6982
}

packages/module/src/ServiceCard/__snapshots__/ServiceCard.test.tsx.snap

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`LogSnippet component should render LogSnippet component 1`] = `
3+
exports[`ServiceCard component should render ServiceCard component 1`] = `
44
{
55
"asFragment": [Function],
66
"baseElement": <body>
77
<div>
88
<div
9-
class="pf-v5-c-card card-0-2-1"
9+
class="pf-v5-c-card"
1010
data-ouia-component-id="ServiceCard-card"
1111
data-ouia-component-type="PF5/Card"
1212
data-ouia-safe="true"
@@ -19,23 +19,31 @@ exports[`LogSnippet component should render LogSnippet component 1`] = `
1919
class="pf-v5-c-card__header-main"
2020
>
2121
<div
22-
class="image-0-2-2"
23-
>
24-
/
25-
</div>
26-
<div
27-
class="pf-v5-c-content"
22+
class="pf-v5-l-flex pf-m-row pf-m-align-items-center"
2823
>
29-
<h2
24+
<div
25+
class="image-0-2-2"
26+
>
27+
/
28+
</div>
29+
<div
3030
class=""
31-
data-ouia-component-id="ServiceCard-title"
32-
data-ouia-component-type="PF5/Text"
33-
data-ouia-safe="true"
34-
data-pf-content="true"
3531
>
36-
Example
37-
</h2>
38-
A basic example
32+
<div
33+
class="pf-v5-c-content"
34+
>
35+
<h2
36+
class=""
37+
data-ouia-component-id="ServiceCard-title"
38+
data-ouia-component-type="PF5/Text"
39+
data-ouia-safe="true"
40+
data-pf-content="true"
41+
>
42+
Example
43+
</h2>
44+
A basic example
45+
</div>
46+
</div>
3947
</div>
4048
</div>
4149
</div>
@@ -45,16 +53,12 @@ exports[`LogSnippet component should render LogSnippet component 1`] = `
4553
>
4654
This is a basic ServiceCard Example
4755
</div>
48-
<div
49-
class="pf-v5-c-card__footer"
50-
data-ouia-component-id="ServiceCard-footer"
51-
/>
5256
</div>
5357
</div>
5458
</body>,
5559
"container": <div>
5660
<div
57-
class="pf-v5-c-card card-0-2-1"
61+
class="pf-v5-c-card"
5862
data-ouia-component-id="ServiceCard-card"
5963
data-ouia-component-type="PF5/Card"
6064
data-ouia-safe="true"
@@ -67,23 +71,31 @@ exports[`LogSnippet component should render LogSnippet component 1`] = `
6771
class="pf-v5-c-card__header-main"
6872
>
6973
<div
70-
class="image-0-2-2"
71-
>
72-
/
73-
</div>
74-
<div
75-
class="pf-v5-c-content"
74+
class="pf-v5-l-flex pf-m-row pf-m-align-items-center"
7675
>
77-
<h2
76+
<div
77+
class="image-0-2-2"
78+
>
79+
/
80+
</div>
81+
<div
7882
class=""
79-
data-ouia-component-id="ServiceCard-title"
80-
data-ouia-component-type="PF5/Text"
81-
data-ouia-safe="true"
82-
data-pf-content="true"
8383
>
84-
Example
85-
</h2>
86-
A basic example
84+
<div
85+
class="pf-v5-c-content"
86+
>
87+
<h2
88+
class=""
89+
data-ouia-component-id="ServiceCard-title"
90+
data-ouia-component-type="PF5/Text"
91+
data-ouia-safe="true"
92+
data-pf-content="true"
93+
>
94+
Example
95+
</h2>
96+
A basic example
97+
</div>
98+
</div>
8799
</div>
88100
</div>
89101
</div>
@@ -93,10 +105,6 @@ exports[`LogSnippet component should render LogSnippet component 1`] = `
93105
>
94106
This is a basic ServiceCard Example
95107
</div>
96-
<div
97-
class="pf-v5-c-card__footer"
98-
data-ouia-component-id="ServiceCard-footer"
99-
/>
100108
</div>
101109
</div>,
102110
"debug": [Function],

0 commit comments

Comments
 (0)