@tpzdsp/next-toolkit 2.5.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +21 -12
- package/src/assets/styles/globals.css +75 -95
- package/src/assets/styles/ol.css +57 -16
- package/src/assets/styles/utils.css +80 -0
- package/src/components/Accordion/Accordion.css +85 -0
- package/src/components/Accordion/Accordion.stories.tsx +264 -0
- package/src/components/Accordion/Accordion.tsx +98 -0
- package/src/components/Alert/Alert.css +129 -0
- package/src/components/Alert/Alert.stories.tsx +291 -0
- package/src/components/Alert/Alert.tsx +76 -0
- package/src/components/BackLink/BackLink.css +12 -0
- package/src/components/BackLink/BackLink.stories.tsx +52 -0
- package/src/components/BackLink/BackLink.tsx +44 -0
- package/src/components/BackToTop/BackToTop.css +23 -0
- package/src/components/BackToTop/BackToTop.stories.tsx +41 -0
- package/src/components/BackToTop/BackToTop.tsx +48 -0
- package/src/components/Breadcrumb/Breadcrumb.css +42 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +94 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +135 -0
- package/src/components/Button/Button.css +229 -0
- package/src/components/Button/Button.stories.tsx +140 -20
- package/src/components/Button/Button.tsx +53 -28
- package/src/components/Button/ButtonSkeleton.stories.tsx +50 -0
- package/src/components/Button/ButtonSkeleton.tsx +14 -0
- package/src/components/ButtonGroup/ButtonGroup.css +52 -0
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +156 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +50 -0
- package/src/components/ButtonLink/ButtonLink.css +114 -0
- package/src/components/ButtonLink/ButtonLink.stories.tsx +38 -51
- package/src/components/ButtonLink/ButtonLink.tsx +54 -23
- package/src/components/Card/Card.css +13 -0
- package/src/components/Card/Card.stories.tsx +98 -57
- package/src/components/Card/Card.tsx +4 -13
- package/src/components/CardGroup/CardGroup.css +10 -0
- package/src/components/CardGroup/CardGroup.stories.tsx +81 -0
- package/src/components/CardGroup/CardGroup.tsx +9 -0
- package/src/components/Checkbox/Checkbox.css +49 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +86 -0
- package/src/components/Checkbox/Checkbox.tsx +32 -0
- package/src/components/Chip/Chip.css +54 -0
- package/src/components/Chip/Chip.stories.tsx +94 -0
- package/src/components/Chip/Chip.tsx +34 -0
- package/src/components/CookieBanner/CookieBanner.css +19 -0
- package/src/components/CookieBanner/CookieBanner.stories.tsx +52 -0
- package/src/components/CookieBanner/CookieBanner.tsx +55 -0
- package/src/components/CopyButton/CopyButton.stories.tsx +28 -0
- package/src/components/CopyButton/CopyButton.tsx +65 -0
- package/src/components/DateInput/DateInput.css +13 -0
- package/src/components/DateInput/DateInput.stories.tsx +131 -0
- package/src/components/DateInput/DateInput.test.tsx +21 -0
- package/src/components/DateInput/DateInput.tsx +98 -0
- package/src/components/Details/Details.css +28 -0
- package/src/components/Details/Details.stories.tsx +48 -0
- package/src/components/Details/Details.tsx +41 -0
- package/src/components/Dropdown/Dropdown.css +81 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +131 -0
- package/src/components/Dropdown/Dropdown.tsx +69 -0
- package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +4 -39
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +7 -4
- package/src/components/ErrorBoundary/ErrorFallback.tsx +32 -66
- package/src/components/ErrorModal/ErrorModal.css +32 -0
- package/src/components/ErrorModal/ErrorModal.stories.tsx +101 -0
- package/src/components/ErrorModal/ErrorModal.tsx +120 -0
- package/src/components/ErrorText/ErrorText.css +22 -0
- package/src/components/ErrorText/ErrorText.stories.tsx +42 -27
- package/src/components/ErrorText/ErrorText.tsx +10 -8
- package/src/components/ExternalLink/ExternalLink.css +12 -0
- package/src/components/ExternalLink/ExternalLink.stories.tsx +43 -0
- package/src/components/ExternalLink/ExternalLink.tsx +43 -0
- package/src/components/Fieldset/Fieldset.css +103 -0
- package/src/components/Fieldset/Fieldset.stories.tsx +304 -0
- package/src/components/Fieldset/Fieldset.tsx +38 -0
- package/src/components/FileUpload/FileUpload.css +85 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +106 -0
- package/src/components/FileUpload/FileUpload.tsx +99 -0
- package/src/components/{layout/footer → Footer}/Copyright.tsx +4 -2
- package/src/components/Footer/Footer.stories.tsx +24 -0
- package/src/components/Footer/Footer.tsx +41 -0
- package/src/components/Footer/Licence.tsx +18 -0
- package/src/components/{layout/footer → Footer}/MetaLinks.tsx +8 -3
- package/src/components/Footer/Small/SmallFooter.tsx +43 -42
- package/src/components/Form/FormGroup.css +18 -0
- package/src/components/Form/FormGroup.stories.tsx +87 -0
- package/src/components/Form/FormGroup.tsx +16 -0
- package/src/components/{layout/header → Header}/Header.stories.tsx +26 -12
- package/src/components/Header/Header.test.tsx +87 -0
- package/src/components/Header/Header.tsx +303 -0
- package/src/components/Header/HeaderAppContent.tsx +120 -0
- package/src/components/Heading/Heading.css +40 -0
- package/src/components/Heading/Heading.stories.tsx +15 -5
- package/src/components/Heading/Heading.tsx +21 -44
- package/src/components/Heading/HeadingSkeleton.stories.tsx +30 -0
- package/src/components/Heading/HeadingSkeleton.tsx +14 -0
- package/src/components/Hero/Hero.css +19 -0
- package/src/components/Hero/Hero.stories.tsx +41 -0
- package/src/components/Hero/Hero.tsx +14 -0
- package/src/components/HintText/HintText.css +5 -0
- package/src/components/HintText/HintText.stories.tsx +29 -0
- package/src/components/HintText/HintText.tsx +13 -0
- package/src/components/InfoBox/InfoBox.css +38 -0
- package/src/components/InfoBox/InfoBox.stories.tsx +51 -403
- package/src/components/InfoBox/InfoBox.tsx +31 -158
- package/src/components/Input/Input.css +77 -0
- package/src/components/Input/Input.stories.tsx +123 -0
- package/src/components/Input/Input.tsx +74 -0
- package/src/components/InputGroup/InputGroup.css +140 -0
- package/src/components/InputGroup/InputGroup.stories.tsx +125 -0
- package/src/components/InputGroup/InputGroup.tsx +47 -0
- package/src/components/InsetText/InsetText.css +5 -0
- package/src/components/InsetText/InsetText.stories.tsx +30 -0
- package/src/components/InsetText/InsetText.tsx +9 -0
- package/src/components/Keyboard/Keyboard.css +9 -0
- package/src/components/Keyboard/Keyboard.stories.tsx +63 -0
- package/src/components/Keyboard/Keyboard.tsx +30 -0
- package/src/components/Label/Label.css +27 -0
- package/src/components/Label/Label.stories.tsx +40 -0
- package/src/components/Label/Label.tsx +17 -0
- package/src/components/LayerSwitcher/LayerSwitcher.css +59 -0
- package/src/components/LayerSwitcher/LayerSwitcher.stories.tsx +112 -0
- package/src/components/LayerSwitcher/LayerSwitcher.tsx +61 -0
- package/src/components/Layout/401/401.stories.tsx +23 -0
- package/src/components/Layout/401/401.tsx +1 -0
- package/src/components/Layout/403/403.stories.tsx +23 -0
- package/src/components/Layout/403/403.tsx +21 -0
- package/src/components/Layout/404/404.stories.tsx +23 -0
- package/src/components/Layout/404/404.tsx +28 -0
- package/src/components/Layout/500/500.stories.tsx +56 -0
- package/src/components/Layout/500/500.tsx +63 -0
- package/src/components/Layout/ButtonGroup/ButtonGroup.css +52 -0
- package/src/components/Layout/ButtonGroup/ButtonGroup.stories.tsx +156 -0
- package/src/components/Layout/ButtonGroup/ButtonGroup.tsx +50 -0
- package/src/components/Layout/HeadingGroup/HeadingGroup.css +21 -0
- package/src/components/Layout/HeadingGroup/HeadingGroup.stories.tsx +84 -0
- package/src/components/Layout/HeadingGroup/HeadingGroup.tsx +9 -0
- package/src/components/Layout/MainWrapper/MainWrapper.css +12 -0
- package/src/components/Layout/MainWrapper/MainWrapper.stories.tsx +36 -0
- package/src/components/Layout/MainWrapper/MainWrapper.tsx +14 -0
- package/src/components/Layout/PageShell/PageShell.css +42 -0
- package/src/components/Layout/PageShell/PageShell.stories.tsx +108 -0
- package/src/components/Layout/PageShell/PageShell.tsx +31 -0
- package/src/components/Layout/ParagraphGroup/ParagraphGroup.css +5 -0
- package/src/components/Layout/ParagraphGroup/ParagraphGroup.stories.tsx +34 -0
- package/src/components/Layout/ParagraphGroup/ParagraphGroup.tsx +9 -0
- package/src/components/Layout/Stack/Stack.css +29 -0
- package/src/components/Layout/Stack/Stack.stories.tsx +83 -0
- package/src/components/Layout/Stack/Stack.tsx +19 -0
- package/src/components/Layout/WidthContainer/WidthContainer.css +17 -0
- package/src/components/Layout/WidthContainer/WidthContainer.stories.tsx +59 -0
- package/src/components/Layout/WidthContainer/WidthContainer.tsx +13 -0
- package/src/components/Legend/Legend.css +95 -0
- package/src/components/Legend/Legend.stories.tsx +81 -0
- package/src/components/Legend/Legend.tsx +32 -0
- package/src/components/Legend/LegendDot.tsx +15 -0
- package/src/components/Legend/LegendPanel.stories.tsx +101 -0
- package/src/components/Legend/LegendPanel.tsx +68 -0
- package/src/components/Legend/LegendSkeleton.stories.tsx +87 -0
- package/src/components/Legend/LegendSkeleton.tsx +73 -0
- package/src/components/LegendSelect/LegendSelect.css +8 -0
- package/src/components/LegendSelect/LegendSelect.stories.tsx +80 -0
- package/src/components/LegendSelect/LegendSelect.tsx +26 -0
- package/src/components/Link/Link.css +98 -0
- package/src/components/Link/Link.stories.tsx +70 -0
- package/src/components/Link/Link.tsx +52 -0
- package/src/components/LinkButton/LinkButton.css +67 -0
- package/src/components/LinkButton/LinkButton.stories.tsx +90 -42
- package/src/components/LinkButton/LinkButton.tsx +50 -63
- package/src/components/LinkList/LinkList.css +28 -0
- package/src/components/LinkList/LinkList.stories.tsx +46 -0
- package/src/components/LinkList/LinkList.tsx +23 -0
- package/src/components/List/Li.tsx +9 -0
- package/src/components/List/List.css +9 -0
- package/src/components/List/OrderedList.stories.tsx +36 -0
- package/src/components/List/OrderedList.tsx +9 -0
- package/src/components/List/UnorderedList.stories.tsx +36 -0
- package/src/components/List/UnorderedList.tsx +9 -0
- package/src/components/LoadingBox/LoadingBox.css +27 -0
- package/src/components/LoadingBox/LoadingBox.stories.tsx +66 -0
- package/src/components/LoadingBox/LoadingBox.tsx +40 -0
- package/src/components/Modal/Modal.css +114 -0
- package/src/components/Modal/Modal.stories.tsx +41 -160
- package/src/components/Modal/Modal.tsx +132 -75
- package/src/components/NoScriptBanner/NoScriptBanner.css +17 -0
- package/src/components/NoScriptBanner/NoScriptBanner.stories.tsx +46 -0
- package/src/components/NoScriptBanner/NoScriptBanner.tsx +26 -0
- package/src/components/Paragraph/Paragraph.css +22 -0
- package/src/components/Paragraph/Paragraph.stories.tsx +1 -42
- package/src/components/Paragraph/Paragraph.tsx +4 -3
- package/src/components/Paragraph/ParagraphSkeleton.stories.tsx +44 -0
- package/src/components/Paragraph/ParagraphSkeleton.tsx +33 -0
- package/src/components/PasswordInput/PasswordInput.css +3 -0
- package/src/components/PasswordInput/PasswordInput.stories.tsx +47 -0
- package/src/components/PasswordInput/PasswordInput.tsx +45 -0
- package/src/components/PhaseBanner/PhaseBanner.css +12 -0
- package/src/components/PhaseBanner/PhaseBanner.stories.tsx +61 -0
- package/src/components/PhaseBanner/PhaseBanner.tsx +17 -0
- package/src/components/Popover/Popover.css +43 -0
- package/src/components/Popover/Popover.stories.tsx +102 -0
- package/src/components/Popover/Popover.tsx +40 -0
- package/src/components/Radio/Radio.css +69 -0
- package/src/components/Radio/Radio.stories.tsx +169 -0
- package/src/components/Radio/Radio.tsx +56 -0
- package/src/components/Resizable/Resizable.css +86 -0
- package/src/components/Resizable/Resizable.stories.tsx +123 -0
- package/src/components/Resizable/Resizable.tsx +86 -0
- package/src/components/ScrollArea/ScrollArea.css +9 -0
- package/src/components/ScrollArea/ScrollArea.stories.tsx +42 -0
- package/src/components/ScrollArea/ScrollArea.tsx +11 -0
- package/src/components/SearchBox/SearchBox.stories.tsx +88 -0
- package/src/components/SearchBox/SearchBox.tsx +36 -0
- package/src/components/Select/AsyncSelect.stories.tsx +84 -0
- package/src/components/Select/AsyncSelect.tsx +41 -0
- package/src/components/Select/Select.css +194 -0
- package/src/components/Select/Select.stories.tsx +162 -0
- package/src/components/Select/Select.test.tsx +36 -0
- package/src/components/Select/Select.tsx +291 -0
- package/src/components/Select/SelectSkeleton.stories.tsx +51 -0
- package/src/components/Select/SelectSkeleton.tsx +23 -0
- package/src/components/Separator/Separator.css +41 -0
- package/src/components/Separator/Separator.stories.tsx +63 -0
- package/src/components/Separator/Separator.tsx +42 -0
- package/src/components/Sidebar/ResizableSidebar.tsx +118 -0
- package/src/components/Sidebar/Sidebar.css +203 -0
- package/src/components/Sidebar/Sidebar.stories.tsx +438 -0
- package/src/components/Sidebar/Sidebar.tsx +225 -0
- package/src/components/Skeleton/Skeleton.css +16 -0
- package/src/components/Skeleton/Skeleton.stories.tsx +39 -0
- package/src/components/Skeleton/Skeleton.tsx +12 -0
- package/src/components/SkipLink/SkipLink.css +16 -0
- package/src/components/SkipLink/SkipLink.stories.tsx +40 -0
- package/src/components/SkipLink/SkipLink.tsx +15 -0
- package/src/components/Spinner/Spinner.css +30 -0
- package/src/components/Spinner/Spinner.stories.tsx +36 -0
- package/src/components/Spinner/Spinner.tsx +45 -0
- package/src/components/StartButton/StartButton.css +13 -0
- package/src/components/StartButton/StartButton.stories.tsx +68 -0
- package/src/components/StartButton/StartButton.tsx +50 -0
- package/src/components/SubmitButton/SubmitButton.stories.tsx +31 -0
- package/src/components/SubmitButton/SubmitButton.tsx +14 -0
- package/src/components/SummaryList/SummaryList.css +71 -0
- package/src/components/SummaryList/SummaryList.stories.tsx +211 -0
- package/src/components/SummaryList/SummaryList.tsx +41 -0
- package/src/components/SummaryList/SummaryListSkeleton.stories.tsx +67 -0
- package/src/components/SummaryList/SummaryListSkeleton.tsx +40 -0
- package/src/components/Tabs/Tabs.css +84 -0
- package/src/components/Tabs/Tabs.stories.tsx +133 -0
- package/src/components/Tabs/Tabs.tsx +66 -0
- package/src/components/Tag/Tag.css +8 -0
- package/src/components/Tag/Tag.stories.tsx +96 -0
- package/src/components/Tag/Tag.tsx +34 -0
- package/src/components/Textarea/Textarea.css +38 -0
- package/src/components/Textarea/Textarea.stories.tsx +131 -0
- package/src/components/Textarea/Textarea.tsx +50 -0
- package/src/components/TextareaCount/TextareaCount.css +17 -0
- package/src/components/TextareaCount/TextareaCount.stories.tsx +131 -0
- package/src/components/TextareaCount/TextareaCount.tsx +108 -0
- package/src/components/Tooltip/Tooltip.css +43 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +110 -0
- package/src/components/Tooltip/Tooltip.tsx +53 -0
- package/src/components/WarningText/WarningText.css +20 -0
- package/src/components/WarningText/WarningText.stories.tsx +29 -0
- package/src/components/WarningText/WarningText.tsx +17 -0
- package/src/components/index.ts +342 -61
- package/src/errors/errorDisplay.ts +70 -0
- package/src/errors/index.ts +1 -0
- package/src/http/constants.ts +1 -0
- package/src/http/fetch.ts +7 -3
- package/src/map/LayerSwitcherPanel.tsx +2 -173
- package/src/map/LegendShim.tsx +2 -0
- package/src/map/MapComponent.tsx +7 -17
- package/src/map/MapControlsOverlay.tsx +68 -175
- package/src/map/Popup.tsx +71 -24
- package/src/map/geocoder/Geocoder.test.tsx +46 -3
- package/src/map/geocoder/Geocoder.tsx +155 -278
- package/src/map/index.ts +1 -1
- package/src/types/utils.ts +0 -10
- package/src/utils/components.ts +96 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.ts +0 -12
- package/src/assets/images/defra-logo.svg +0 -51
- package/src/assets/images/ea-logo.svg +0 -58
- package/src/assets/images/ogl.svg +0 -1
- package/src/components/Button/Button.test.tsx +0 -53
- package/src/components/ButtonLink/ButtonLink.test.tsx +0 -154
- package/src/components/Card/Card.test.tsx +0 -51
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +0 -75
- package/src/components/ErrorBoundary/ErrorFallback.test.tsx +0 -107
- package/src/components/ErrorText/ErrorText.test.tsx +0 -33
- package/src/components/Heading/Heading.test.tsx +0 -22
- package/src/components/Hint/Hint.stories.tsx +0 -58
- package/src/components/Hint/Hint.test.tsx +0 -33
- package/src/components/Hint/Hint.tsx +0 -12
- package/src/components/InfoBox/InfoBox.test.tsx +0 -330
- package/src/components/InfoBox/types.ts +0 -6
- package/src/components/LinkButton/LinkButton.test.tsx +0 -177
- package/src/components/Modal/Modal.test.tsx +0 -246
- package/src/components/NotificationBanner/NotificationBanner.stories.tsx +0 -45
- package/src/components/NotificationBanner/NotificationBanner.test.tsx +0 -58
- package/src/components/NotificationBanner/NotificationBanner.tsx +0 -45
- package/src/components/Paragraph/Paragraph.test.tsx +0 -10
- package/src/components/SlidingPanel/SlidingPanel.stories.tsx +0 -129
- package/src/components/SlidingPanel/SlidingPanel.test.tsx +0 -83
- package/src/components/SlidingPanel/SlidingPanel.tsx +0 -258
- package/src/components/accordion/Accordion.stories.tsx +0 -234
- package/src/components/accordion/Accordion.test.tsx +0 -192
- package/src/components/accordion/Accordion.tsx +0 -73
- package/src/components/backToTop/BackToTop.stories.tsx +0 -407
- package/src/components/backToTop/BackToTop.test.tsx +0 -57
- package/src/components/backToTop/BackToTop.tsx +0 -134
- package/src/components/chip/Chip.stories.tsx +0 -61
- package/src/components/chip/Chip.test.tsx +0 -31
- package/src/components/chip/Chip.tsx +0 -23
- package/src/components/container/Container.tsx +0 -42
- package/src/components/cookieBanner/CookieBanner.stories.tsx +0 -257
- package/src/components/cookieBanner/CookieBanner.test.tsx +0 -68
- package/src/components/cookieBanner/CookieBanner.tsx +0 -73
- package/src/components/divider/RuleDivider.stories.tsx +0 -254
- package/src/components/divider/RuleDivider.test.tsx +0 -164
- package/src/components/divider/RuleDivider.tsx +0 -23
- package/src/components/dropdown/DropdownMenu.test.tsx +0 -211
- package/src/components/dropdown/DropdownMenu.tsx +0 -108
- package/src/components/dropdown/useDropdownMenu.ts +0 -249
- package/src/components/form/Input.stories.tsx +0 -435
- package/src/components/form/Input.test.tsx +0 -206
- package/src/components/form/Input.tsx +0 -23
- package/src/components/form/TextArea.stories.tsx +0 -464
- package/src/components/form/TextArea.test.tsx +0 -232
- package/src/components/form/TextArea.tsx +0 -23
- package/src/components/layout/footer/Footer.tsx +0 -26
- package/src/components/layout/footer/Licence.tsx +0 -19
- package/src/components/layout/header/Header.test.tsx +0 -36
- package/src/components/layout/header/Header.tsx +0 -90
- package/src/components/layout/header/HeaderAuthClient.test.tsx +0 -45
- package/src/components/layout/header/HeaderAuthClient.tsx +0 -40
- package/src/components/layout/header/HeaderNavClient.test.tsx +0 -44
- package/src/components/layout/header/HeaderNavClient.tsx +0 -67
- package/src/components/link/ExternalLink.test.tsx +0 -102
- package/src/components/link/ExternalLink.tsx +0 -29
- package/src/components/link/Link.tsx +0 -26
- package/src/components/select/Select.stories.tsx +0 -336
- package/src/components/select/Select.test.tsx +0 -474
- package/src/components/select/Select.tsx +0 -125
- package/src/components/select/SelectSkeleton.stories.tsx +0 -194
- package/src/components/select/SelectSkeleton.test.tsx +0 -104
- package/src/components/select/SelectSkeleton.tsx +0 -23
- package/src/components/select/common.ts +0 -3
- package/src/components/select/index.ts +0 -18
- package/src/components/skipLink/SkipLink.stories.tsx +0 -346
- package/src/components/skipLink/SkipLink.test.tsx +0 -22
- package/src/components/skipLink/SkipLink.tsx +0 -49
- package/src/map/LegendPanel.tsx +0 -85
- /package/src/{components → assets}/images/DefraLogo.tsx +0 -0
- /package/src/{components → assets}/images/EaLogo.tsx +0 -0
- /package/src/{components → assets}/images/OglLogo.tsx +0 -0
- /package/src/components/{googleAnalytics → GoogleAnalytics}/GlobalVars.tsx +0 -0
- /package/src/components/{googleAnalytics → GoogleAnalytics}/GoogleAnalytics.tsx +0 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { InputGroup, InputPrefix, InputSuffix, InputSwatch } from './InputGroup';
|
|
4
|
+
import { ErrorText } from '../ErrorText/ErrorText';
|
|
5
|
+
import { FormGroup } from '../Form/FormGroup';
|
|
6
|
+
import { Input } from '../Input/Input';
|
|
7
|
+
import { Label } from '../Label/Label';
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof InputGroup> = {
|
|
10
|
+
title: 'Components/InputGroup',
|
|
11
|
+
component: InputGroup,
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
'Layout wrapper for attaching addons to an input. Use `InputPrefix` and `InputSuffix` as positioning containers, and `InputSwatch` inside them for decorative GDS-style labels (currency, units). For interactive addons such as buttons, place them directly inside `InputPrefix` or `InputSuffix`, they receive gap spacing automatically.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof InputGroup>;
|
|
26
|
+
|
|
27
|
+
export const WithPrefix: Story = {
|
|
28
|
+
render: () => (
|
|
29
|
+
<div>
|
|
30
|
+
<Label htmlFor="prefix-input">Cost per unit</Label>
|
|
31
|
+
|
|
32
|
+
<InputGroup>
|
|
33
|
+
<InputPrefix>
|
|
34
|
+
<InputSwatch>£</InputSwatch>
|
|
35
|
+
</InputPrefix>
|
|
36
|
+
|
|
37
|
+
<Input id="prefix-input" inputMode="numeric" size="short" />
|
|
38
|
+
</InputGroup>
|
|
39
|
+
</div>
|
|
40
|
+
),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const WithSuffix: Story = {
|
|
44
|
+
render: () => (
|
|
45
|
+
<div>
|
|
46
|
+
<Label htmlFor="suffix-input">Weight</Label>
|
|
47
|
+
|
|
48
|
+
<InputGroup>
|
|
49
|
+
<Input id="suffix-input" inputMode="numeric" size="short" />
|
|
50
|
+
|
|
51
|
+
<InputSuffix>
|
|
52
|
+
<InputSwatch>kg</InputSwatch>
|
|
53
|
+
</InputSuffix>
|
|
54
|
+
</InputGroup>
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const WithPrefixAndSuffix: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<div>
|
|
62
|
+
<Label htmlFor="both-input">Cost per kilogram</Label>
|
|
63
|
+
|
|
64
|
+
<InputGroup>
|
|
65
|
+
<InputPrefix>
|
|
66
|
+
<InputSwatch>£</InputSwatch>
|
|
67
|
+
</InputPrefix>
|
|
68
|
+
|
|
69
|
+
<Input id="both-input" inputMode="numeric" size="short" />
|
|
70
|
+
|
|
71
|
+
<InputSuffix>
|
|
72
|
+
<InputSwatch>per kg</InputSwatch>
|
|
73
|
+
</InputSuffix>
|
|
74
|
+
</InputGroup>
|
|
75
|
+
</div>
|
|
76
|
+
),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const WithPrefixAndSuffixDisabled: Story = {
|
|
80
|
+
render: () => (
|
|
81
|
+
<FormGroup>
|
|
82
|
+
<Label htmlFor="both-input-disabled">Cost per kilogram</Label>
|
|
83
|
+
|
|
84
|
+
<InputGroup>
|
|
85
|
+
<InputPrefix>
|
|
86
|
+
<InputSwatch>£</InputSwatch>
|
|
87
|
+
</InputPrefix>
|
|
88
|
+
|
|
89
|
+
<Input id="both-input-disabled" inputMode="numeric" size="short" disabled />
|
|
90
|
+
|
|
91
|
+
<InputSuffix>
|
|
92
|
+
<InputSwatch>per kg</InputSwatch>
|
|
93
|
+
</InputSuffix>
|
|
94
|
+
</InputGroup>
|
|
95
|
+
</FormGroup>
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const WithPrefixAndSuffixInvalid: Story = {
|
|
100
|
+
render: () => (
|
|
101
|
+
<FormGroup invalid>
|
|
102
|
+
<Label htmlFor="both-input-invalid">Cost per kilogram</Label>
|
|
103
|
+
|
|
104
|
+
<ErrorText id="cost-error">Enter a cost per kilogram</ErrorText>
|
|
105
|
+
|
|
106
|
+
<InputGroup>
|
|
107
|
+
<InputPrefix>
|
|
108
|
+
<InputSwatch>£</InputSwatch>
|
|
109
|
+
</InputPrefix>
|
|
110
|
+
|
|
111
|
+
<Input
|
|
112
|
+
id="both-input-invalid"
|
|
113
|
+
inputMode="numeric"
|
|
114
|
+
size="short"
|
|
115
|
+
aria-invalid
|
|
116
|
+
aria-describedby="cost-error"
|
|
117
|
+
/>
|
|
118
|
+
|
|
119
|
+
<InputSuffix>
|
|
120
|
+
<InputSwatch>per kg</InputSwatch>
|
|
121
|
+
</InputSuffix>
|
|
122
|
+
</InputGroup>
|
|
123
|
+
</FormGroup>
|
|
124
|
+
),
|
|
125
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
|
|
5
|
+
export type InputGroupDirection = 'horizontal' | 'vertical';
|
|
6
|
+
|
|
7
|
+
export type InputGroupProps = ComponentProps<'div'> & {
|
|
8
|
+
direction?: InputGroupDirection;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const InputGroup = ({ className, direction = 'horizontal', ...props }: InputGroupProps) => (
|
|
12
|
+
<div
|
|
13
|
+
data-slot="input-group"
|
|
14
|
+
data-direction={direction}
|
|
15
|
+
className={cn('cn-input-group', className)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export type InputToolbarProps = ComponentProps<'div'>;
|
|
21
|
+
|
|
22
|
+
export const InputToolbar = ({ className, ...props }: InputToolbarProps) => (
|
|
23
|
+
<div data-slot="input-toolbar" className={cn('cn-input-toolbar', className)} {...props} />
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export type InputPrefixProps = ComponentProps<'div'>;
|
|
27
|
+
|
|
28
|
+
export const InputPrefix = ({ className, ...props }: InputPrefixProps) => (
|
|
29
|
+
<div data-slot="input-prefix" className={cn('cn-input-prefix', className)} {...props} />
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export type InputSuffixProps = ComponentProps<'div'>;
|
|
33
|
+
|
|
34
|
+
export const InputSuffix = ({ className, ...props }: InputSuffixProps) => (
|
|
35
|
+
<div data-slot="input-suffix" className={cn('cn-input-suffix', className)} {...props} />
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export type InputSwatchProps = ComponentProps<'div'>;
|
|
39
|
+
|
|
40
|
+
export const InputSwatch = ({ className, ...props }: InputSwatchProps) => (
|
|
41
|
+
<div
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
data-slot="input-swatch"
|
|
44
|
+
className={cn('cn-input-swatch', className)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { InsetText } from './InsetText';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof InsetText> = {
|
|
6
|
+
title: 'Components/InsetText',
|
|
7
|
+
component: InsetText,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'Use the inset text component to differentiate a block of text from the content that surrounds it.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
children: { control: 'text' },
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
children:
|
|
22
|
+
'It can take up to 8 weeks to register a lasting power of attorney if there are no mistakes in the application. ',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
|
|
28
|
+
type Story = StoryObj<typeof InsetText>;
|
|
29
|
+
|
|
30
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
|
|
5
|
+
export type InsetTextProps = ComponentProps<'div'>;
|
|
6
|
+
|
|
7
|
+
export const InsetText = ({ className, ...props }: InsetTextProps) => {
|
|
8
|
+
return <div className={cn('cn-inset-text', className)} {...props} />;
|
|
9
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { Keyboard, KeyboardGroup } from './Keyboard';
|
|
4
|
+
import { Paragraph } from '../Paragraph/Paragraph';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Keyboard> = {
|
|
7
|
+
title: 'Components/Keyboard',
|
|
8
|
+
component: Keyboard,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component: 'Used to display textual user input from keyboard.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
argTypes: {
|
|
18
|
+
children: { control: 'text' },
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
children: 'Ctrl + C',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
|
|
27
|
+
type Story = StoryObj<typeof Keyboard>;
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {};
|
|
30
|
+
|
|
31
|
+
export const Symbols: Story = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<KeyboardGroup>
|
|
34
|
+
<Keyboard>⌘</Keyboard>
|
|
35
|
+
|
|
36
|
+
<Keyboard>⇧</Keyboard>
|
|
37
|
+
|
|
38
|
+
<Keyboard>⌥</Keyboard>
|
|
39
|
+
|
|
40
|
+
<Keyboard>⌃</Keyboard>
|
|
41
|
+
</KeyboardGroup>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Grouped: Story = {
|
|
46
|
+
render: () => (
|
|
47
|
+
<Paragraph>
|
|
48
|
+
Use{' '}
|
|
49
|
+
<KeyboardGroup>
|
|
50
|
+
<Keyboard>Ctrl + C</Keyboard>
|
|
51
|
+
|
|
52
|
+
<Keyboard>Ctrl + V</Keyboard>
|
|
53
|
+
</KeyboardGroup>{' '}
|
|
54
|
+
to copy and paste text on Windows, or{' '}
|
|
55
|
+
<KeyboardGroup>
|
|
56
|
+
<Keyboard>⌘ + C</Keyboard>
|
|
57
|
+
|
|
58
|
+
<Keyboard>⌘ + V</Keyboard>
|
|
59
|
+
</KeyboardGroup>{' '}
|
|
60
|
+
on macOS.
|
|
61
|
+
</Paragraph>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
|
|
5
|
+
export type KeyboardGroupProps = ComponentProps<'kbd'>;
|
|
6
|
+
|
|
7
|
+
export const KeyboardGroup = ({ className, ...props }: KeyboardGroupProps) => {
|
|
8
|
+
return (
|
|
9
|
+
<kbd
|
|
10
|
+
data-slot="kbd-group"
|
|
11
|
+
className={cn('cn-kbd-group inline-flex items-center', className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type KeyboardProps = ComponentProps<'kbd'>;
|
|
18
|
+
|
|
19
|
+
export const Keyboard = ({ className, ...props }: KeyboardProps) => {
|
|
20
|
+
return (
|
|
21
|
+
<kbd
|
|
22
|
+
data-slot="kbd"
|
|
23
|
+
className={cn(
|
|
24
|
+
'cn-kbd pointer-events-none inline-flex items-center justify-center select-none',
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/*
|
|
3
|
+
MARK: Label
|
|
4
|
+
GDS labels are block elements, bold, in the primary text colour.
|
|
5
|
+
No external margin. Spacing is the consumer's job.
|
|
6
|
+
*/
|
|
7
|
+
.cn-label {
|
|
8
|
+
@apply block text-lg text-body cursor-pointer;
|
|
9
|
+
|
|
10
|
+
/* Three ways a label can end up describing a disabled control:
|
|
11
|
+
- ~ (a later sibling) covers FormGroup's own order, label then
|
|
12
|
+
control.
|
|
13
|
+
- :has(:is(...)) > & (an ancestor with a disabled child) covers the
|
|
14
|
+
opposite order, control then label, e.g. FieldItem/GDS's own
|
|
15
|
+
radios and checkboxes convention, and Radio/Checkbox's own
|
|
16
|
+
standalone stories. CSS has no "previous sibling" selector, ~ can
|
|
17
|
+
only ever match a sibling that comes after this one, so that combinator
|
|
18
|
+
alone can never catch this order regardless of how it's written.
|
|
19
|
+
- fieldset:disabled covers the whole group being disabled, not just
|
|
20
|
+
one control. */
|
|
21
|
+
&:has(:is(:disabled, [aria-disabled='true']), ~ :is(:disabled, [aria-disabled='true'])),
|
|
22
|
+
:has(:is(:disabled, [aria-disabled='true'])) > &,
|
|
23
|
+
fieldset:disabled & {
|
|
24
|
+
@apply cursor-not-allowed select-none text-body-secondary;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { Label } from './Label';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Label> = {
|
|
6
|
+
title: 'Components/Label',
|
|
7
|
+
component: Label,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: 'Use a label to associate text with form controls and improve accessibility.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
argTypes: {
|
|
17
|
+
htmlFor: { control: 'text' },
|
|
18
|
+
children: { control: 'text' },
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
htmlFor: 'email',
|
|
22
|
+
children: 'Email address',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
|
|
28
|
+
type Story = StoryObj<typeof Label>;
|
|
29
|
+
|
|
30
|
+
export const Default: Story = {};
|
|
31
|
+
|
|
32
|
+
export const Disabled: Story = {
|
|
33
|
+
render: (args) => (
|
|
34
|
+
<fieldset disabled>
|
|
35
|
+
<Label>{args.children}</Label>
|
|
36
|
+
|
|
37
|
+
<input className="border-form border-input disabled:opacity-50" />
|
|
38
|
+
</fieldset>
|
|
39
|
+
),
|
|
40
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
|
+
|
|
5
|
+
import { Label as LabelPrimitive } from 'radix-ui';
|
|
6
|
+
|
|
7
|
+
import { cn } from '../../utils';
|
|
8
|
+
|
|
9
|
+
export type LabelProps = ComponentProps<typeof LabelPrimitive.Root>;
|
|
10
|
+
|
|
11
|
+
export const Label = ({ className, ...props }: LabelProps) => (
|
|
12
|
+
<LabelPrimitive.Root
|
|
13
|
+
{...props}
|
|
14
|
+
data-slot="label"
|
|
15
|
+
className={cn('cn-label flex items-center', className)}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: Option button
|
|
3
|
+
Uses ghost button appearance via @apply so interaction states match
|
|
4
|
+
cn-button-appearance-ghost exactly. Layout overrides (flex-col, full width)
|
|
5
|
+
sit on top. Selected state wins via higher specificity ([aria-checked]). */
|
|
6
|
+
.cn-layer-switcher-option {
|
|
7
|
+
@apply cn-button flex flex-col items-center gap-gds-1 p-gds-1 text-sm bg-transparent text-body;
|
|
8
|
+
|
|
9
|
+
/* Suppress the base :focus-visible outline, focus is shown via border + inset ring */
|
|
10
|
+
&:focus {
|
|
11
|
+
@apply outline-none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&:hover:not(:disabled) {
|
|
15
|
+
@apply bg-transparent;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Ghost-style: no bottom press shadow on focus (mouse or keyboard) */
|
|
19
|
+
&:focus:not(:active):not(:hover) {
|
|
20
|
+
@apply shadow-none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Ghost has no bottom border to press into, so no shift or border clear */
|
|
24
|
+
&:active:not(:disabled) {
|
|
25
|
+
@apply bg-black/5 transform-none;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* MARK: Selected state */
|
|
30
|
+
.cn-layer-switcher-option[aria-checked='true'] {
|
|
31
|
+
@apply bg-accent text-on-accent;
|
|
32
|
+
|
|
33
|
+
/* cn-button's :focus:not(:active):not(:hover) (specificity 0,4,0) sets bg-focus.
|
|
34
|
+
This rule wins at 0,5,0 to keep accent background while the yellow border/ring
|
|
35
|
+
from :focus (border-color + box-shadow only) still shows keyboard focus. */
|
|
36
|
+
&:focus:not(:active):not(:hover) {
|
|
37
|
+
@apply bg-accent text-on-accent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&:hover:not(:disabled) {
|
|
41
|
+
@apply bg-focus text-on-focus;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* MARK: Thumbnail */
|
|
46
|
+
.cn-layer-switcher-option-image {
|
|
47
|
+
@apply max-w-gds-9 max-h-gds-9 object-contain border-panel;
|
|
48
|
+
border-width: var(--focus-ring-width);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.cn-layer-switcher-option[aria-checked='true'] .cn-layer-switcher-option-image {
|
|
52
|
+
filter: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* MARK: Label */
|
|
56
|
+
.cn-layer-switcher-option-label {
|
|
57
|
+
@apply text-xs leading-tight text-center;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import { LayerSwitcher } from './LayerSwitcher';
|
|
6
|
+
import type { LayerOption } from './LayerSwitcher';
|
|
7
|
+
|
|
8
|
+
const EXAMPLE_LAYERS: LayerOption[] = [
|
|
9
|
+
{ name: 'OS Map' },
|
|
10
|
+
{ name: 'OS Light' },
|
|
11
|
+
{ name: 'OS Outdoor' },
|
|
12
|
+
{ name: 'Satellite' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const IMAGE_LAYERS: LayerOption[] = [
|
|
16
|
+
{
|
|
17
|
+
name: 'OS Map',
|
|
18
|
+
image:
|
|
19
|
+
'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"><rect width="60" height="60" fill="%23a8d5a2"/><rect x="10" y="25" width="40" height="4" fill="%23888"/></svg>',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'OS Light',
|
|
23
|
+
image:
|
|
24
|
+
'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"><rect width="60" height="60" fill="%23e8e8e8"/><rect x="10" y="25" width="40" height="4" fill="%23ccc"/></svg>',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Satellite',
|
|
28
|
+
image:
|
|
29
|
+
'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"><rect width="60" height="60" fill="%23228B22"/><rect x="10" y="25" width="40" height="4" fill="%23555"/></svg>',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const meta: Meta<typeof LayerSwitcher> = {
|
|
34
|
+
title: 'Components/LayerSwitcher',
|
|
35
|
+
component: LayerSwitcher,
|
|
36
|
+
tags: ['autodocs'],
|
|
37
|
+
parameters: {
|
|
38
|
+
docs: {
|
|
39
|
+
description: {
|
|
40
|
+
component: `
|
|
41
|
+
Icon button that opens a popover for selecting a basemap layer. Intended for use inside a map toolbar.
|
|
42
|
+
|
|
43
|
+
Each layer option is a radio-button card showing an optional thumbnail image and the layer name. The trigger uses \`appearance="panel"\` so it integrates visually with a compressed \`ButtonGroup\` toolbar.
|
|
44
|
+
|
|
45
|
+
**Props:**
|
|
46
|
+
- \`layers\`: array of \`LayerOption\` objects (\`name\`, optional \`image\` URL)
|
|
47
|
+
- \`activeLayerName\`: the currently selected layer name
|
|
48
|
+
- \`onSelectLayer\`: called with the layer name when a new option is chosen
|
|
49
|
+
`.trim(),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default meta;
|
|
56
|
+
|
|
57
|
+
type Story = StoryObj<typeof LayerSwitcher>;
|
|
58
|
+
|
|
59
|
+
const DefaultStory = () => {
|
|
60
|
+
const [active, setActive] = useState<string | null>('OS Map');
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex items-start">
|
|
64
|
+
<LayerSwitcher layers={EXAMPLE_LAYERS} activeLayerName={active} onSelectLayer={setActive} />
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const WithImagesStory = () => {
|
|
70
|
+
const [active, setActive] = useState<string | null>('OS Map');
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="flex items-start">
|
|
74
|
+
<LayerSwitcher layers={IMAGE_LAYERS} activeLayerName={active} onSelectLayer={setActive} />
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const NoActiveLayerStory = () => {
|
|
80
|
+
const [active, setActive] = useState<string | null>(null);
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="flex items-start">
|
|
84
|
+
<LayerSwitcher layers={EXAMPLE_LAYERS} activeLayerName={active} onSelectLayer={setActive} />
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const Default: Story = {
|
|
90
|
+
render: () => <DefaultStory />,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const WithImages: Story = {
|
|
94
|
+
parameters: {
|
|
95
|
+
docs: {
|
|
96
|
+
description: {
|
|
97
|
+
story:
|
|
98
|
+
'When each layer has an `image` URL the thumbnail is shown above the name. Non-active thumbnails are rendered in greyscale.',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
render: () => <WithImagesStory />,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const NoActiveLayer: Story = {
|
|
106
|
+
parameters: {
|
|
107
|
+
docs: {
|
|
108
|
+
description: { story: 'When `activeLayerName` is `null` no option is shown as selected.' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
render: () => <NoActiveLayerStory />,
|
|
112
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
|
+
|
|
5
|
+
import { LuLayers } from 'react-icons/lu';
|
|
6
|
+
|
|
7
|
+
import { Button } from '../Button/Button';
|
|
8
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover';
|
|
9
|
+
|
|
10
|
+
export type LayerOption = {
|
|
11
|
+
name: string;
|
|
12
|
+
image?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type LayerSwitcherProps = Omit<
|
|
16
|
+
ComponentProps<typeof Button>,
|
|
17
|
+
'variant' | 'appearance' | 'children'
|
|
18
|
+
> & {
|
|
19
|
+
layers: LayerOption[];
|
|
20
|
+
activeLayerName: string | null;
|
|
21
|
+
onSelectLayer: (name: string) => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const LayerSwitcher = ({
|
|
25
|
+
layers,
|
|
26
|
+
activeLayerName,
|
|
27
|
+
onSelectLayer,
|
|
28
|
+
...props
|
|
29
|
+
}: LayerSwitcherProps) => (
|
|
30
|
+
<Popover>
|
|
31
|
+
<PopoverTrigger asChild>
|
|
32
|
+
<Button variant="icon" appearance="panel" aria-label="Select basemap" {...props}>
|
|
33
|
+
<LuLayers aria-hidden />
|
|
34
|
+
</Button>
|
|
35
|
+
</PopoverTrigger>
|
|
36
|
+
|
|
37
|
+
<PopoverContent
|
|
38
|
+
side="left"
|
|
39
|
+
align="start"
|
|
40
|
+
sideOffset={8}
|
|
41
|
+
aria-label="Basemap selector"
|
|
42
|
+
className="w-44 p-gds-2"
|
|
43
|
+
>
|
|
44
|
+
<div role="radiogroup" aria-label="Basemap options" className="flex flex-col gap-gds-1">
|
|
45
|
+
{layers.map(({ name, image }) => (
|
|
46
|
+
<button
|
|
47
|
+
key={name}
|
|
48
|
+
type="button"
|
|
49
|
+
role="radio"
|
|
50
|
+
aria-checked={name === activeLayerName}
|
|
51
|
+
onClick={() => onSelectLayer(name)}
|
|
52
|
+
className="cn-layer-switcher-option"
|
|
53
|
+
>
|
|
54
|
+
{image ? <img src={image} alt="" className="cn-layer-switcher-option-image" /> : null}
|
|
55
|
+
<span className="cn-layer-switcher-option-label">{name}</span>
|
|
56
|
+
</button>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
</PopoverContent>
|
|
60
|
+
</Popover>
|
|
61
|
+
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { Unauthorized } from './401';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Unauthorized> = {
|
|
6
|
+
title: 'Components/Layout/401',
|
|
7
|
+
component: Unauthorized,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'Use Unauthorized for displaying text when a user is not signed in and must authenticate to view a page (401).',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default meta;
|
|
20
|
+
|
|
21
|
+
type Story = StoryObj<typeof Unauthorized>;
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Forbidden as Unauthorized } from '../403/403';
|