Skip to content

Commit

Permalink
New components WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeb-nhs committed Mar 19, 2024
1 parent 03cc471 commit bb8f4e3
Show file tree
Hide file tree
Showing 28 changed files with 994 additions and 83 deletions.
114 changes: 114 additions & 0 deletions docs/upgrade-to-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,117 @@ You can now specify prefixes and/or suffixes for your text inputs. These are exp
```
<TextInput prefix="£" suffix="pounds" />
```

### Exclusive option for checkboxes

Added "None of the above" exclusive behaviour to checkboxes - allowing all checkboxes in a group to be automatically unchecked when the "None of the above" option is checked. To use this feature, a new prop is available on `Checkbox.Box` - set the `exclusive` prop to make that option exclusive, e.g.

```
<Checkboxes id="symptoms" name="symptoms" hint="Select all the symptoms you have.">
<Checkboxes.Box value="sore-throat">Sore throat</Checkboxes.Box>
<Checkboxes.Box value="runny-nose">Runny nose</Checkboxes.Box>
<Checkboxes.Box value="muscle-pain">Muscle or joint pain</Checkboxes.Box>
<Checkboxes.Divider />
<Checkboxes.Box value="none" exclusive>
None
</Checkboxes.Box>
</Checkboxes>
```

### New component - Character Count

