diff --git a/.storybook/main.ts b/.storybook/main.ts index 5de8735a..6f8dca60 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -4,13 +4,15 @@ import { type InlineConfig, mergeConfig } from 'vite'; import tsConfigPaths from 'vite-tsconfig-paths'; const config: StorybookConfig = { - stories: ['../stories/**/*.stories.@(ts|tsx)', '../stories/**/*.mdx'], addons: ['@storybook/addon-links', '@storybook/addon-docs'], framework: { name: '@storybook/react-vite', options: {}, }, + staticDirs: ['../node_modules/nhsuk-frontend/dist/nhsuk'], + stories: ['../stories/**/*.stories.@(ts|tsx)', '../stories/**/*.mdx'], + typescript: { reactDocgen: 'react-docgen-typescript', }, diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 00000000..5a85c138 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1 @@ + diff --git a/.storybook/storybook.scss b/.storybook/storybook.scss index ea4b70a2..1cf1ac12 100644 --- a/.storybook/storybook.scss +++ b/.storybook/storybook.scss @@ -1,5 +1,3 @@ -@forward 'nhsuk-frontend/dist/nhsuk'; - .tag-wrapper { display: flex; flex-direction: column; diff --git a/.storybook/theme.ts b/.storybook/theme.ts index c47f6582..e2a816a2 100644 --- a/.storybook/theme.ts +++ b/.storybook/theme.ts @@ -4,6 +4,6 @@ import packageJson from '../package.json' with { type: 'json' }; export default create({ base: 'light', - brandTitle: `NHS.UK React Components (v${packageJson.version})`, + brandTitle: `NHS.UK React components (v${packageJson.version})`, brandUrl: 'https://github.com/NHSDigital/nhsuk-react-components', }); diff --git a/README.md b/README.md index d80ff5f2..5d239140 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NHS.UK React components -This repository contains the code for NHS.UK React components - a port of the [NHS.UK Frontend components](https://github.com/nhsuk/nhsuk-frontend). +This repository contains the code for NHS.UK React components - a port of the [NHS.UK frontend components](https://github.com/nhsuk/nhsuk-frontend). [![GitHub Actions CI Status](https://github.com/NHSDigital/nhsuk-react-components/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/NHSDigital/nhsuk-react-components/actions?query=workflow%3A%22CI+Build%22+branch%3Amain) [![Bundle Size](https://img.shields.io/bundlephobia/minzip/nhsuk-react-components.svg)](https://bundlephobia.com/result?p=nhsuk-react-components) @@ -22,7 +22,7 @@ yarn add nhsuk-react-components ## Usage ```jsx -import { DateInput, Form } from 'nhsuk-react-components'; +import { Button, DateInput, Form } from 'nhsuk-react-components';
` component. +In NHS.UK frontend v5 and above, the header text now defaults to wrapping underneath the logo without the need for a modifier. It is safe to remove the `long` prop from the `` component. ## The `prefixText` prop has been added to the `DoDontList.Item` component diff --git a/src/components/content-presentation/icons/Icon.tsx b/src/components/content-presentation/icons/Icon.tsx index ccbbb90d..e5f12b75 100644 --- a/src/components/content-presentation/icons/Icon.tsx +++ b/src/components/content-presentation/icons/Icon.tsx @@ -24,8 +24,8 @@ export const Icon: FC = ({ children, iconType, modifier = iconType - ?.replace('nhsuk-icon__', '') // NHS.UK Frontend v9.x - .replace('nhsuk-icon--', ''), // NHS.UK Frontend v10.x + ?.replace('nhsuk-icon__', '') // NHS.UK frontend v9.x + .replace('nhsuk-icon--', ''), // NHS.UK frontend v10.x title, ...rest }) => ( diff --git a/stories/Content Presentation/Details.stories.tsx b/stories/Content Presentation/Details.stories.tsx index 107e4226..63ef2962 100644 --- a/stories/Content Presentation/Details.stories.tsx +++ b/stories/Content Presentation/Details.stories.tsx @@ -17,11 +17,19 @@ const meta: Meta = { title: 'Content Presentation/Details', component: Details, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { - argTypes: { expander: { table: { disable: true } } }, +export const Default: Story = { + name: 'Details default', + argTypes: { + expander: { + table: { + disable: true, + }, + }, + }, render: ({ expander }) => (
Where can I find my NHS number? @@ -44,7 +52,10 @@ export const Standard: Story = { }; export const Expander: Story = { - args: { expander: true }, + name: 'Expander', + args: { + expander: true, + }, render: ({ expander }) => (
Opening times @@ -95,6 +106,7 @@ export const Expander: Story = { }; export const ExpanderGroup: Story = { + name: 'Expander group', render: (args) => (
diff --git a/stories/Content Presentation/DoAndDontList.stories.tsx b/stories/Content Presentation/DoAndDontList.stories.tsx index 0234d142..b3584150 100644 --- a/stories/Content Presentation/DoAndDontList.stories.tsx +++ b/stories/Content Presentation/DoAndDontList.stories.tsx @@ -16,13 +16,15 @@ import { DoAndDontList } from '#components/content-presentation/do-and-dont-list * See the custom prefix text story for an example. */ const meta: Meta = { - title: 'Content Presentation/DoAndDontList', + title: "Content Presentation/Do and Don't list", component: DoAndDontList, }; + export default meta; type Story = StoryObj; export const Do: Story = { + name: "Do and Don't list (do)", args: { listType: 'do', }, @@ -40,6 +42,7 @@ export const Do: Story = { }; export const Dont: Story = { + name: "Do and Don't list (don't)", args: { listType: 'dont', }, @@ -74,6 +77,7 @@ export const Dont: Story = { * | JSX | The JSX will be rendered, such as `` | */ export const CustomPrefixText: Story = { + name: "Do and Don't list with custom prefix", args: { listType: 'dont', }, diff --git a/stories/Content Presentation/Hero.stories.tsx b/stories/Content Presentation/Hero.stories.tsx index bbb38ec7..17ecc2f3 100644 --- a/stories/Content Presentation/Hero.stories.tsx +++ b/stories/Content Presentation/Hero.stories.tsx @@ -10,29 +10,31 @@ const meta: Meta = { width: false, }, }; + export default meta; type Story = StoryObj; -export const HeroWithHeadingAndContent: Story = { +export const Default: Story = { + name: 'Hero default', render: (args) => ( - + We're here for you. Helping you take control of your health and wellbeing. ), }; -export const HeroWithImageHeadingAndContent: Story = { - render: (args) => ( - - We're here for you. - Helping you take control of your health and wellbeing. - - ), +export const WithImage: Story = { + name: 'Hero with image', + args: { + imageSrc: 'https://assets.nhs.uk/prod/images/S_0818_homepage_hero_1_F0147446.width-1000.jpg', + }, }; -export const HeroWithImageOnly: Story = { - render: (args) => ( - - ), +export const WithImageContent: Story = { + name: 'Hero with image, content', + args: { + imageSrc: 'https://assets.nhs.uk/prod/images/S_0818_homepage_hero_1_F0147446.width-1000.jpg', + }, + render: Default.render, }; diff --git a/stories/Content Presentation/Icons.stories.tsx b/stories/Content Presentation/Icons.stories.tsx index 4aa88398..64ce91a6 100644 --- a/stories/Content Presentation/Icons.stories.tsx +++ b/stories/Content Presentation/Icons.stories.tsx @@ -14,14 +14,46 @@ import { const meta: Meta = { title: 'Content Presentation/Icons', }; + export default meta; type Story = StoryObj; -export const ArrowLeft: Story = { render: (args) => }; -export const ArrowRight: Story = { render: (args) => }; -export const ArrowRightCircle: Story = { render: (args) => }; -export const ChevronRightCircle: Story = { render: (args) => }; -export const Cross: Story = { render: (args) => }; -export const Search: Story = { render: (args) => }; -export const Tick: Story = { render: (args) => }; -export const User: Story = { render: (args) => }; +export const ArrowLeft: Story = { + name: 'Arrow left', + render: (args) => , +}; + +export const ArrowRight: Story = { + name: 'Arrow right', + render: (args) => , +}; + +export const ArrowRightCircle: Story = { + name: 'Arrow right circle', + render: (args) => , +}; + +export const ChevronRightCircle: Story = { + name: 'Chevron right circle', + render: (args) => , +}; + +export const Cross: Story = { + name: 'Cross', + render: (args) => , +}; + +export const Search: Story = { + name: 'Search', + render: (args) => , +}; + +export const Tick: Story = { + name: 'Tick', + render: (args) => , +}; + +export const User: Story = { + name: 'User', + render: (args) => , +}; diff --git a/stories/Content Presentation/Images.stories.tsx b/stories/Content Presentation/Images.stories.tsx index baf05ab7..65b1989a 100644 --- a/stories/Content Presentation/Images.stories.tsx +++ b/stories/Content Presentation/Images.stories.tsx @@ -5,21 +5,44 @@ import { Images } from '#components/content-presentation/images/index.js'; const meta: Meta = { title: 'Content Presentation/Images', component: Images, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + name: 'Image default', + args: { + src: 'https://assets.nhs.uk/prod/images/A_0218_exercise-main_FKW1X7.width-690.jpg', + caption: + 'No specific amount of time is recommended, but a typical training session could take less than 20 minutes.', + }, +}; + +export const WithSrcSet: Story = { + name: 'Image with srcset', args: { - src: 'https://assets.nhs.uk/prod/images/S_1017_allergic-conjunctivitis_M15.2e16d0ba.fill-320x213.jpg', - alt: 'Picture of allergic conjunctivitis', - sizes: '(min-width: 1020px) 320px, (min-width: 768px) 50vw, 100vw', + src: 'https://service-manual.nhs.uk/assets/image-example-stretch-marks-600w.jpg', + sizes: '(max-width: 768px) 100vw, 66vw', srcSet: - 'https://assets.nhs.uk/prod/images/S_1017_allergic-conjunctivitis_M15.2e16d0ba.fill-640x427.jpg 640w, https://assets.nhs.uk/prod/images/S_1017_allergic-conjunctivitis_M15.2e16d0ba.fill-767x511.jpg 767w', + 'https://service-manual.nhs.uk/assets/image-example-stretch-marks-600w.jpg 600w, https://service-manual.nhs.uk/assets/image-example-stretch-marks-1000w.jpg 1000w', + caption: + 'Stretch marks can be pink, red, brown, black, silver or purple. They usually start off darker and fade over time.', }, }; -export default meta; -type Story = StoryObj; -export const ImageWithCaption: Story = { +export const WithSrcSetAltText: Story = { + name: 'Image with srcset and alt text', args: { - caption: 'Caption for image', + ...WithSrcSet.args, + alt: "Close-up of a person's tummy showing a number of creases in the skin under their belly button. Shown on light brown skin.", }, }; -export const ImageWithoutCaption: Story = {}; +export const WithoutCaption: Story = { + name: 'Image without caption', + args: { + ...WithSrcSetAltText, + caption: undefined, + }, +}; diff --git a/stories/Content Presentation/InsetText.stories.tsx b/stories/Content Presentation/InsetText.stories.tsx index 871e2efc..b8f5c9cf 100644 --- a/stories/Content Presentation/InsetText.stories.tsx +++ b/stories/Content Presentation/InsetText.stories.tsx @@ -3,7 +3,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { InsetText } from '#components/content-presentation/inset-text/index.js'; const meta: Meta = { - title: 'Content Presentation/InsetText', + title: 'Content Presentation/Inset text', component: InsetText, render: (args) => ( @@ -17,7 +17,8 @@ const meta: Meta = { ), }; + export default meta; type Story = StoryObj; -export const Standard: Story = {}; +export const Default: Story = {}; diff --git a/stories/Content Presentation/NotificationBanner.stories.tsx b/stories/Content Presentation/NotificationBanner.stories.tsx index 17524905..21afc018 100644 --- a/stories/Content Presentation/NotificationBanner.stories.tsx +++ b/stories/Content Presentation/NotificationBanner.stories.tsx @@ -15,14 +15,15 @@ import { NotificationBanner } from '#components/content-presentation/notificatio * - `NotificationBanner.Link` */ const meta: Meta = { - title: 'Content Presentation/Notification Banner', + title: 'Content Presentation/Notification banner', component: NotificationBanner, }; + export default meta; type Story = StoryObj; -export const StandardPanel: Story = { - args: {}, +export const Default: Story = { + name: 'Notification banner default', render: (args) => ( @@ -32,7 +33,8 @@ export const StandardPanel: Story = { ), }; -export const SuccessPanel: Story = { +export const Success: Story = { + name: 'Notification banner success', args: { success: true, }, @@ -48,8 +50,8 @@ export const SuccessPanel: Story = { ), }; -export const StandardPanelWithLink: Story = { - args: {}, +export const WithLink: Story = { + name: 'Notification banner with link', render: (args) => ( @@ -60,7 +62,8 @@ export const StandardPanelWithLink: Story = { ), }; -export const StandardPanelWithCustomTitle: Story = { +export const WithCustomTitle: Story = { + name: 'Notification banner with custom title', args: { title: 'Important Message', }, @@ -72,8 +75,8 @@ export const StandardPanelWithCustomTitle: Story = { ), }; -export const StandardPanelWithCustomTitleElement: Story = { - args: {}, +export const WithCustomTitleElement: Story = { + name: 'Notification banner with custom title element', render: (args) => ( @@ -85,8 +88,8 @@ export const StandardPanelWithCustomTitleElement: Story = { ), }; -export const StandardPanelWithList: Story = { - args: {}, +export const WithList: Story = { + name: 'Notification banner with list', render: (args) => ( 4 files uploaded @@ -108,8 +111,8 @@ export const StandardPanelWithList: Story = { ), }; -export const StandardPanelWithLotsOfContent: Story = { - args: {}, +export const WithLotsOfContent: Story = { + name: 'Notification banner with lots of content', render: (args) => ( diff --git a/stories/Content Presentation/Panel.stories.tsx b/stories/Content Presentation/Panel.stories.tsx index c699f0c3..6aa4f860 100644 --- a/stories/Content Presentation/Panel.stories.tsx +++ b/stories/Content Presentation/Panel.stories.tsx @@ -7,10 +7,12 @@ const meta: Meta = { title: 'Content Presentation/Panel', component: Panel, }; + export default meta; type Story = StoryObj; -export const StandardPanel: Story = { +export const Default: Story = { + name: 'Panel default', render: (args) => ( Booking complete diff --git a/stories/Content Presentation/SummaryList.stories.tsx b/stories/Content Presentation/SummaryList.stories.tsx index e593e3de..cdaa77b2 100644 --- a/stories/Content Presentation/SummaryList.stories.tsx +++ b/stories/Content Presentation/SummaryList.stories.tsx @@ -5,7 +5,7 @@ import { Card } from '#components/navigation/card/index.js'; import { BodyText } from '#components/typography/BodyText.js'; const meta: Meta = { - title: 'Content Presentation/SummaryList', + title: 'Content Presentation/Summary list', component: SummaryList, argTypes: { noBorder: { @@ -14,10 +14,11 @@ const meta: Meta = { }, }, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { name: 'Summary list default', render: (args) => ( diff --git a/stories/Content Presentation/Table.stories.tsx b/stories/Content Presentation/Table.stories.tsx index 5d0c69a9..a5e9e16b 100644 --- a/stories/Content Presentation/Table.stories.tsx +++ b/stories/Content Presentation/Table.stories.tsx @@ -7,10 +7,12 @@ const meta: Meta = { title: 'Content Presentation/Table', component: Table, }; + export default meta; type Story = StoryObj; -export const StandardTable: Story = { +export const Default: Story = { + name: 'Table default', args: { caption: 'Skin symptoms and possible causes', }, @@ -40,7 +42,8 @@ export const StandardTable: Story = { ), }; -export const TableCard: Story = { +export const AsACard: Story = { + name: 'Table as a card', args: { caption: 'Impetigo can look similar to other skin conditions', }, @@ -73,7 +76,8 @@ export const TableCard: Story = { ), }; -export const ResponsiveTable: Story = { +export const Responsive: Story = { + name: 'Table responsive', args: { caption: 'Ibuprofen syrup dosages for children', responsive: true, @@ -108,7 +112,8 @@ export const ResponsiveTable: Story = { ), }; -export const ResponsiveTableWithCustomHTML: Story = { +export const WithCustomHTML: Story = { + name: 'Table with custom HTML', args: { caption: 'Skin symptoms and possible causes', }, @@ -183,6 +188,7 @@ export const ResponsiveTableWithCustomHTML: Story = { }; export const FirstCellAsHeader: Story = { + name: 'Table with first cell as header', args: { firstCellIsHeader: true, }, @@ -229,6 +235,7 @@ export const FirstCellAsHeader: Story = { }; export const NumericCells: Story = { + name: 'Table with numeric cells', args: { caption: 'Number of cases', }, diff --git a/stories/Content Presentation/Tabs.stories.tsx b/stories/Content Presentation/Tabs.stories.tsx index b9fc34b0..d8cebd86 100644 --- a/stories/Content Presentation/Tabs.stories.tsx +++ b/stories/Content Presentation/Tabs.stories.tsx @@ -21,7 +21,8 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Tabs default', render: (args) => ( Contents @@ -45,33 +46,3 @@ export const Standard: Story = { ), }; - -/** - * There is a heading available by using the `Tabs.Title` component. - * This heading is hidden on larger screens, and is useful for accessibility concerns and screen readers. - * It also displays if the user has JavaScript disabled. - */ -export const DifferentAccessibleHeading: Story = { - render: (args) => ( - - Tabs title - - Past day - Past week - Past month - - - -

Past day contents go here

-
- - -

Past week contents go here

-
- - -

Past month contents go here

-
-
- ), -}; diff --git a/stories/Content Presentation/Tag.stories.tsx b/stories/Content Presentation/Tag.stories.tsx index 2ae6bd7f..bd49e41c 100644 --- a/stories/Content Presentation/Tag.stories.tsx +++ b/stories/Content Presentation/Tag.stories.tsx @@ -6,24 +6,51 @@ const meta: Meta = { title: 'Content Presentation/Tag', component: Tag, }; + export default meta; type Story = StoryObj; -export const StandardTag: Story = { args: { children: 'Standard' } }; +export const Default: Story = { + name: 'Tag default', + args: { + children: 'Active', + }, +}; -export const AllColours: Story = { +export const Colours: Story = { + name: 'Tag colours', render: () => ( -
- Started - Not started - New - Active - Pending - Received - Sent - Rejected - Declined - Delayed -
+ <> +

+ In progress +

+

+ Inactive +

+

+ New +

+

+ Active +

+

+ Pending +

+

+ Received +

+

+ Sent +

+

+ Rejected +

+

+ Declined +

+

+ Delayed +

+ ), }; diff --git a/stories/Content Presentation/WarningCallout.stories.tsx b/stories/Content Presentation/WarningCallout.stories.tsx index 50ec08c7..4a3a4265 100644 --- a/stories/Content Presentation/WarningCallout.stories.tsx +++ b/stories/Content Presentation/WarningCallout.stories.tsx @@ -3,13 +3,15 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { WarningCallout } from '#components/content-presentation/warning-callout/index.js'; const meta: Meta = { - title: 'Content Presentation/WarningCallout', + title: 'Content Presentation/Warning callout', component: WarningCallout, }; + export default meta; type Story = StoryObj; -export const StandardWarningCallout: Story = { +export const Default: Story = { + name: 'Warning callout default', render: (args) => ( Important @@ -19,11 +21,10 @@ export const StandardWarningCallout: Story = {

), - - name: 'WarningCallout', }; -export const WarningCalloutWithCustomHeading: Story = { +export const WithCustomHeading: Story = { + name: 'Warning callout with custom heading', render: (args) => ( School, nursery or work @@ -35,7 +36,8 @@ export const WarningCalloutWithCustomHeading: Story = { ), }; -export const WarningCalloutWithCustomHeadingLevel: Story = { +export const WithCustomHeadingLevel: Story = { + name: 'Warning callout with custom heading level', render: (args) => ( School, nursery or work diff --git a/stories/Form Elements/Button.stories.tsx b/stories/Form Elements/Button.stories.tsx index e8c83082..e14d95fd 100644 --- a/stories/Form Elements/Button.stories.tsx +++ b/stories/Form Elements/Button.stories.tsx @@ -48,13 +48,15 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Primary: Story = { +export const Default: Story = { + name: 'Button default', args: { children: 'Save and continue', }, }; -export const SmallPrimary: Story = { +export const SmallDefault: Story = { + name: 'Button default, small', args: { small: true, children: 'Save and continue', @@ -62,6 +64,7 @@ export const SmallPrimary: Story = { }; export const Secondary: Story = { + name: 'Button secondary', args: { secondary: true, children: 'Find my location', @@ -69,6 +72,7 @@ export const Secondary: Story = { }; export const SmallSecondary: Story = { + name: 'Button secondary, small', args: { secondary: true, small: true, @@ -77,6 +81,7 @@ export const SmallSecondary: Story = { }; export const Warning: Story = { + name: 'Button warning', args: { warning: true, children: 'Yes, delete this vaccine', @@ -84,6 +89,7 @@ export const Warning: Story = { }; export const SmallWarning: Story = { + name: 'Button warning, small', args: { warning: true, small: true, @@ -92,6 +98,7 @@ export const SmallWarning: Story = { }; export const Login: Story = { + name: 'Button login', args: { login: true, children: 'Continue to NHS login', @@ -99,6 +106,7 @@ export const Login: Story = { }; export const SmallLogin: Story = { + name: 'Button login, small', args: { login: true, small: true, @@ -107,47 +115,83 @@ export const SmallLogin: Story = { }; export const Reverse: Story = { + name: 'Button reverse', args: { reverse: true, children: 'Log out', }, + globals: { + backgrounds: { value: 'dark' }, + }, }; export const SmallReverse: Story = { + name: 'Button reverse, small', args: { reverse: true, small: true, children: 'Log out', }, + globals: { + backgrounds: { value: 'dark' }, + }, }; /** - * * Disabled buttons have poor contrast and can confuse some users. Only use them if user research shows it makes things easier for users to understand. - * */ +export const Disabled: Story = { + name: 'Button disabled', + args: { + disabled: true, + children: 'Disabled button', + }, +}; + +export const SmallDisabled: Story = { + name: 'Button disabled, small', + args: { + small: true, + disabled: true, + children: 'Disabled button', + }, +}; -export const Disabled: Story = { args: { disabled: true, children: 'Disabled' } }; -export const LinkButton: Story = { args: { href: '/', children: 'As a Link' } }; -export const ForceButton: Story = { args: { as: 'button', children: 'As a Button' } }; -export const ForceAnchor: Story = { args: { as: 'a', children: 'As an Anchor' } }; +export const AsALink: Story = { + name: 'Button as a link', + args: { + href: '/', + children: 'Link button', + }, +}; + +export const SmallAsALink: Story = { + name: 'Button as a link, small', + args: { + href: '/', + small: true, + children: 'Link button', + }, +}; /** * You can test this button by opening the browser console. It will log the current dateTime once per debounce. */ export const PreventDoubleClickButton: Story = { + name: 'Button with double click prevented', args: { preventDoubleClick: true, onClick: () => console.log(new Date()), - children: 'Debounced Button', + children: 'Save and continue', }, }; export const PreventDoubleClickLinkButton: Story = { + name: 'Button as a link with double click prevented', args: { - preventDoubleClick: true, href: '#', + preventDoubleClick: true, onClick: () => console.log(new Date()), - children: 'Debounced Button as Link', + children: 'Save and continue', }, }; diff --git a/stories/Form Elements/CharacterCount.stories.tsx b/stories/Form Elements/CharacterCount.stories.tsx index 75ec204a..9406ec28 100644 --- a/stories/Form Elements/CharacterCount.stories.tsx +++ b/stories/Form Elements/CharacterCount.stories.tsx @@ -11,7 +11,7 @@ import { CharacterCount } from '#components/form-elements/character-count/index. */ const meta: Meta = { - title: 'Form Elements/CharacterCount', + title: 'Form Elements/Character count', component: CharacterCount, args: { label: 'Can you provide more detail?', @@ -25,7 +25,8 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Character count default', args: { maxLength: 200, }, @@ -35,6 +36,7 @@ export const Standard: Story = { * Sometimes, rather than counting the number of characters, it is useful to count the number of words instead. */ export const WordCountLimit: Story = { + name: 'Character count wth maxwords', args: { maxWords: 150, }, @@ -46,6 +48,7 @@ export const WordCountLimit: Story = { * Use the `threshold` prop to only show the count message when users have reached that percentage of the limit. */ export const MessageThreshold: Story = { + name: 'Character count wth threshold', args: { maxLength: 112, threshold: 75, @@ -53,6 +56,7 @@ export const MessageThreshold: Story = { }; export const WithTranslations: Story = { + name: 'Character count wth translations', args: { label: 'Allwch chi roi mwy o fanylion?', hint: 'Peidiwch â chynnwys gwybodaeth bersonol, fel eich enw, dyddiad geni na rhif y GIG', diff --git a/stories/Form Elements/Checkboxes.stories.tsx b/stories/Form Elements/Checkboxes.stories.tsx index 9ae0533d..bd03e0d3 100644 --- a/stories/Form Elements/Checkboxes.stories.tsx +++ b/stories/Form Elements/Checkboxes.stories.tsx @@ -3,10 +3,11 @@ import { type ChangeEvent, type InputEvent, useEffect, useRef, useState } from ' import { Checkboxes } from '#components/form-elements/checkboxes/index.js'; import { Fieldset } from '#components/form-elements/fieldset/Fieldset.js'; -import { TextInput } from '#components/form-elements/text-input/index.js'; import { BodyText } from '#components/typography/BodyText.js'; import { Heading } from '#components/typography/Heading.js'; +import { ExampleEmail, ExampleMobilePhoneNumber, ExamplePhoneNumber } from './TextInput.stories.js'; + /** * This component can be found in the `nhsuk-frontend` repository here. * @@ -26,6 +27,7 @@ const meta: Meta = { name: 'example', }, }; + export default meta; type Story = StoryObj; @@ -35,10 +37,11 @@ interface CheckboxState { box3: { name?: string; id?: string }; } -export const Standard: Story = { +export const Default: Story = { name: 'Checkboxes default', args: { hint: 'Select all options that are relevant to you', + name: 'default', }, render: (args) => ( @@ -49,6 +52,17 @@ export const Standard: Story = { ), }; +export const Small: Story = { + name: 'Checkboxes small', + args: { + ...Default.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small', + small: true, + }, + render: Default.render, +}; + export const WithCaption: Story = { name: 'Checkboxes with caption', args: { @@ -58,15 +72,34 @@ export const WithCaption: Story = { How do you want to be contacted about this? ), + name: 'with-caption', }, - render: Standard.render, + render: Default.render, }; -export const WithHintText: Story = { +export const SmallWithCaption: Story = { + name: 'Checkboxes with caption, small', + args: { + ...WithCaption.args, + legend: ( + <> + About you How do you want to be contacted about + this? + + ), + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-caption', + small: true, + }, + render: WithCaption.render, +}; + +export const WithHint: Story = { name: 'Checkboxes with hint', args: { legend: 'What is your nationality?', hint: 'If you have dual nationality, select all options that are relevant to you', + name: 'with-hint', }, render: (args) => ( @@ -77,11 +110,23 @@ export const WithHintText: Story = { ), }; -export const WithHintTextOnItems: Story = { +export const SmallWithHint: Story = { + name: 'Checkboxes with hint, small', + args: { + ...WithHint.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-hint', + small: true, + }, + render: WithHint.render, +}; + +export const WithHintOnItems: Story = { name: 'Checkboxes with hints on items', args: { legend: 'What is your nationality?', hint: 'If you have dual nationality, select all options that are relevant to you', + name: 'with-hint-on-items', }, render: (args) => ( @@ -94,91 +139,63 @@ export const WithHintTextOnItems: Story = { ), }; -export const WithValues: Story = { - name: 'Checkboxes with pre-checked values', +export const SmallWithHintOnItems: Story = { + name: 'Checkboxes with hints on items, small', + args: { + ...WithHintOnItems.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-hint-on-items', + small: true, + }, + render: WithHintOnItems.render, +}; + +export const WithError: Story = { + name: 'Checkboxes with error message', args: { - name: 'exampleConditional1', error: 'Select how you want to be contacted', + name: 'with-error', }, - render: (args) => ( - - - } - > - Email - - - } - > - Phone - - - } - > - Text message - - - ), + render: Default.render, }; -export const Small: Story = { - name: 'Checkboxes small', +export const SmallWithError: Story = { + name: 'Checkboxes with error message, small', args: { - ...Standard.args, + ...WithError.args, legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-error', small: true, }, - render: Standard.render, + render: WithError.render, }; -export const SmallWithHintText: Story = { - name: 'Checkboxes small with hint', +export const WithHintAndError: Story = { + name: 'Checkboxes with error message and hint', args: { - ...WithHintText.args, - legendProps: { isPageHeading: true, size: 'm' }, - small: true, + hint: 'Select all options that are relevant to you', + error: 'Select how you want to be contacted', + name: 'with-hint-and-error', }, - render: WithHintText.render, + render: Default.render, }; -export const SmallWithHintTextOnItems: Story = { - name: 'Checkboxes small with hints on items', +export const SmallWithHintAndError: Story = { + name: 'Checkboxes with error message and hint, small', args: { - ...WithHintTextOnItems.args, + ...WithHintAndError.args, legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-hint-and-error', small: true, }, - render: WithHintTextOnItems.render, + render: WithHintAndError.render, }; export const WithDisabledItem: Story = { name: 'Checkboxes with disabled item', + args: { + name: 'with-disabled-item', + }, render: (args) => ( Red @@ -190,68 +207,70 @@ export const WithDisabledItem: Story = { ), }; -export const WithError: Story = { - name: 'Checkboxes with error message', +export const SmallWithDisabledItem: Story = { + name: 'Checkboxes with disabled item, small', args: { - error: 'Select how you want to be contacted', + ...WithDisabledItem.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-disabled-item', + small: true, }, - render: Standard.render, + render: WithDisabledItem.render, }; -export const WithHintAndError: Story = { - name: 'Checkboxes with hint and error', +export const WithValues: Story = { + name: 'Checkboxes with pre-checked values', args: { - hint: 'Select all options that are relevant to you', - error: 'Select how you want to be contacted', + name: 'with-values', }, - render: Standard.render, + render: (args) => ( + + + Email + + Phone + + Text message + + + ), }; export const WithConditionalContent: Story = { name: 'Checkboxes with conditional content', args: { - name: 'exampleConditional2', hint: 'Select all options that are relevant to you', + name: 'with-conditional-content', }, render: (args) => ( - - } - > + }> Email - - } - > + }> Phone - - } - > + }> + Text message + + + ), +}; + +export const WithConditionalContentValues: Story = { + name: 'Checkboxes with conditional content and pre-checked values', + args: { + name: 'with-conditional-content-values', + }, + render: (args) => ( + + } defaultChecked> + Email + + }> + Phone + + } defaultChecked> Text message @@ -263,6 +282,7 @@ export const WithConditionalContentError: Story = { args: { hint: 'Select all options that are relevant to you', error: 'Select how you like to be contacted', + name: 'with-conditional-content-error', }, render: WithConditionalContent.render, }; @@ -270,50 +290,22 @@ export const WithConditionalContentError: Story = { export const WithConditionalContentErrorNested: Story = { name: 'Checkboxes with conditional content, error message (nested)', args: { - name: 'exampleConditional3', hint: 'Select all options that are relevant to you', + name: 'with-conditional-content-error-nested', }, render: (args) => ( - - } - > + }> Email - } + conditional={} + defaultChecked > Phone - - } - > + }> Text message @@ -325,6 +317,7 @@ export const WithExclusiveNoneOption: Story = { args: { legend: 'How do you want to be contacted about this?', hint: 'Select all options that are relevant to you', + name: 'with-exclusive-none-option', }, render: (args) => ( @@ -342,49 +335,19 @@ export const WithExclusiveNoneOption: Story = { export const WithExclusiveNoneOptionConditional: Story = { name: 'Checkboxes with "none of the above" option, conditional content', args: { - name: 'exampleConditional4', legend: 'How do you want to be contacted about this?', hint: 'Select all options that are relevant to you', + name: 'with-exclusive-none-option-conditional', }, render: (args) => ( - - } - > + }> Email - - } - > + }> Phone - - } - > + }> Text message @@ -397,6 +360,9 @@ export const WithExclusiveNoneOptionConditional: Story = { export const WithExclusiveNoneOptionNamed: Story = { name: 'Checkboxes with "none of the above" option (named groups)', + args: { + name: 'with-exclusive-none-option-named', + }, render: (args) => (
diff --git a/stories/Form Elements/DateInput.stories.tsx b/stories/Form Elements/DateInput.stories.tsx index 89b24a86..d60345c0 100644 --- a/stories/Form Elements/DateInput.stories.tsx +++ b/stories/Form Elements/DateInput.stories.tsx @@ -4,76 +4,97 @@ import { useState } from 'react'; import { DateInput, type DateInputValue } from '#components/form-elements/date-input/index.js'; const meta: Meta = { - title: 'Form Elements/DateInput', + title: 'Form Elements/Date input', component: DateInput, args: { legend: 'What is your date of birth?', legendProps: { isPageHeading: true, size: 'l' }, - hint: 'For example, 15 3 1984', }, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Date input default', + args: { + hint: 'For example, 15 3 1984', + }, + render: (args) => , +}; + +export const WithError: Story = { + name: 'Date input with error message only', + args: { + error: 'Enter your date of birth', + }, + render: (args) => , +}; + +export const WithErrorDay: Story = { + name: 'Date input with error on day input', + args: { + error: 'Date of birth must include a day', + }, render: (args) => ( - <> -

Scenario: onChange and onInput handlers are bound without any other props

-
Expected Behaviour
-
    -
  • OnChange Handlers are fired using the generated IDs and Names
  • -
  • The value is passed through
  • -
-
Component
- console.log(e.target.value)} {...args} /> - + + + + + + ), +}; + +export const WithErrorMonth: Story = { + name: 'Date input with error on month input', + args: { + error: 'Date of birth must include a month', + }, + render: (args) => ( + + + + + ), }; -export const StandardWithError: Story = { +export const WithErrorYear: Story = { + name: 'Date input with error on year input', + args: { + error: 'Date of birth must include a year', + }, render: (args) => ( - <> -

Scenario: onChange and onInput handlers are bound without any other props

-
Expected Behaviour
-
    -
  • OnChange Handlers are fired using the generated IDs and Names
  • -
  • The value is passed through
  • -
-
Component
- console.log(e.target.value)} - {...args} - /> - -
Component with specific field errors
- console.log(e.target.value)} - {...args} - > - - - - - + + + + + ), }; -export const PrePopulatedIndividualComponents: Story = { - render: (args) => { - const defaultValue = { day: '20', month: '09', year: '1996' }; - return ( - - - - - - ); +export const WithErrorAndHint: Story = { + name: 'Date input with error message and hint', + args: { + hint: 'For example, 15 3 1984', + error: 'Enter your date of birth', + }, + render: (args) => , +}; + +export const WithValue: Story = { + name: 'Date input with value', + args: { + defaultValue: { + day: '20', + month: '09', + year: '1996', + }, }, }; -export const PrePopulatedWrapper: Story = { +export const WithValues: Story = { + name: 'Date input with values', args: { defaultValue: { day: '20', @@ -81,12 +102,38 @@ export const PrePopulatedWrapper: Story = { year: '1996', }, }, - render: (args) => { - return ; + render: ({ defaultValue, ...args }) => ( + + + + + + ), +}; + +export const WithValueControlled: Story = { + name: 'Date input with value, controlled', + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, + args: { + value: { + day: '20', + month: '09', + year: '1996', + }, }, }; -export const ControlledElementIndividualComponents: Story = { +export const WithValuesControlled: Story = { + name: 'Date input with values, controlled', + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, args: { value: { day: '20', @@ -105,21 +152,14 @@ export const ControlledElementIndividualComponents: Story = { }, }; -export const ControlledElementWrapper: Story = { - args: { - value: { - day: '20', - month: '09', - year: '1996', +export const WithValueStateControlled: Story = { + name: 'Date input with value state, controlled', + parameters: { + chromatic: { + disableSnapshot: true, }, }, - render: (args) => { - return ; - }, -}; - -export const ChangeableControlledElement: Story = { - render: function ChangeableControlledElementRender(args) { + render: function WithValueStateControlledRender(args) { const [value, setValue] = useState | undefined>({ day: '20', month: '09', @@ -129,3 +169,24 @@ export const ChangeableControlledElement: Story = { return setValue(e.currentTarget.value)} {...args} />; }, }; + +export const OnChangeHandler: Story = { + name: 'Date input change handler', + parameters: { + chromatic: { + disableSnapshot: true, + }, + }, + render: (args) => ( + <> +
    +
  • Change handler is fired using the generated IDs and names
  • +
  • The value is passed through
  • +
+ +
+ + console.log(e.target.value)} {...args} /> + + ), +}; diff --git a/stories/Form Elements/ErrorMessage.stories.tsx b/stories/Form Elements/ErrorMessage.stories.tsx index a8247d87..00d2e30e 100644 --- a/stories/Form Elements/ErrorMessage.stories.tsx +++ b/stories/Form Elements/ErrorMessage.stories.tsx @@ -6,7 +6,7 @@ import { ErrorMessage } from '#components/form-elements/error-message/index.js'; * This component can be found in the `nhsuk-frontend` repository here. */ const meta: Meta = { - title: 'Form Elements/ErrorMessage', + title: 'Form Elements/Error message', component: ErrorMessage, args: { children: 'Enter your full name', @@ -20,17 +20,22 @@ const meta: Meta = { }, }, }; + export default meta; type Story = StoryObj; -export const Standard: Story = {}; +export const Default: Story = { + name: 'Error message default', +}; export const CustomVisuallyHiddenText: Story = { + name: 'Error message with custom visually hidden text', args: { visuallyHiddenText: 'Custom' }, argTypes: { visuallyHiddenText: { control: false } }, }; export const EmptyVisuallyHiddenText: Story = { + name: 'Error message with empty visually hidden text', args: { visuallyHiddenText: '' }, argTypes: { visuallyHiddenText: { control: false } }, }; diff --git a/stories/Form Elements/ErrorSummary.stories.tsx b/stories/Form Elements/ErrorSummary.stories.tsx index f8032784..bd1a1549 100644 --- a/stories/Form Elements/ErrorSummary.stories.tsx +++ b/stories/Form Elements/ErrorSummary.stories.tsx @@ -14,49 +14,73 @@ import { ErrorSummary } from '#components/form-elements/error-summary/index.js'; * - `ErrorSummary.ListItem` */ const meta: Meta = { - title: 'Form Elements/ErrorSummary', + title: 'Form Elements/Error summary', component: ErrorSummary, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Error summary default', render: (args) => ( There is a problem +

Describe the errors and how to correct them

- Enter your full name + + Date of birth must be in the past +
), }; -export const WithDescription: Story = { +export const WithMultipleErrors: Story = { + name: 'Error summary with multiple errors', render: (args) => ( There is a problem -

Describe the errors and how to correct them

- - Date of birth must be in the past + + Enter your first name + + + Enter your last name
), }; -export const WithAutoFocusDisabled: Story = { - args: { - disableAutoFocus: true, - }, +export const WithoutDescription: Story = { + name: 'Error summary without description', render: (args) => ( There is a problem - + Date of birth must be in the past ), }; + +export const WithoutErrorList: Story = { + name: 'Error summary without error list', + render: (args) => ( + + There is a problem +

Describe the errors and how to correct them

+
+ ), +}; + +export const WithAutoFocusDisabled: Story = { + name: 'Error summary with auto-focus disabled', + args: { + disableAutoFocus: true, + }, + render: Default.render, +}; diff --git a/stories/Form Elements/Fieldset.stories.tsx b/stories/Form Elements/Fieldset.stories.tsx index e6376c8f..fe6a8645 100644 --- a/stories/Form Elements/Fieldset.stories.tsx +++ b/stories/Form Elements/Fieldset.stories.tsx @@ -10,10 +10,12 @@ const meta: Meta = { title: 'Form Elements/Fieldset', component: Fieldset, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Fieldset default', render: (args) => (
@@ -50,7 +52,7 @@ export const Standard: Story = { }; export const WithCustomLegendSizeS: Story = { - name: 'With Bold Text (S)', + name: 'Fieldset with size S legend', render: (args) => (
@@ -61,7 +63,7 @@ export const WithCustomLegendSizeS: Story = { }; export const WithCustomLegendSizeM: Story = { - name: 'With Custom Size (M)', + name: 'Fieldset with size M legend', render: (args) => (
@@ -72,7 +74,7 @@ export const WithCustomLegendSizeM: Story = { }; export const WithCustomLegendSizeL: Story = { - name: 'With Custom Size (L)', + name: 'Fieldset with size L legend', render: (args) => (
@@ -83,7 +85,7 @@ export const WithCustomLegendSizeL: Story = { }; export const WithCustomLegendSizeXL: Story = { - name: 'With Custom Size (XL)', + name: 'Fieldset with size XL legend', render: (args) => (
diff --git a/stories/Form Elements/FileUpload.stories.tsx b/stories/Form Elements/FileUpload.stories.tsx index 6c685017..1b6090c2 100644 --- a/stories/Form Elements/FileUpload.stories.tsx +++ b/stories/Form Elements/FileUpload.stories.tsx @@ -3,7 +3,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { FileUpload } from '#components/form-elements/file-upload/index.js'; const meta: Meta = { - title: 'Form Elements/FileUpload', + title: 'Form Elements/File upload', component: FileUpload, args: { id: 'input-example', @@ -19,20 +19,26 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Standard: Story = {}; -export const WithHintText: Story = { +export const Default: Story = { + name: 'File upload default', +}; + +export const WithHint: Story = { + name: 'File upload with hint', args: { hint: 'Your photo may be in your Pictures, Photos, Downloads or Desktop folder', }, }; export const WithError: Story = { + name: 'File upload with error message', args: { error: 'The selected file must be a JPG, BMP or TIF', }, }; -export const WithErrorAndHintText: Story = { +export const WithErrorAndHint: Story = { + name: 'File upload with error message and hint', args: { error: 'The selected file must be a JPG, BMP or TIF', hint: 'Your photo may be in your Pictures, Photos, Downloads or Desktop folder', @@ -40,24 +46,28 @@ export const WithErrorAndHintText: Story = { }; export const WithSmallPrimaryButton: Story = { + name: 'File upload with small primary button', args: { chooseFilesButtonClassList: ['nhsuk-button--small'], }, }; export const WithSecondaryButton: Story = { + name: 'File upload with secondary button', args: { chooseFilesButtonClassList: ['nhsuk-button--secondary'], }, }; export const WithSmallSecondaryButton: Story = { + name: 'File upload with small secondary button', args: { chooseFilesButtonClassList: ['nhsuk-button--small', 'nhsuk-button--secondary'], }, }; export const WithMultiple: Story = { + name: 'File upload with multiple', args: { label: 'Upload multiple files', multiple: true, @@ -70,6 +80,7 @@ export const WithMultiple: Story = { }; export const WithTranslations: Story = { + name: 'File upload with translations', args: { label: 'Upload multiple files', multiple: true, @@ -88,6 +99,7 @@ export const WithTranslations: Story = { }; export const Disabled: Story = { + name: 'File upload disabled', args: { disabled: true, }, diff --git a/stories/Form Elements/HintText.stories.tsx b/stories/Form Elements/HintText.stories.tsx index dc8c5550..30481601 100644 --- a/stories/Form Elements/HintText.stories.tsx +++ b/stories/Form Elements/HintText.stories.tsx @@ -3,13 +3,14 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { HintText } from '#components/form-elements/hint-text/index.js'; const meta: Meta = { - title: 'Form Elements/HintText', + title: 'Form Elements/Hint text', component: HintText, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { args: { children: 'Do not include personal information like your name, date of birth or NHS number', }, diff --git a/stories/Form Elements/Label.stories.tsx b/stories/Form Elements/Label.stories.tsx index a15744f4..e6762d59 100644 --- a/stories/Form Elements/Label.stories.tsx +++ b/stories/Form Elements/Label.stories.tsx @@ -14,38 +14,40 @@ const meta: Meta = { size: { control: 'select', options: [undefined, 's', 'm', 'l', 'xl'] }, }, }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Label default', args: { size: 'l', }, }; export const WithCustomSizeS: Story = { - name: 'With Bold Text (S)', + name: 'Label with size S text', args: { size: 's', }, }; export const WithCustomSizeM: Story = { - name: 'With Custom Size (M)', + name: 'Label with size M text', args: { size: 'm', }, }; export const WithCustomSizeL: Story = { - name: 'With Custom Size (L)', + name: 'Label with size L text', args: { size: 'l', }, }; export const WithCustomSizeXL: Story = { - name: 'With Custom Size (XL)', + name: 'Label with size XL text', args: { size: 'xl', }, diff --git a/stories/Form Elements/PasswordInput.stories.tsx b/stories/Form Elements/PasswordInput.stories.tsx index a652175a..88a00660 100644 --- a/stories/Form Elements/PasswordInput.stories.tsx +++ b/stories/Form Elements/PasswordInput.stories.tsx @@ -3,7 +3,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { PasswordInput } from '#components/form-elements/password-input/index.js'; const meta: Meta = { - title: 'Form Elements/PasswordInput', + title: 'Form Elements/Password input', component: PasswordInput, args: { id: 'input-example', @@ -19,20 +19,26 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Standard: Story = {}; -export const WithHintText: Story = { +export const Default: Story = { + name: 'Password input default', +}; + +export const WithHint: Story = { + name: 'Password input with hint', args: { hint: 'It probably has some letters, numbers and maybe even some symbols in it', }, }; export const WithError: Story = { + name: 'Password input with error message', args: { error: 'Enter a password', }, }; -export const WithErrorAndHintText: Story = { +export const WithErrorAndHint: Story = { + name: 'Password input with error message and hint', args: { error: 'Enter a password', hint: 'It probably has some letters, numbers and maybe even some symbols in it', @@ -40,6 +46,7 @@ export const WithErrorAndHintText: Story = { }; export const WithTranslations: Story = { + name: 'Password input with translations', args: { label: 'Cyfrinair', i18n: { diff --git a/stories/Form Elements/Radios.stories.tsx b/stories/Form Elements/Radios.stories.tsx index f9f81732..94989764 100644 --- a/stories/Form Elements/Radios.stories.tsx +++ b/stories/Form Elements/Radios.stories.tsx @@ -1,196 +1,214 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { Checkboxes } from '#components/form-elements/checkboxes/index.js'; import { Radios } from '#components/form-elements/radios/index.js'; -import { TextInput } from '#components/form-elements/text-input/index.js'; + +import { ExampleEmail, ExampleMobilePhoneNumber, ExamplePhoneNumber } from './TextInput.stories.js'; const meta: Meta = { title: 'Form Elements/Radios', component: Radios, args: { - legend: 'Have you changed your name?', + legend: 'How do you want to be contacted about this?', legendProps: { isPageHeading: true, size: 'l' }, - hint: 'This includes changing your last name or spelling your name differently', + name: 'example', }, }; + export default meta; type Story = StoryObj; -export const StandardRadios: Story = { +export const Default: Story = { + name: 'Radios default', args: { - idPrefix: 'standard', + legend: 'Are you 18 or over?', + name: 'default', }, render: (args) => ( Yes - - No + No + + ), +}; + +export const Small: Story = { + name: 'Radios small', + args: { + ...Default.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small', + small: true, + }, + render: (args) => ( + + + Yes + No ), }; -export const InlineRadios: Story = { +export const Inline: Story = { + name: 'Radios inline', args: { - idPrefix: 'inline', + legend: 'Are you 18 or over?', + name: 'inline', inline: true, }, render: (args) => ( Yes - - No - + No ), }; -export const RadiosWithCaption: Story = { +export const SmallInline: Story = { + name: 'Radios inline, small', + args: { + ...Inline.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-inline', + small: true, + }, + render: Inline.render, +}; + +export const WithCaption: Story = { + name: 'Radios with caption', args: { legend: ( <> - About you Have you changed your name? + About you Are you 18 or over? ), + name: 'with-caption', }, render: (args) => ( Yes - - No - + No ), }; -export const RadiosWithConditionalContent: Story = { +export const SmallWithCaption: Story = { + name: 'Radios with caption, small', args: { - legend: 'Impairment requirement', - hint: 'Select relevant options', - idPrefix: 'conditional', - }, - render: (args) => { - const impairmentsForm = ( - - Autism - - Developmental conditions (excluding autism) - - Dementia - Learning disability - Mental Health Condition - Physical disability - - Sensory disability - such as sight, hearing or verbal - - Long-term condition - - ); - - return ( - - - Patient requires an impairment to be added - - Patient would prefer not to say - - ); + ...WithCaption.args, + legend: ( + <> + About you Are you 18 or over? + + ), + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-caption', + small: true, }, + render: WithCaption.render, }; -export const RadiosWithADivider: Story = { +export const WithHint: Story = { + name: 'Radios with hint', args: { - legend: 'How do you want to sign in?', - hint: undefined, - idPrefix: 'divider', + hint: 'Select 1 option', + name: 'with-hint', }, render: (args) => ( - Use Government Gateway - Use NHS.UK login - or - Create an account + Email + Phone + Text message ), }; -export const RadiosWithHintsOnItems: Story = { +export const SmallWithHint: Story = { + name: 'Radios with hint, small', + args: { + ...WithHint.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-hint', + small: true, + }, + render: WithHint.render, +}; + +export const WithHintOnItems: Story = { + name: 'Radios with hints on items', args: { - legend: 'How do you want to sign in?', - hint: undefined, - idPrefix: 'hints', + legend: 'Do you have a mobile phone with signal?', + name: 'with-hint-on-items', }, render: (args) => ( - - Use Government Gateway + + Yes, I have a mobile phone with signal - - Use NHS.UK login + + No, I want to use my landline ), }; -export const SmallRadios: Story = { +export const SmallWithHintOnItems: Story = { + name: 'Radios with hints on items, small', args: { - ...StandardRadios.args, + ...WithHintOnItems.args, legendProps: { isPageHeading: true, size: 'm' }, - idPrefix: 'small', + name: 'small-with-hint-on-items', small: true, }, - render: StandardRadios.render, + render: WithHintOnItems.render, }; -export const SmallInlineRadios: Story = { +export const WithError: Story = { + name: 'Radios with error message', args: { - ...InlineRadios.args, - legendProps: { isPageHeading: true, size: 'm' }, - small: true, + error: 'Select how you want to be contacted', + name: 'with-error', }, - render: InlineRadios.render, + render: WithHint.render, }; -export const SmallRadiosWithConditionalContent: Story = { +export const SmallWithError: Story = { + name: 'Radios with error message, small', args: { - ...RadiosWithConditionalContent.args, + ...WithError.args, legendProps: { isPageHeading: true, size: 'm' }, - idPrefix: 'small-conditional', + name: 'small-with-error', small: true, }, - render: RadiosWithConditionalContent.render, + render: WithError.render, }; -export const SmallRadiosWithADivider: Story = { +export const WithHintAndError: Story = { + name: 'Radios with error message and hint', args: { - ...RadiosWithADivider.args, - legendProps: { isPageHeading: true, size: 'm' }, - idPrefix: 'small-divider', - small: true, + hint: 'Select 1 option', + error: 'Select how you want to be contacted', + name: 'with-hint-and-error', }, - render: RadiosWithADivider.render, + render: WithHint.render, }; -export const SmallRadiosWithHintsOnItems: Story = { +export const SmallWithHintAndError: Story = { + name: 'Radios with error message and hint, small', args: { - ...RadiosWithHintsOnItems.args, + ...WithHintAndError.args, legendProps: { isPageHeading: true, size: 'm' }, - idPrefix: 'small-hints', + name: 'small-with-hint-and-error', small: true, }, - render: RadiosWithHintsOnItems.render, + render: WithHintAndError.render, }; -export const DisabledRadios: Story = { +export const WithDisabledItems: Story = { + name: 'Radios with disabled items', args: { - idPrefix: 'disabled', + name: 'with-disabled-items', }, render: (args) => ( @@ -204,24 +222,133 @@ export const DisabledRadios: Story = { ), }; -export const RadiosWithError: Story = { +export const SmallWithDisabledItems: Story = { + name: 'Radios with disabled items, small', args: { - idPrefix: 'error', + ...WithDisabledItems.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-disabled-items', + small: true, }, - render: function RadiosWithErrorRender(args) { - const [error, setError] = useState('Select yes if you have changed your name'); - return ( - <> - - Yes - - No - - - setError(e.currentTarget.value)} /> - - ); + render: WithDisabledItems.render, +}; + +export const WithDivider: Story = { + name: 'Radios with divider', + args: { + legend: "Have you had all the vaccinations you're eligible for in the UK?", + hint: 'You would have got these vaccinations at school, from your GP surgery or another healthcare provider', + name: 'with-divider', }, + render: (args) => ( + + Yes + No + or + I do not know + + ), +}; + +export const SmallWithDivider: Story = { + name: 'Radios divider, small', + args: { + ...WithDivider.args, + legendProps: { isPageHeading: true, size: 'm' }, + name: 'small-with-divider', + small: true, + }, + render: WithDivider.render, +}; + +export const WithValues: Story = { + name: 'Radios with pre-checked value', + args: { + name: 'with-values', + }, + render: (args) => ( + + + Email + + Phone + Text message + + ), +}; + +export const WithConditionalContent: Story = { + name: 'Radios with conditional content', + args: { + hint: 'Select 1 option', + name: 'with-conditional-content', + }, + render: (args) => ( + + }> + Email + + }> + Phone + + }> + Text message + + + ), +}; + +export const WithConditionalContentValues: Story = { + name: 'Radios with conditional content and pre-checked value', + args: { + name: 'with-conditional-content-values', + }, + render: (args) => ( + + } defaultChecked> + Email + + }> + Phone + + }> + Text message + + + ), +}; + +export const WithConditionalContentError: Story = { + name: 'Radios with conditional content, error message', + args: { + hint: 'Select 1 option', + error: 'Select how you like to be contacted', + name: 'with-conditional-content-error', + }, + render: WithConditionalContent.render, +}; - name: 'Radios With Error (String)', +export const WithConditionalContentErrorNested: Story = { + name: 'Radios with conditional content, error message (nested)', + args: { + hint: 'Select 1 option', + name: 'with-conditional-content-error-nested', + }, + render: (args) => ( + + }> + Email + + } + defaultChecked + > + Phone + + }> + Text message + + + ), }; diff --git a/stories/Form Elements/Select.stories.tsx b/stories/Form Elements/Select.stories.tsx index ae74c37b..a8c7dbc4 100644 --- a/stories/Form Elements/Select.stories.tsx +++ b/stories/Form Elements/Select.stories.tsx @@ -25,10 +25,12 @@ const meta: Meta = { ), }; + export default meta; type Story = StoryObj; -export const Standard: Story = { +export const Default: Story = { + name: 'Select default', render: (args) => ( First name (A to Z) @@ -71,6 +77,7 @@ export const SelectWithDivider: Story = { }; export const SelectWithButton: Story = { + name: 'Select with button', args: { formGroupProps: { afterInput: ( @@ -83,6 +90,7 @@ export const SelectWithButton: Story = { }; export const SelectWithButtonAndError: Story = { + name: 'Select with button and error message', args: { error: 'Select a location', formGroupProps: { diff --git a/stories/Form Elements/TextInput.stories.tsx b/stories/Form Elements/TextInput.stories.tsx index 2aa614a5..c7e7cbc2 100644 --- a/stories/Form Elements/TextInput.stories.tsx +++ b/stories/Form Elements/TextInput.stories.tsx @@ -1,11 +1,12 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; import { Button } from '#components/form-elements/button/Button.js'; -import { TextInput } from '#components/form-elements/text-input/index.js'; +import { TextInput, type TextInputProps } from '#components/form-elements/text-input/index.js'; const meta: Meta = { - title: 'Form Elements/TextInput', + title: 'Form Elements/Text input', component: TextInput, + excludeStories: /^Example/, args: { id: 'input-example', name: 'example', @@ -20,8 +21,12 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Standard: Story = {}; -export const WithHintText: Story = { +export const Default: Story = { + name: 'Text input default', +}; + +export const WithHint: Story = { + name: 'Text input with hint', args: { label: 'What is your NHS number?', code: true, @@ -37,6 +42,7 @@ export const WithHintText: Story = { }; export const WithError: Story = { + name: 'Text input with error message', args: { label: 'What is your NHS number?', error: 'Enter NHS number', @@ -46,7 +52,8 @@ export const WithError: Story = { }, }; -export const WithErrorAndHintText: Story = { +export const WithErrorAndHint: Story = { + name: 'Text input with error message and hint', args: { label: 'What is your NHS number?', error: 'Enter NHS number', @@ -63,6 +70,7 @@ export const WithErrorAndHintText: Story = { }; export const WithWidthModifier: Story = { + name: 'Text input with width modifier', args: { label: 'What is your NHS number?', width: 10, @@ -79,6 +87,7 @@ export const WithWidthModifier: Story = { }; export const WithPrefix: Story = { + name: 'Text input with prefix', args: { label: 'Cost in pounds', prefix: '£', @@ -87,6 +96,7 @@ export const WithPrefix: Story = { }; export const WithSuffix: Story = { + name: 'Text input with suffix', args: { label: 'Weight in kilograms', suffix: 'kg', @@ -95,6 +105,7 @@ export const WithSuffix: Story = { }; export const WithPrefixAndSuffix: Story = { + name: 'Text input with prefix and suffix', args: { label: 'Cost per item, in pounds', prefix: '£', @@ -104,6 +115,7 @@ export const WithPrefixAndSuffix: Story = { }; export const WithPrefixAndSuffixAndError: Story = { + name: 'Text input with prefix and suffix and error message', args: { label: 'Cost per item, in pounds', error: 'Enter a cost per item, in pounds', @@ -114,6 +126,7 @@ export const WithPrefixAndSuffixAndError: Story = { }; export const WithButton: Story = { + name: 'Text input with button', args: { label: 'What is your NHS number?', width: 10, @@ -131,6 +144,7 @@ export const WithButton: Story = { }; export const WithButtonAndError: Story = { + name: 'Text input with button and error message', args: { label: 'What is your NHS number?', error: 'Enter NHS number', @@ -147,3 +161,33 @@ export const WithButtonAndError: Story = { }, }, }; + +export const ExampleEmail = (args: TextInputProps) => ( + +); + +export const ExamplePhoneNumber = (args: TextInputProps) => ( + +); + +export const ExampleMobilePhoneNumber = (args: TextInputProps) => ( + +); diff --git a/stories/Form Elements/Textarea.stories.tsx b/stories/Form Elements/Textarea.stories.tsx index d038be2f..9135c579 100644 --- a/stories/Form Elements/Textarea.stories.tsx +++ b/stories/Form Elements/Textarea.stories.tsx @@ -1,7 +1,5 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { useState } from 'react'; -import { TextInput } from '#components/form-elements/text-input/index.js'; import { Textarea } from '#components/form-elements/textarea/index.js'; const meta: Meta = { @@ -10,33 +8,44 @@ const meta: Meta = { args: { label: 'Can you provide more detail?', labelProps: { isPageHeading: true, size: 'l' }, - hint: 'Do not include personal information, like your name, date of birth or NHS number', id: 'example', name: 'example', rows: 5, }, }; + export default meta; type Story = StoryObj; -export const Standard: Story = {}; +export const Default: Story = { + name: 'Textarea default', +}; -export const TextareaWithAutoCompleteAttribute: Story = { +export const WithHint: Story = { + name: 'Textarea with hint', args: { - autoComplete: 'street-address', + hint: 'Do not include personal information, like your name, date of birth or NHS number', }, }; -export const TextareaWithError: Story = { - render: function TextareaWithErrorRender(args) { - const [error, setError] = useState('You must provide an explanation'); - return ( - <> -