@tpzdsp/next-toolkit 2.5.0 → 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 +22 -13
- 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,169 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { Radio, RadioGroup } from './Radio';
|
|
4
|
+
import { FieldItem } from '../Fieldset/Fieldset';
|
|
5
|
+
import { Label } from '../Label/Label';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Radio> = {
|
|
8
|
+
title: 'Components/Radio',
|
|
9
|
+
component: Radio,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: 'Use the radios component when users can only select one option from a list.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
size: {
|
|
20
|
+
control: { type: 'radio' },
|
|
21
|
+
options: ['sm', 'base'],
|
|
22
|
+
},
|
|
23
|
+
disabled: { control: 'boolean' },
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
size: 'base',
|
|
27
|
+
value: 'default',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
|
|
33
|
+
type Story = StoryObj<typeof Radio>;
|
|
34
|
+
|
|
35
|
+
export const Default: Story = {
|
|
36
|
+
render: (args) => (
|
|
37
|
+
<RadioGroup>
|
|
38
|
+
<Radio {...args} />
|
|
39
|
+
</RadioGroup>
|
|
40
|
+
),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const Sizes: Story = {
|
|
44
|
+
render: () => (
|
|
45
|
+
<div className="flex items-center gap-gds-4">
|
|
46
|
+
<div className="flex flex-col items-center gap-gds-1">
|
|
47
|
+
<RadioGroup>
|
|
48
|
+
<Radio size="sm" value="sm" id="radio-size-sm" />
|
|
49
|
+
</RadioGroup>
|
|
50
|
+
|
|
51
|
+
<Label htmlFor="radio-size-sm">sm</Label>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div className="flex flex-col items-center gap-gds-1">
|
|
55
|
+
<RadioGroup>
|
|
56
|
+
<Radio size="base" value="base" id="radio-size-base" />
|
|
57
|
+
</RadioGroup>
|
|
58
|
+
|
|
59
|
+
<Label htmlFor="radio-size-base">base</Label>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const WithLabels: Story = {
|
|
66
|
+
parameters: {
|
|
67
|
+
docs: {
|
|
68
|
+
description: {
|
|
69
|
+
story:
|
|
70
|
+
'Standard radio group with labels. Arrow keys move between options, `RadioGroup` manages focus and selection.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
render: () => (
|
|
75
|
+
<RadioGroup defaultValue="yes">
|
|
76
|
+
<div className="flex flex-col gap-gds-2">
|
|
77
|
+
<FieldItem>
|
|
78
|
+
<Radio id="yes" value="yes" />
|
|
79
|
+
|
|
80
|
+
<Label htmlFor="yes">Yes</Label>
|
|
81
|
+
</FieldItem>
|
|
82
|
+
|
|
83
|
+
<FieldItem>
|
|
84
|
+
<Radio id="no" value="no" />
|
|
85
|
+
|
|
86
|
+
<Label htmlFor="no">No</Label>
|
|
87
|
+
</FieldItem>
|
|
88
|
+
|
|
89
|
+
<FieldItem>
|
|
90
|
+
<Radio id="maybe" value="maybe" />
|
|
91
|
+
|
|
92
|
+
<Label htmlFor="maybe">Maybe</Label>
|
|
93
|
+
</FieldItem>
|
|
94
|
+
</div>
|
|
95
|
+
</RadioGroup>
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const Horizontal: Story = {
|
|
100
|
+
parameters: {
|
|
101
|
+
docs: {
|
|
102
|
+
description: {
|
|
103
|
+
story:
|
|
104
|
+
'Pass orientation="horizontal" for an inline row of options (GDS\'s "inline" radios), it wraps onto more than one line rather than overflowing if there isn\'t room for all of them.',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
render: () => (
|
|
109
|
+
<RadioGroup defaultValue="yes" orientation="horizontal">
|
|
110
|
+
<FieldItem>
|
|
111
|
+
<Radio id="h-yes" value="yes" />
|
|
112
|
+
|
|
113
|
+
<Label htmlFor="h-yes">Yes</Label>
|
|
114
|
+
</FieldItem>
|
|
115
|
+
|
|
116
|
+
<FieldItem>
|
|
117
|
+
<Radio id="h-no" value="no" />
|
|
118
|
+
|
|
119
|
+
<Label htmlFor="h-no">No</Label>
|
|
120
|
+
</FieldItem>
|
|
121
|
+
|
|
122
|
+
<FieldItem>
|
|
123
|
+
<Radio id="h-maybe" value="maybe" />
|
|
124
|
+
|
|
125
|
+
<Label htmlFor="h-maybe">Maybe</Label>
|
|
126
|
+
</FieldItem>
|
|
127
|
+
</RadioGroup>
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const Disabled: Story = {
|
|
132
|
+
render: () => (
|
|
133
|
+
<RadioGroup defaultValue="yes">
|
|
134
|
+
<div className="flex flex-col gap-gds-2">
|
|
135
|
+
<FieldItem>
|
|
136
|
+
<Radio id="d-yes" value="yes" disabled />
|
|
137
|
+
|
|
138
|
+
<Label htmlFor="d-yes">Yes (disabled, selected)</Label>
|
|
139
|
+
</FieldItem>
|
|
140
|
+
|
|
141
|
+
<FieldItem>
|
|
142
|
+
<Radio id="d-no" value="no" disabled />
|
|
143
|
+
|
|
144
|
+
<Label htmlFor="d-no">No (disabled)</Label>
|
|
145
|
+
</FieldItem>
|
|
146
|
+
</div>
|
|
147
|
+
</RadioGroup>
|
|
148
|
+
),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export const Small: Story = {
|
|
152
|
+
render: () => (
|
|
153
|
+
<RadioGroup defaultValue="one">
|
|
154
|
+
<div className="flex flex-col gap-gds-1">
|
|
155
|
+
<FieldItem>
|
|
156
|
+
<Radio id="sm-one" value="one" size="sm" />
|
|
157
|
+
|
|
158
|
+
<Label htmlFor="sm-one">Option one</Label>
|
|
159
|
+
</FieldItem>
|
|
160
|
+
|
|
161
|
+
<FieldItem>
|
|
162
|
+
<Radio id="sm-two" value="two" size="sm" />
|
|
163
|
+
|
|
164
|
+
<Label htmlFor="sm-two">Option two</Label>
|
|
165
|
+
</FieldItem>
|
|
166
|
+
</div>
|
|
167
|
+
</RadioGroup>
|
|
168
|
+
),
|
|
169
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
|
+
|
|
5
|
+
import { RadioGroup as RadioGroupPrimitive } from 'radix-ui';
|
|
6
|
+
|
|
7
|
+
import { cn } from '../../utils/components';
|
|
8
|
+
|
|
9
|
+
const RADIO_SIZES = {
|
|
10
|
+
sm: 'cn-radio-size-sm',
|
|
11
|
+
base: 'cn-radio-size-base',
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
export type RadioSize = keyof typeof RADIO_SIZES;
|
|
15
|
+
|
|
16
|
+
export type RadioGroupProps<T extends string> = Omit<
|
|
17
|
+
ComponentProps<typeof RadioGroupPrimitive.Root>,
|
|
18
|
+
'defaultValue' | 'value' | 'onValueChange'
|
|
19
|
+
> & {
|
|
20
|
+
defaultValue?: T;
|
|
21
|
+
value?: T;
|
|
22
|
+
onValueChange?: (value: T) => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type RadioProps = ComponentProps<typeof RadioGroupPrimitive.Item> & {
|
|
26
|
+
size?: RadioSize;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const RadioGroup = <T extends string = string>({
|
|
30
|
+
defaultValue,
|
|
31
|
+
value,
|
|
32
|
+
onValueChange,
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}: RadioGroupProps<T>) => (
|
|
36
|
+
<RadioGroupPrimitive.Root
|
|
37
|
+
{...props}
|
|
38
|
+
data-slot="radio-group"
|
|
39
|
+
className={cn('cn-radio-group', className)}
|
|
40
|
+
defaultValue={defaultValue as string}
|
|
41
|
+
value={value as string}
|
|
42
|
+
onValueChange={(value) => onValueChange?.(value as T)}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export const Radio = ({ className, size = 'base', ...props }: RadioProps) => (
|
|
47
|
+
<RadioGroupPrimitive.Item
|
|
48
|
+
{...props}
|
|
49
|
+
|
|
50
|
+
data-slot="radio"
|
|
51
|
+
data-size={size}
|
|
52
|
+
className={cn('cn-radio', RADIO_SIZES[size], className)}
|
|
53
|
+
>
|
|
54
|
+
<RadioGroupPrimitive.Indicator data-slot="radio-indicator" className="cn-radio-indicator" />
|
|
55
|
+
</RadioGroupPrimitive.Item>
|
|
56
|
+
);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: Resizing
|
|
3
|
+
Applied to <body> for the duration of a drag (see ResizableHandle), so
|
|
4
|
+
the pointer moving during the drag can't select surrounding page text. */
|
|
5
|
+
body.cn-resizing {
|
|
6
|
+
@apply select-none;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/* MARK: Panel group
|
|
10
|
+
Flex container, direction driven by the aria-orientation attribute set
|
|
11
|
+
by react-resizable-panels. */
|
|
12
|
+
.cn-resizable-panel-group {
|
|
13
|
+
@apply flex h-full w-full;
|
|
14
|
+
|
|
15
|
+
&[aria-orientation='vertical'] {
|
|
16
|
+
@apply flex-col;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* MARK: Panel
|
|
21
|
+
react-resizable-panels hardcodes overflow: visible as an inline style
|
|
22
|
+
on every panel's own [data-panel] div (confirmed directly in its
|
|
23
|
+
source), regardless of props, so any content that visually extends
|
|
24
|
+
even slightly past a panel's own edge (a map control, a tooltip, a
|
|
25
|
+
canvas that doesn't perfectly match its container) isn't clipped
|
|
26
|
+
there, it keeps going and can inflate the whole page's scrollable
|
|
27
|
+
area instead of staying contained to the panel, even though every
|
|
28
|
+
statically-sized ancestor up to <html> still measures a perfectly
|
|
29
|
+
normal, viewport-matching size. !important is the only way to win
|
|
30
|
+
against an inline style. */
|
|
31
|
+
[data-panel] {
|
|
32
|
+
overflow: hidden !important;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* MARK: Handle
|
|
36
|
+
The drag separator between panels. The ::after expands the hit area
|
|
37
|
+
without changing the visual size. Horizontal orientation flips the
|
|
38
|
+
layout so the line and hit target run across the full width. cn-z-top
|
|
39
|
+
keeps the handle reachable regardless of DOM order relative to whatever
|
|
40
|
+
else is rendered in the panels either side of it. */
|
|
41
|
+
.cn-resizable-handle {
|
|
42
|
+
@apply relative cn-z-top flex items-center justify-center bg-divider-input cursor-col-resize;
|
|
43
|
+
width: var(--border-width-form);
|
|
44
|
+
|
|
45
|
+
&::after {
|
|
46
|
+
@apply content-[''] absolute inset-y-0 left-1/2 w-gds-2 -translate-x-1/2;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&[aria-orientation='horizontal'] {
|
|
50
|
+
@apply w-full cursor-row-resize;
|
|
51
|
+
height: var(--border-width-form);
|
|
52
|
+
|
|
53
|
+
&::after {
|
|
54
|
+
/* translate-x-0 resets the base -translate-x-1/2 */
|
|
55
|
+
@apply inset-x-0 top-1/2 w-full h-gds-2 translate-x-0 -translate-y-1/2;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
> .cn-resizable-handle-icon {
|
|
59
|
+
@apply rotate-90;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&:focus-visible {
|
|
64
|
+
@apply outline-none;
|
|
65
|
+
|
|
66
|
+
> .cn-resizable-handle-icon {
|
|
67
|
+
@apply bg-focus border-focus text-on-focus;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Hides a disabled handle entirely rather than showing a greyed-out,
|
|
72
|
+
non-functional line. visibility (not display: none) so it keeps
|
|
73
|
+
occupying its normal thin width, react-resizable-panels tracks its
|
|
74
|
+
Separators by React mount/unmount, not CSS, so this never touches
|
|
75
|
+
that, only how the already-mounted disabled Separator looks. */
|
|
76
|
+
&[data-separator='disabled'] {
|
|
77
|
+
@apply invisible pointer-events-none;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* MARK: Grip icon
|
|
82
|
+
Optional visible drag affordance centred on the handle line. */
|
|
83
|
+
.cn-resizable-handle-icon {
|
|
84
|
+
@apply z-10 flex shrink-0 items-center justify-center panel text-body size-6;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from './Resizable';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ResizablePanelGroup> = {
|
|
6
|
+
title: 'Components/Resizable',
|
|
7
|
+
component: ResizablePanelGroup,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
Drag-to-resize panel layout powered by [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels).
|
|
14
|
+
|
|
15
|
+
Install the peer dependency to use this component:
|
|
16
|
+
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
npm install react-resizable-panels
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
- **\`ResizablePanelGroup\`**: the flex container. Set \`direction="horizontal"\` (default) or \`direction="vertical"\`.
|
|
22
|
+
- **\`ResizablePanel\`**: an individual panel. Accepts \`defaultSize\` (percentage), \`minSize\`, \`maxSize\`.
|
|
23
|
+
- **\`ResizableHandle\`**: the drag separator. Disable \`withHandle\` to hide the visible grip icon.
|
|
24
|
+
`.trim(),
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default meta;
|
|
31
|
+
type Story = StoryObj<typeof ResizablePanelGroup>;
|
|
32
|
+
|
|
33
|
+
export const Horizontal: Story = {
|
|
34
|
+
parameters: {
|
|
35
|
+
docs: {
|
|
36
|
+
description: { story: 'Default horizontal layout. Drag the handle to resize.' },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
render: () => (
|
|
40
|
+
<ResizablePanelGroup
|
|
41
|
+
orientation="horizontal"
|
|
42
|
+
className="min-h-48 border-form border-solid border-divider-input"
|
|
43
|
+
>
|
|
44
|
+
<ResizablePanel defaultSize={50}>
|
|
45
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
46
|
+
Panel A
|
|
47
|
+
</div>
|
|
48
|
+
</ResizablePanel>
|
|
49
|
+
|
|
50
|
+
<ResizableHandle />
|
|
51
|
+
|
|
52
|
+
<ResizablePanel defaultSize={50}>
|
|
53
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
54
|
+
Panel B
|
|
55
|
+
</div>
|
|
56
|
+
</ResizablePanel>
|
|
57
|
+
</ResizablePanelGroup>
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Vertical: Story = {
|
|
62
|
+
parameters: {
|
|
63
|
+
docs: {
|
|
64
|
+
description: { story: 'Vertical layout, panels stack top to bottom.' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
render: () => (
|
|
68
|
+
<ResizablePanelGroup
|
|
69
|
+
orientation="vertical"
|
|
70
|
+
className="min-h-64 border-form border-solid border-divider-input"
|
|
71
|
+
>
|
|
72
|
+
<ResizablePanel defaultSize={50}>
|
|
73
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
74
|
+
Top
|
|
75
|
+
</div>
|
|
76
|
+
</ResizablePanel>
|
|
77
|
+
|
|
78
|
+
<ResizableHandle />
|
|
79
|
+
|
|
80
|
+
<ResizablePanel defaultSize={50}>
|
|
81
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
82
|
+
Bottom
|
|
83
|
+
</div>
|
|
84
|
+
</ResizablePanel>
|
|
85
|
+
</ResizablePanelGroup>
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const ThreePanels: Story = {
|
|
90
|
+
parameters: {
|
|
91
|
+
docs: {
|
|
92
|
+
description: { story: 'Multiple panels with independent size constraints.' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
render: () => (
|
|
96
|
+
<ResizablePanelGroup
|
|
97
|
+
orientation="horizontal"
|
|
98
|
+
className="min-h-48 border-form border-solid border-divider-input"
|
|
99
|
+
>
|
|
100
|
+
<ResizablePanel defaultSize={20} minSize={10}>
|
|
101
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
102
|
+
Sidebar
|
|
103
|
+
</div>
|
|
104
|
+
</ResizablePanel>
|
|
105
|
+
|
|
106
|
+
<ResizableHandle />
|
|
107
|
+
|
|
108
|
+
<ResizablePanel defaultSize={60}>
|
|
109
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
110
|
+
Main
|
|
111
|
+
</div>
|
|
112
|
+
</ResizablePanel>
|
|
113
|
+
|
|
114
|
+
<ResizableHandle />
|
|
115
|
+
|
|
116
|
+
<ResizablePanel defaultSize={20} minSize={10}>
|
|
117
|
+
<div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
|
|
118
|
+
Detail
|
|
119
|
+
</div>
|
|
120
|
+
</ResizablePanel>
|
|
121
|
+
</ResizablePanelGroup>
|
|
122
|
+
),
|
|
123
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type ComponentProps, type PointerEventHandler } from 'react';
|
|
4
|
+
|
|
5
|
+
import { LuGripVertical } from 'react-icons/lu';
|
|
6
|
+
import { Group, Panel, Separator } from 'react-resizable-panels';
|
|
7
|
+
|
|
8
|
+
import { cn } from '../../utils/components';
|
|
9
|
+
|
|
10
|
+
export type ResizablePanelGroupProps = ComponentProps<typeof Group>;
|
|
11
|
+
|
|
12
|
+
// react-resizable-panels pads a separator's drag hit region up to this size
|
|
13
|
+
// (centred on the actual gap between panels) if it's smaller, but defaults to
|
|
14
|
+
// only 10px for a mouse/10px pointer and 20px for touch. Our handle's visible
|
|
15
|
+
// grip icon (ResizableHandle, size-6 = 24px) is bigger than that default, so
|
|
16
|
+
// clicking near its edges misses the real hit region even though it looks
|
|
17
|
+
// like one big button. 24px also matches WCAG 2.5.5/2.5.8's minimum target
|
|
18
|
+
// size guidance, so raise the default for every pointer type, not just to
|
|
19
|
+
// match the icon.
|
|
20
|
+
const RESIZE_TARGET_MINIMUM_SIZE = { coarse: 24, fine: 24 };
|
|
21
|
+
|
|
22
|
+
export const ResizablePanelGroup = ({
|
|
23
|
+
className,
|
|
24
|
+
resizeTargetMinimumSize = RESIZE_TARGET_MINIMUM_SIZE,
|
|
25
|
+
...props
|
|
26
|
+
}: ResizablePanelGroupProps) => (
|
|
27
|
+
<Group
|
|
28
|
+
data-slot="resizable-panel-group"
|
|
29
|
+
className={cn('cn-resizable-panel-group', className)}
|
|
30
|
+
resizeTargetMinimumSize={resizeTargetMinimumSize}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export type ResizablePanelProps = ComponentProps<typeof Panel>;
|
|
36
|
+
|
|
37
|
+
export const ResizablePanel = (props: ResizablePanelProps) => (
|
|
38
|
+
<Panel data-slot="resizable-panel" {...props} />
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export type ResizableHandleProps = ComponentProps<typeof Separator> & {
|
|
42
|
+
withHandle?: boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const ResizableHandle = ({
|
|
46
|
+
withHandle = true,
|
|
47
|
+
className,
|
|
48
|
+
onPointerDown,
|
|
49
|
+
...props
|
|
50
|
+
}: ResizableHandleProps) => {
|
|
51
|
+
// Dragging the handle can otherwise select surrounding page text, the
|
|
52
|
+
// pointer doesn't stay perfectly still while dragging, and text selection
|
|
53
|
+
// is a normal side effect of that movement. There's no public drag-state
|
|
54
|
+
// event from react-resizable-panels to hook into, so track it directly:
|
|
55
|
+
// add on pointerdown, remove on pointerup/pointercancel wherever the
|
|
56
|
+
// pointer ends up, dragging routinely moves it off the handle itself.
|
|
57
|
+
const handlePointerDown: PointerEventHandler<HTMLDivElement> = (event) => {
|
|
58
|
+
document.body.classList.add('cn-resizing');
|
|
59
|
+
|
|
60
|
+
const stopResizing = () => {
|
|
61
|
+
document.body.classList.remove('cn-resizing');
|
|
62
|
+
window.removeEventListener('pointerup', stopResizing);
|
|
63
|
+
window.removeEventListener('pointercancel', stopResizing);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
window.addEventListener('pointerup', stopResizing);
|
|
67
|
+
window.addEventListener('pointercancel', stopResizing);
|
|
68
|
+
|
|
69
|
+
onPointerDown?.(event);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Separator
|
|
74
|
+
data-slot="resizable-handle"
|
|
75
|
+
className={cn('cn-resizable-handle', className)}
|
|
76
|
+
onPointerDown={handlePointerDown}
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
{withHandle ? (
|
|
80
|
+
<div className="cn-resizable-handle-icon">
|
|
81
|
+
<LuGripVertical aria-hidden />
|
|
82
|
+
</div>
|
|
83
|
+
) : null}
|
|
84
|
+
</Separator>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: Scroll area
|
|
3
|
+
Native scrollbars in both directions. min-height: 0 prevents the default
|
|
4
|
+
flex min-height: auto from stopping the area from shrinking inside a flex
|
|
5
|
+
parent. Without it, the area grows to fit content instead of scrolling. */
|
|
6
|
+
.cn-scroll-area {
|
|
7
|
+
@apply overflow-scroll min-h-0;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { ScrollArea } from './ScrollArea';
|
|
4
|
+
import { Heading } from '../Heading/Heading';
|
|
5
|
+
import { Separator } from '../Separator/Separator';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof ScrollArea> = {
|
|
8
|
+
title: 'Components/ScrollArea',
|
|
9
|
+
component: ScrollArea,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component:
|
|
15
|
+
"A div wrapper that enables native scrollbars when content overflows. Set a constrained `height` or `max-height` via `className`, the area scrolls within those bounds using the browser's native scrollbar, consistent with the rest of the page.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default meta;
|
|
22
|
+
|
|
23
|
+
type Story = StoryObj<typeof ScrollArea>;
|
|
24
|
+
|
|
25
|
+
const TAGS = Array.from({ length: 50 }, (_, i) => `v1.2.0-beta.${50 - i}`);
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
render: () => (
|
|
29
|
+
<ScrollArea className="h-72 w-48 panel">
|
|
30
|
+
<div className="p-gds-2">
|
|
31
|
+
<Heading level="h4">Tags</Heading>
|
|
32
|
+
{TAGS.map((tag) => (
|
|
33
|
+
<div key={tag}>
|
|
34
|
+
<div>{tag}</div>
|
|
35
|
+
|
|
36
|
+
<Separator className="my-2" />
|
|
37
|
+
</div>
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
</ScrollArea>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
|
|
5
|
+
export type ScrollAreaProps = ComponentProps<'div'>;
|
|
6
|
+
|
|
7
|
+
export const ScrollArea = ({ className, children, ...props }: ScrollAreaProps) => (
|
|
8
|
+
<div data-slot="scroll-area" className={cn('cn-scroll-area', className)} {...props}>
|
|
9
|
+
{children}
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { SearchBox } from './SearchBox';
|
|
4
|
+
import { ErrorText } from '../ErrorText/ErrorText';
|
|
5
|
+
import { FormGroup } from '../Form/FormGroup';
|
|
6
|
+
import { HintText } from '../HintText/HintText';
|
|
7
|
+
import { Label } from '../Label/Label';
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof SearchBox> = {
|
|
10
|
+
title: 'Components/SearchBox',
|
|
11
|
+
component: SearchBox,
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
parameters: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: {
|
|
16
|
+
component:
|
|
17
|
+
'A search input with a primary submit button. Wrap in a `<form role="search">` to wire up submission. The button is `type="submit"` so it works with standard form behaviour. Accepts all `Input` props except `type`.',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof SearchBox>;
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
render: (args) => (
|
|
29
|
+
<form role="search" onSubmit={(event) => event.preventDefault()}>
|
|
30
|
+
<SearchBox placeholder="Search" {...args} />
|
|
31
|
+
</form>
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithLabel: Story = {
|
|
36
|
+
render: (args) => (
|
|
37
|
+
<form role="search" onSubmit={(event) => event.preventDefault()}>
|
|
38
|
+
<div className="flex flex-col gap-gds-1">
|
|
39
|
+
<Label htmlFor="site-search">Search this site</Label>
|
|
40
|
+
|
|
41
|
+
<HintText id="site-search-hint">Enter a keyword or phrase.</HintText>
|
|
42
|
+
|
|
43
|
+
<SearchBox
|
|
44
|
+
id="site-search"
|
|
45
|
+
placeholder="Search"
|
|
46
|
+
aria-describedby="site-search-hint"
|
|
47
|
+
{...args}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</form>
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Invalid: Story = {
|
|
55
|
+
render: (args) => (
|
|
56
|
+
<form role="search" onSubmit={(event) => event.preventDefault()}>
|
|
57
|
+
<FormGroup invalid>
|
|
58
|
+
<Label htmlFor="search-invalid">Search this site</Label>
|
|
59
|
+
|
|
60
|
+
<ErrorText id="search-invalid-error">Enter a search term</ErrorText>
|
|
61
|
+
|
|
62
|
+
<SearchBox
|
|
63
|
+
id="search-invalid"
|
|
64
|
+
placeholder="Search"
|
|
65
|
+
aria-invalid
|
|
66
|
+
aria-describedby="search-invalid-error"
|
|
67
|
+
{...args}
|
|
68
|
+
/>
|
|
69
|
+
</FormGroup>
|
|
70
|
+
</form>
|
|
71
|
+
),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const Disabled: Story = {
|
|
75
|
+
render: (args) => (
|
|
76
|
+
<form role="search">
|
|
77
|
+
<SearchBox placeholder="Search" disabled {...args} />
|
|
78
|
+
</form>
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const CustomLabel: Story = {
|
|
83
|
+
render: (args) => (
|
|
84
|
+
<form role="search" onSubmit={(event) => event.preventDefault()}>
|
|
85
|
+
<SearchBox placeholder="Enter location" buttonLabel="Find" {...args} />
|
|
86
|
+
</form>
|
|
87
|
+
),
|
|
88
|
+
};
|