See [the Digital Service Manual](https://service-manual.nhs.uk/design-system/components/character-count) for information.
Usage:

```
<CharacterCount
maxLength={150}
countType={CharacterCountType.Characters}
textAreaId="more-details"
>
<Label htmlFor="more-details">Can you provide more detail?</Label>
<Textarea id="more-details" className="nhsuk-js-character-count" name="more-details" rows={5} />
</CharacterCount>
```

### New component - Tabs

See [the Digital Service Manual](https://service-manual.nhs.uk/design-system/components/tabs) for information.
Usage:

```
<Tabs>
<Tabs.Title>Contents</Tabs.Title>
<Tabs.List>
<Tabs.ListItem id="past-day">Past day</Tabs.ListItem>
<Tabs.ListItem id="past-week">Past week</Tabs.ListItem>
<Tabs.ListItem id="past-month">Past month</Tabs.ListItem>
</Tabs.List>
<Tabs.Contents id="past-day">
<div>Past day contents go here</div>
</Tabs.Contents>
<Tabs.Contents id="past-week">
<div>Past week contents go here</div>
</Tabs.Contents>
<Tabs.Contents id="past-month">
<div>Past month contents go here</div>
</Tabs.Contents>
</Tabs>
```

### New card variants

Two new card variants have been added - `primary` and `secondary`.

#### Primary

More information can be found in the [NHS digital service manual](https://service-manual.nhs.uk/design-system/components/card#primary-card-with-chevron)

Usage:

```
<Card clickable primary>
<Card.Content>
<Card.Heading>
<Card.Link href="#">Primary card heading</Card.Link>
</Card.Heading>
<Card.Description>Primary card description</Card.Description>
<ChevronRightCircle />
</Card.Content>
</Card>
```

#### Secondary

More information can be found in the [NHS digital service manual](https://service-manual.nhs.uk/design-system/components/card#secondary-card)

Usage:

```
<Card clickable secondary>
<Card.Content>
<Card.Heading>
<Card.Link href="#">Secondary card heading</Card.Link>
</Card.Heading>
<Card.Description>Secondary card description</Card.Description>
</Card.Content>
</Card>
```

## Notes for maintainers

Ensure you are using an update to date copy of `yarn`

```
# Remove yarn
npm -g remove yarn
# Enable corepack
corepack enable
# Set yarn version
yarn set version stable
```
72 changes: 72 additions & 0 deletions src/components/content-presentation/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import classNames from 'classnames';
import React, { HTMLAttributes, useEffect } from 'react';
import HeadingLevel, { HeadingLevelType } from '../../../util/HeadingLevel';
import TabsJs from 'nhsuk-frontend/packages/components/tabs/tabs.js';

type TabsProps = HTMLAttributes<HTMLDivElement>;

type TabTitleProps = { children: React.ReactNode; headingLevel?: HeadingLevelType };

type TabListProps = {
children: React.ReactNode;
};

type TabListItemProps = {
id: string;
children: React.ReactNode;
};

type TabContentsProps = {
id: string;
children: React.ReactNode;
};

const TabTitle: React.FC<TabTitleProps> = ({ children, headingLevel = 'h2' }) => (
<HeadingLevel className="nhsuk-tabs__title" headingLevel={headingLevel}>
{children}
</HeadingLevel>
);

const TabList: React.FC<TabListProps> = ({ children }) => (
<ul className="nhsuk-tabs__list">{children}</ul>
);

const TabListItem: React.FC<TabListItemProps> = ({ id, children }) => (
<li className="nhsuk-tabs__list-item">
<a className="nhsuk-tabs__tab" href={`#${id}`}>
{children}
</a>
</li>
);

const TabContents: React.FC<TabContentsProps> = ({ id, children }) => (
<div className="nhsuk-tabs__panel" id={id}>
{children}
</div>
);

interface Tabs extends React.FC<TabsProps> {
Title: React.FC<TabTitleProps>;
List: React.FC<TabListProps>;
ListItem: React.FC<TabListItemProps>;
Contents: React.FC<TabContentsProps>;
}

const Tabs: Tabs = ({ className, children, ...rest }) => {
useEffect(() => {
TabsJs();
}, []);

return (
<div className={classNames('nhsuk-tabs', className)} data-module="nhsuk-tabs" {...rest}>
{children}
</div>
);
};

Tabs.Title = TabTitle;
Tabs.List = TabList;
Tabs.ListItem = TabListItem;
Tabs.Contents = TabContents;

export default Tabs;
32 changes: 32 additions & 0 deletions src/components/content-presentation/tabs/__tests__/Tabs.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { render } from '@testing-library/react';
import Tabs from '../Tabs';

describe('The tabs component', () => {
it('Matches the snapshot', () => {
const { container } = render(
<Tabs>
<Tabs.Title>Contents</Tabs.Title>
<Tabs.List>
<Tabs.ListItem id="past-day">Past day</Tabs.ListItem>
<Tabs.ListItem id="past-week">Past week</Tabs.ListItem>
<Tabs.ListItem id="past-month">Past month</Tabs.ListItem>
</Tabs.List>

<Tabs.Contents id="past-day">
<div>Past day contents go here</div>
</Tabs.Contents>

<Tabs.Contents id="past-week">
<div>Past week contents go here</div>
</Tabs.Contents>

<Tabs.Contents id="past-month">
<div>Past month contents go here</div>
</Tabs.Contents>
</Tabs>,
);

expect(container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`The tabs component Matches the snapshot 1`] = `
<div>
<div
class="nhsuk-tabs"
data-module="nhsuk-tabs"
>
<h2
class="nhsuk-tabs__title"
>
Contents
</h2>
<ul
class="nhsuk-tabs__list"
role="tablist"
>
<li
class="nhsuk-tabs__list-item nhsuk-tabs__list-item--selected"
role="presentation"
>
<a
aria-controls="past-day"
aria-selected="true"
class="nhsuk-tabs__tab"
href="#past-day"
id="tab_past-day"
role="tab"
tabindex="0"
>
Past day
</a>
</li>
<li
class="nhsuk-tabs__list-item"
role="presentation"
>
<a
aria-controls="past-week"
aria-selected="false"
class="nhsuk-tabs__tab"
href="#past-week"
id="tab_past-week"
role="tab"
tabindex="-1"
>
Past week
</a>
</li>
<li
class="nhsuk-tabs__list-item"
role="presentation"
>
<a
aria-controls="past-month"
aria-selected="false"
class="nhsuk-tabs__tab"
href="#past-month"
id="tab_past-month"
role="tab"
tabindex="-1"
>
Past month
</a>
</li>
</ul>
<div
aria-labelledby="tab_past-day"
class="nhsuk-tabs__panel"
id="past-day"
role="tabpanel"
>
<div>
Past day contents go here
</div>
</div>
<div
aria-labelledby="tab_past-week"
class="nhsuk-tabs__panel nhsuk-tabs__panel--hidden"
id="past-week"
role="tabpanel"
>
<div>
Past week contents go here
</div>
</div>
<div
aria-labelledby="tab_past-month"
class="nhsuk-tabs__panel nhsuk-tabs__panel--hidden"
id="past-month"
role="tabpanel"
>
<div>
Past month contents go here
</div>
</div>
</div>
</div>
`;
3 changes: 3 additions & 0 deletions src/components/content-presentation/tabs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Tabs from './Tabs';

export default Tabs;
54 changes: 54 additions & 0 deletions src/components/form-elements/character-count/CharacterCount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { useEffect } from 'react';
import CharacterCountJs from 'nhsuk-frontend/packages/components/character-count/character-count.js';
import { HTMLAttributesWithData } from '../../../util/types/NHSUKTypes';

export enum CharacterCountType {
Characters,
Words,
}

type CharacterCountProps = React.HTMLAttributes<HTMLDivElement> & {
children: React.ReactNode;
maxLength: number;
countType: CharacterCountType;
textAreaId: string;
thresholdPercent?: number;
};

const CharacterCount: React.FC<CharacterCountProps> = ({
children,
maxLength,
countType,
textAreaId,
thresholdPercent,
...rest
}) => {
useEffect(() => {
CharacterCountJs();
}, []);

const characterCountProps: HTMLAttributesWithData<HTMLDivElement> =
countType === CharacterCountType.Characters
? { ...rest, ['data-maxlength']: maxLength }
: { ...rest, ['data-maxwords']: maxLength };

if (thresholdPercent) {
characterCountProps['data-threshold'] = thresholdPercent;
}

return (
<div
className="nhsuk-character-count"
data-module="nhsuk-character-count"
{...characterCountProps}
>
<div className="nhsuk-form-group">{children}</div>

<div className="nhsuk-hint nhsuk-character-count__message" id={`${textAreaId}-info`}>
You can enter up to {maxLength} characters
</div>
</div>
);
};

export default CharacterCount;
Loading

0 comments on commit bb8f4e3

Please sign in to comment.