Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/nhsuk frontend v8 new comps #200

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
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
32 changes: 32 additions & 0 deletions docs/upgrade-to-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,35 @@ 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>
```
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { render } from '@testing-library/react';
import CharacterCount, { CharacterCountType } from '../CharacterCount';
import { Label, HintText, Textarea } from '../../../..';

describe('Character Count', () => {
it('Matches snapshot with character count type', () => {
const { container } = render(
<CharacterCount
maxLength={200}
countType={CharacterCountType.Characters}
textAreaId="more-detail"
>
<Label htmlFor="more-detail">Can you provide more detail?</Label>
<HintText id="more-detail-hint">
Do not include personal information like your name, date of birth or NHS number.
</HintText>
<Textarea
id="more-detail"
className="nhsuk-js-character-count"
name="more-detail"
aria-describedby="more-detail-hint"
rows={5}
/>
</CharacterCount>,
);

expect(container).toMatchSnapshot();
});

it('Matches snapshot with word count type', () => {
const { container } = render(
<CharacterCount
maxLength={150}
countType={CharacterCountType.Words}
textAreaId="job-description-detail"
>
<Label htmlFor="job-description-detail" size="l">
Enter a job description
</Label>
<Textarea
id="job-description-detail"
className="nhsuk-js-character-count"
name="job-description-detail"
rows={5}
/>
</CharacterCount>,
);

expect(container).toMatchSnapshot();
});

it('Matches snapshot with threshold percent set', () => {
const { container } = render(
<CharacterCount
maxLength={112}
countType={CharacterCountType.Characters}
textAreaId="threshold"
thresholdPercent={75}
>
<Label htmlFor="threshold">Can you provide more detail?</Label>
<Textarea id="threshold" className="nhsuk-js-character-count" name="threshold" rows={5}>
Type another letter into this field after this message to see the threshold feature
</Textarea>
</CharacterCount>,
);

expect(container).toMatchSnapshot();
});
});
Loading
Loading