Skip to content

Commit 4becb36

Browse files
authored
Merge pull request #76 from NHSDigital/feature/newCard
NHS.UK 4 - Add New Card Component
2 parents 025f45b + 2ffd2e0 commit 4becb36

File tree

19 files changed

+693
-22
lines changed

19 files changed

+693
-22
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# Unreleased Changes
2+
## New Card Component
3+
4+
The new Card component from `nhsuk-frontend` 4 has been added. Check out the storybook for usage examples!
25

36
## WarningCallout
47

@@ -42,5 +45,3 @@ The `WarningCallout.Label` now has the hidden text `Important: ` before the labe
4245
Something Much Less Important
4346
</WarningCallout.Label>
4447
```
45-
46-

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"eslint-plugin-react-hooks": "^1.7.0",
4747
"jest": "^26.6.1",
4848
"jest-axe": "^3.4.0",
49-
"nhsuk-frontend": "^3.1.0",
49+
"nhsuk-frontend": "^4.1.0",
5050
"node-sass": "^4.14.1",
5151
"prettier": "^1.18.2",
5252
"react": "^16.9.3",
@@ -65,7 +65,7 @@
6565
"typescript": "^3.9.5"
6666
},
6767
"peerDependencies": {
68-
"nhsuk-frontend": "^3.0.0",
68+
"nhsuk-frontend": ">=4.0.0",
6969
"react": "^16.9.0",
7070
"react-dom": "^16.9.0"
7171
},

src/__tests__/index.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('Index', () => {
99
'Button',
1010
'DefaultButton',
1111
'ButtonLink',
12+
'Card',
1213
'CareCard',
1314
'Checkboxes',
1415
'ContentsList',

src/components/action-link/ActionLink.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React from 'react';
22
import classNames from 'classnames';
33
import { ArrowRightCircle } from '../icons';
4-
import type { AsElementLink } from 'util/types/LinkTypes';
4+
import type { AsElementLink } from '../../util/types/LinkTypes';
55

6-
const ActionLink: React.FC<AsElementLink<HTMLAnchorElement>> = ({
7-
children,
8-
asElement: Component = 'a',
9-
className,
10-
...rest
6+
const ActionLink: React.FC<AsElementLink<HTMLAnchorElement>> = ({
7+
children,
8+
asElement: Component = 'a',
9+
className,
10+
...rest
1111
}) => (
1212
<div className="nhsuk-action-link">
1313
<Component className={classNames('nhsuk-action-link__link', className)} {...rest}>

src/components/card/Card.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { HTMLProps } from 'react';
2+
import classNames from 'classnames';
3+
import CardContext from './CardContext';
4+
import CardContent from './components/CardContent';
5+
import CardDescription from './components/CardDescription';
6+
import CardImage from './components/CardImage';
7+
import CardLink from './components/CardLink';
8+
import CardHeading from './components/CardHeading';
9+
import CardGroup from './components/CardGroup';
10+
import CardGroupItem from './components/CardGroupItem';
11+
12+
interface CardProps extends HTMLProps<HTMLDivElement> {
13+
clickable?: boolean;
14+
feature?: boolean;
15+
}
16+
17+
interface ICard extends React.FC<CardProps> {
18+
Content: typeof CardContent;
19+
Description: typeof CardDescription;
20+
Image: typeof CardImage;
21+
Link: typeof CardLink;
22+
Heading: typeof CardHeading;
23+
Group: typeof CardGroup;
24+
GroupItem: typeof CardGroupItem;
25+
}
26+
27+
const Card: ICard = ({ className, clickable, children, feature, ...rest }) => (
28+
<div
29+
className={classNames(
30+
'nhsuk-card',
31+
{ 'nhsuk-card--clickable': clickable },
32+
{ 'nhsuk-card--feature': feature },
33+
className,
34+
)}
35+
{...rest}
36+
>
37+
<CardContext.Provider value={{ feature: Boolean(feature) }}>{children}</CardContext.Provider>
38+
</div>
39+
);
40+
41+
Card.defaultProps = {
42+
feature: false,
43+
};
44+
45+
Card.Heading = CardHeading;
46+
Card.Description = CardDescription;
47+
Card.Image = CardImage;
48+
Card.Link = CardLink;
49+
Card.Content = CardContent;
50+
Card.Group = CardGroup;
51+
Card.GroupItem = CardGroupItem;
52+
53+
export default Card;

src/components/card/CardContext.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React from 'react';
2+
3+
export interface ICardContext {
4+
feature: boolean;
5+
}
6+
7+
const CardContext = React.createContext<ICardContext>({
8+
feature: false,
9+
});
10+
11+
export default CardContext;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { mount } from 'enzyme';
2+
import React from 'react';
3+
import Card from '../Card';
4+
5+
describe('Card', () => {
6+
it('matches snapshot', () => {
7+
const wrapper = mount(
8+
<Card>
9+
<Card.Image src="imageSrc" alt="imageAlt" />
10+
<Card.Content>
11+
<Card.Heading>If you need help now but it&apos;s not an emergency</Card.Heading>
12+
<Card.Description>
13+
Go to <a href="#">111.nhs.uk</a> or <a href="#">call 111</a>
14+
</Card.Description>
15+
</Card.Content>
16+
</Card>,
17+
);
18+
19+
expect(wrapper).toMatchSnapshot();
20+
21+
wrapper.unmount();
22+
});
23+
24+
it('can render Card.Link as different elements', () => {
25+
const wrapper = mount(
26+
<Card clickable>
27+
<Card.Content>
28+
<Card.Heading>
29+
<Card.Link asElement="button" type="button">
30+
Click me!
31+
</Card.Link>
32+
</Card.Heading>
33+
</Card.Content>
34+
</Card>,
35+
);
36+
expect(
37+
wrapper.find(Card.Link).containsMatchingElement(
38+
<button type="button" className="nhsuk-card__link">
39+
Click me!
40+
</button>,
41+
),
42+
).toBeTruthy();
43+
});
44+
45+
it('adds clickable classes', () => {
46+
const wrapper = mount(
47+
<Card clickable>
48+
<Card.Content>
49+
<Card.Heading className="nhsuk-heading-m">
50+
<Card.Link href="#">Introduction to care and support</Card.Link>
51+
</Card.Heading>
52+
<Card.Description>
53+
A quick guide for people who have care and support needs and their carers
54+
</Card.Description>
55+
</Card.Content>
56+
</Card>,
57+
);
58+
59+
expect(wrapper.find('div.nhsuk-card').props().className).toBe(
60+
'nhsuk-card nhsuk-card--clickable',
61+
);
62+
63+
wrapper.unmount();
64+
});
65+
66+
it('adds feature classes to all elements', () => {
67+
const wrapper = mount(
68+
<Card feature>
69+
<Card.Content>
70+
<Card.Heading>Feature card heading</Card.Heading>
71+
<Card.Description>Feature card description</Card.Description>
72+
</Card.Content>
73+
</Card>,
74+
);
75+
76+
expect(wrapper.find('div.nhsuk-card').props().className).toBe('nhsuk-card nhsuk-card--feature');
77+
expect(wrapper.find('div.nhsuk-card__content').props().className).toBe(
78+
'nhsuk-card__content nhsuk-card__content--feature',
79+
);
80+
expect(wrapper.find('h2.nhsuk-card__heading').props().className).toBe(
81+
'nhsuk-card__heading nhsuk-card__heading--feature',
82+
);
83+
84+
wrapper.unmount();
85+
});
86+
87+
describe('Card.Group', () => {
88+
it('matches snapshot', () => {
89+
const wrapper = mount(
90+
<Card.Group>
91+
<Card.GroupItem width="one-half">
92+
<Card>
93+
<Card.Content>
94+
<Card.Heading>Test Card 1</Card.Heading>
95+
<Card.Description>Test Card 1 Description</Card.Description>
96+
</Card.Content>
97+
</Card>
98+
</Card.GroupItem>
99+
<Card.GroupItem width="one-half">
100+
<Card>
101+
<Card.Content>
102+
<Card.Heading>Test Card 2</Card.Heading>
103+
<Card.Description>Test Card 2 Description</Card.Description>
104+
</Card.Content>
105+
</Card>
106+
</Card.GroupItem>
107+
</Card.Group>,
108+
);
109+
110+
expect(wrapper).toMatchSnapshot();
111+
112+
wrapper.unmount();
113+
});
114+
});
115+
});

0 commit comments

Comments
 (0)