@tpzdsp/next-toolkit 2.5.1 → 3.1.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 +34 -0
- package/src/components/ErrorModal/ErrorModal.stories.tsx +100 -0
- package/src/components/ErrorModal/ErrorModal.tsx +123 -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 +3 -2
- package/src/components/Footer/Footer.stories.tsx +27 -0
- package/src/components/Footer/Footer.tsx +46 -0
- package/src/components/Footer/Licence.tsx +18 -0
- package/src/components/Footer/MetaLinks.tsx +47 -0
- package/src/components/Footer/Small/SmallFooter.stories.tsx +3 -0
- package/src/components/Footer/Small/SmallFooter.tsx +53 -41
- 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 +304 -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 +111 -0
- package/src/components/Modal/Modal.stories.tsx +94 -155
- package/src/components/Modal/Modal.tsx +141 -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 +202 -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 +296 -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 +343 -61
- package/src/errors/errorDisplay.ts +70 -0
- package/src/errors/index.ts +1 -0
- package/src/http/constants.ts +2 -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/footer/MetaLinks.tsx +0 -36
- 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,71 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: Summary list
|
|
3
|
+
GDS govuk-summary-list pattern. Uses <dl> with <div> row wrappers so
|
|
4
|
+
the border spans the full width rather than individual cells. Stacks
|
|
5
|
+
vertically on mobile, flex row on sm+. */
|
|
6
|
+
.cn-summary-list {
|
|
7
|
+
@apply m-0 p-0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* MARK: Row */
|
|
11
|
+
.cn-summary-list-row {
|
|
12
|
+
@apply flex flex-col sm:flex-row border-b border-divider gap-y-gds-1 sm:gap-y-0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* MARK: Key, value, actions, shared padding */
|
|
16
|
+
.cn-summary-list-key,
|
|
17
|
+
.cn-summary-list-value,
|
|
18
|
+
.cn-summary-list-actions {
|
|
19
|
+
@apply m-0 py-gds-1 sm:py-gds-2;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Unbreakable text (a single long word, a URL) would otherwise overflow
|
|
23
|
+
the key's fixed sm:w-[40%] and visually overlap the value next to it,
|
|
24
|
+
rather than wrapping the way multi-word text already does. Matches
|
|
25
|
+
GDS's own govuk-summary-list__key/__value, same reasoning. */
|
|
26
|
+
.cn-summary-list-key,
|
|
27
|
+
.cn-summary-list-value {
|
|
28
|
+
@apply wrap-break-word;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* MARK: Key
|
|
32
|
+
Wider than GDS's own default 30%: this project's keys tend to run
|
|
33
|
+
longer than GDS's own examples, and at 30% they wrap mid-word often
|
|
34
|
+
enough to look broken rather than intentional. 40% gives long keys
|
|
35
|
+
more room to wrap on whole words before running out of width. */
|
|
36
|
+
.cn-summary-list-key {
|
|
37
|
+
@apply font-bold sm:w-[40%] sm:flex-none sm:pr-gds-4;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* MARK: Value */
|
|
41
|
+
.cn-summary-list-value {
|
|
42
|
+
@apply sm:flex-1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* MARK: Actions
|
|
46
|
+
Right-aligned on sm+, stacks below value on mobile. */
|
|
47
|
+
.cn-summary-list-actions {
|
|
48
|
+
@apply sm:ml-auto sm:pl-gds-4 sm:text-right;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* MARK: Skeleton
|
|
52
|
+
Reuses SummaryList/Row/Key/Value directly, only the text content is
|
|
53
|
+
replaced with bars, so dimensions match exactly once real data loads.
|
|
54
|
+
|
|
55
|
+
w-full max-w-* (not a fixed w-*): sm:w-[30%]/sm:flex-1 on the real
|
|
56
|
+
Key/Value only kick in past the sm *viewport* breakpoint, not however
|
|
57
|
+
wide this SummaryList's own container actually is, so a narrow
|
|
58
|
+
container on a wide viewport (a sidebar, a Popup) still lays key/value
|
|
59
|
+
out side by side there. Real content is text, it wraps to whatever
|
|
60
|
+
width it's given so this never bites it, but a fixed-width bar can't
|
|
61
|
+
shrink to fit and overflows into the other column instead. max-w-*
|
|
62
|
+
keeps the same size as before wherever there's room for it, and only
|
|
63
|
+
shrinks below that once the row genuinely doesn't have the space. */
|
|
64
|
+
.cn-summary-list-skeleton-key {
|
|
65
|
+
@apply h-4 w-full max-w-24;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.cn-summary-list-skeleton-value {
|
|
69
|
+
@apply h-4 w-full max-w-40;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SummaryList,
|
|
5
|
+
SummaryListActions,
|
|
6
|
+
SummaryListKey,
|
|
7
|
+
SummaryListRow,
|
|
8
|
+
SummaryListValue,
|
|
9
|
+
} from './SummaryList';
|
|
10
|
+
import { Link } from '../Link/Link';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof SummaryList> = {
|
|
13
|
+
title: 'Components/SummaryList',
|
|
14
|
+
component: SummaryList,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
parameters: {
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component: `
|
|
20
|
+
GDS summary list for displaying key–value pairs, most commonly used on "check your answers" pages.
|
|
21
|
+
|
|
22
|
+
Uses semantic \`<dl>\` / \`<dt>\` / \`<dd>\` markup. Row \`<div>\` wrappers allow the bottom border to span the full width.
|
|
23
|
+
|
|
24
|
+
**Components:**
|
|
25
|
+
- \`SummaryList\`: the \`<dl>\` container
|
|
26
|
+
- \`SummaryListRow\`: wraps each key–value pair
|
|
27
|
+
- \`SummaryListKey\`: the \`<dt>\` term (bold)
|
|
28
|
+
- \`SummaryListValue\`: the \`<dd>\` value
|
|
29
|
+
- \`SummaryListActions\`: optional \`<dd>\` for change/delete links, right-aligned on desktop
|
|
30
|
+
`.trim(),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default meta;
|
|
37
|
+
type Story = StoryObj<typeof SummaryList>;
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
parameters: {
|
|
41
|
+
docs: {
|
|
42
|
+
description: { story: 'Typical "check your answers" usage with a change link on each row.' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
render: () => (
|
|
46
|
+
<SummaryList>
|
|
47
|
+
<SummaryListRow>
|
|
48
|
+
<SummaryListKey>Name</SummaryListKey>
|
|
49
|
+
|
|
50
|
+
<SummaryListValue>Sarah Philips</SummaryListValue>
|
|
51
|
+
|
|
52
|
+
<SummaryListActions>
|
|
53
|
+
<Link href="#">
|
|
54
|
+
Change <span className="sr-only">name</span>
|
|
55
|
+
</Link>
|
|
56
|
+
</SummaryListActions>
|
|
57
|
+
</SummaryListRow>
|
|
58
|
+
|
|
59
|
+
<SummaryListRow>
|
|
60
|
+
<SummaryListKey>Date of birth</SummaryListKey>
|
|
61
|
+
|
|
62
|
+
<SummaryListValue>5 January 1978</SummaryListValue>
|
|
63
|
+
|
|
64
|
+
<SummaryListActions>
|
|
65
|
+
<Link href="#">
|
|
66
|
+
Change <span className="sr-only">date of birth</span>
|
|
67
|
+
</Link>
|
|
68
|
+
</SummaryListActions>
|
|
69
|
+
</SummaryListRow>
|
|
70
|
+
|
|
71
|
+
<SummaryListRow>
|
|
72
|
+
<SummaryListKey>Contact information</SummaryListKey>
|
|
73
|
+
|
|
74
|
+
<SummaryListValue>
|
|
75
|
+
72 Guild Street
|
|
76
|
+
<br />
|
|
77
|
+
London
|
|
78
|
+
<br />
|
|
79
|
+
SE23 6FH
|
|
80
|
+
</SummaryListValue>
|
|
81
|
+
|
|
82
|
+
<SummaryListActions>
|
|
83
|
+
<Link href="#">
|
|
84
|
+
Change <span className="sr-only">contact information</span>
|
|
85
|
+
</Link>
|
|
86
|
+
</SummaryListActions>
|
|
87
|
+
</SummaryListRow>
|
|
88
|
+
</SummaryList>
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const MultipleActions: Story = {
|
|
93
|
+
parameters: {
|
|
94
|
+
docs: {
|
|
95
|
+
description: {
|
|
96
|
+
story:
|
|
97
|
+
'When a row has more than one action, list them with visually hidden context text on each.',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
render: () => (
|
|
102
|
+
<SummaryList>
|
|
103
|
+
<SummaryListRow>
|
|
104
|
+
<SummaryListKey>Name</SummaryListKey>
|
|
105
|
+
|
|
106
|
+
<SummaryListValue>Sarah Philips</SummaryListValue>
|
|
107
|
+
|
|
108
|
+
<SummaryListActions>
|
|
109
|
+
<ul className="m-0 p-0 list-none flex gap-gds-2 sm:flex-col sm:gap-gds-1">
|
|
110
|
+
<li>
|
|
111
|
+
<Link href="#">
|
|
112
|
+
Change <span className="sr-only">name</span>
|
|
113
|
+
</Link>
|
|
114
|
+
</li>
|
|
115
|
+
|
|
116
|
+
<li>
|
|
117
|
+
<Link href="#">
|
|
118
|
+
Delete <span className="sr-only">name</span>
|
|
119
|
+
</Link>
|
|
120
|
+
</li>
|
|
121
|
+
</ul>
|
|
122
|
+
</SummaryListActions>
|
|
123
|
+
</SummaryListRow>
|
|
124
|
+
|
|
125
|
+
<SummaryListRow>
|
|
126
|
+
<SummaryListKey>Date of birth</SummaryListKey>
|
|
127
|
+
|
|
128
|
+
<SummaryListValue>5 January 1978</SummaryListValue>
|
|
129
|
+
|
|
130
|
+
<SummaryListActions>
|
|
131
|
+
<ul className="m-0 p-0 list-none flex gap-gds-2 sm:flex-col sm:gap-gds-1">
|
|
132
|
+
<li>
|
|
133
|
+
<Link href="#">
|
|
134
|
+
Change <span className="sr-only">date of birth</span>
|
|
135
|
+
</Link>
|
|
136
|
+
</li>
|
|
137
|
+
|
|
138
|
+
<li>
|
|
139
|
+
<Link href="#">
|
|
140
|
+
Delete <span className="sr-only">date of birth</span>
|
|
141
|
+
</Link>
|
|
142
|
+
</li>
|
|
143
|
+
</ul>
|
|
144
|
+
</SummaryListActions>
|
|
145
|
+
</SummaryListRow>
|
|
146
|
+
</SummaryList>
|
|
147
|
+
),
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const NarrowContainer: Story = {
|
|
151
|
+
parameters: {
|
|
152
|
+
docs: {
|
|
153
|
+
description: {
|
|
154
|
+
story:
|
|
155
|
+
'A single long, unbreakable word (no space to wrap at) in a narrow container. Without break-words on the key/value, this would overflow its own box and visually overlap its neighbour rather than wrapping the way multi-word text does.',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
render: () => (
|
|
160
|
+
<div className="max-w-xs">
|
|
161
|
+
<SummaryList>
|
|
162
|
+
<SummaryListRow>
|
|
163
|
+
<SummaryListKey>Sites</SummaryListKey>
|
|
164
|
+
|
|
165
|
+
<SummaryListValue>240</SummaryListValue>
|
|
166
|
+
</SummaryListRow>
|
|
167
|
+
|
|
168
|
+
<SummaryListRow>
|
|
169
|
+
<SummaryListKey>Total area</SummaryListKey>
|
|
170
|
+
|
|
171
|
+
<SummaryListValue>1,315,000 ha</SummaryListValue>
|
|
172
|
+
</SummaryListRow>
|
|
173
|
+
|
|
174
|
+
<SummaryListRow>
|
|
175
|
+
<SummaryListKey>Percentage</SummaryListKey>
|
|
176
|
+
|
|
177
|
+
<SummaryListValue>34.2%</SummaryListValue>
|
|
178
|
+
</SummaryListRow>
|
|
179
|
+
</SummaryList>
|
|
180
|
+
</div>
|
|
181
|
+
),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const NoActions: Story = {
|
|
185
|
+
parameters: {
|
|
186
|
+
docs: {
|
|
187
|
+
description: { story: 'Read-only display, no actions column.' },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
render: () => (
|
|
191
|
+
<SummaryList>
|
|
192
|
+
<SummaryListRow>
|
|
193
|
+
<SummaryListKey>Name</SummaryListKey>
|
|
194
|
+
|
|
195
|
+
<SummaryListValue>Sarah Philips</SummaryListValue>
|
|
196
|
+
</SummaryListRow>
|
|
197
|
+
|
|
198
|
+
<SummaryListRow>
|
|
199
|
+
<SummaryListKey>Date of birth</SummaryListKey>
|
|
200
|
+
|
|
201
|
+
<SummaryListValue>5 January 1978</SummaryListValue>
|
|
202
|
+
</SummaryListRow>
|
|
203
|
+
|
|
204
|
+
<SummaryListRow>
|
|
205
|
+
<SummaryListKey>Address</SummaryListKey>
|
|
206
|
+
|
|
207
|
+
<SummaryListValue>72 Guild Street, London, SE23 6FH</SummaryListValue>
|
|
208
|
+
</SummaryListRow>
|
|
209
|
+
</SummaryList>
|
|
210
|
+
),
|
|
211
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
|
|
5
|
+
export type SummaryListProps = ComponentProps<'dl'>;
|
|
6
|
+
|
|
7
|
+
export const SummaryList = ({ className, ...props }: SummaryListProps) => (
|
|
8
|
+
<dl data-slot="summary-list" className={cn('cn-summary-list', className)} {...props} />
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export type SummaryListRowProps = ComponentProps<'div'>;
|
|
12
|
+
|
|
13
|
+
export const SummaryListRow = ({ className, ...props }: SummaryListRowProps) => (
|
|
14
|
+
<div data-slot="summary-list-row" className={cn('cn-summary-list-row', className)} {...props} />
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export type SummaryListKeyProps = ComponentProps<'dt'>;
|
|
18
|
+
|
|
19
|
+
export const SummaryListKey = ({ className, ...props }: SummaryListKeyProps) => (
|
|
20
|
+
<dt data-slot="summary-list-key" className={cn('cn-summary-list-key', className)} {...props} />
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type SummaryListValueProps = ComponentProps<'dd'>;
|
|
24
|
+
|
|
25
|
+
export const SummaryListValue = ({ className, ...props }: SummaryListValueProps) => (
|
|
26
|
+
<dd
|
|
27
|
+
data-slot="summary-list-value"
|
|
28
|
+
className={cn('cn-summary-list-value', className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export type SummaryListActionsProps = ComponentProps<'dd'>;
|
|
34
|
+
|
|
35
|
+
export const SummaryListActions = ({ className, ...props }: SummaryListActionsProps) => (
|
|
36
|
+
<dd
|
|
37
|
+
data-slot="summary-list-actions"
|
|
38
|
+
className={cn('cn-summary-list-actions', className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { SummaryList, SummaryListKey, SummaryListRow, SummaryListValue } from './SummaryList';
|
|
4
|
+
import { SummaryListSkeleton } from './SummaryListSkeleton';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof SummaryListSkeleton> = {
|
|
7
|
+
title: 'Components/SummaryList/Skeleton',
|
|
8
|
+
component: SummaryListSkeleton,
|
|
9
|
+
tags: ['autodocs'],
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'Placeholder for a SummaryList while its rows are loading. Reuses SummaryList/SummaryListRow/SummaryListKey/SummaryListValue directly, so it lines up exactly with the real list once data arrives.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
|
|
22
|
+
type Story = StoryObj<typeof SummaryListSkeleton>;
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
render: () => (
|
|
26
|
+
<div className="max-w-md">
|
|
27
|
+
<SummaryListSkeleton />
|
|
28
|
+
</div>
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const SideBySide: Story = {
|
|
33
|
+
parameters: {
|
|
34
|
+
docs: {
|
|
35
|
+
description: { story: 'The skeleton and the real list line up exactly once loaded.' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
render: () => (
|
|
39
|
+
<div className="flex gap-gds-6">
|
|
40
|
+
<div className="max-w-md w-full">
|
|
41
|
+
<SummaryListSkeleton rows={3} />
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div className="max-w-md w-full">
|
|
45
|
+
<SummaryList>
|
|
46
|
+
<SummaryListRow>
|
|
47
|
+
<SummaryListKey>Name</SummaryListKey>
|
|
48
|
+
|
|
49
|
+
<SummaryListValue>Sarah Philips</SummaryListValue>
|
|
50
|
+
</SummaryListRow>
|
|
51
|
+
|
|
52
|
+
<SummaryListRow>
|
|
53
|
+
<SummaryListKey>Date of birth</SummaryListKey>
|
|
54
|
+
|
|
55
|
+
<SummaryListValue>5 January 1978</SummaryListValue>
|
|
56
|
+
</SummaryListRow>
|
|
57
|
+
|
|
58
|
+
<SummaryListRow>
|
|
59
|
+
<SummaryListKey>Address</SummaryListKey>
|
|
60
|
+
|
|
61
|
+
<SummaryListValue>72 Guild Street, London, SE23 6FH</SummaryListValue>
|
|
62
|
+
</SummaryListRow>
|
|
63
|
+
</SummaryList>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { SummaryList, SummaryListKey, SummaryListRow, SummaryListValue } from './SummaryList';
|
|
4
|
+
import { Skeleton } from '../Skeleton/Skeleton';
|
|
5
|
+
|
|
6
|
+
// Stable, fixed identifiers rather than the array index, even though these
|
|
7
|
+
// are decorative and never reorder, so a row's identity is never tied to
|
|
8
|
+
// its position at render time.
|
|
9
|
+
const ROW_KEYS = ['row-1', 'row-2', 'row-3', 'row-4', 'row-5', 'row-6'] as const;
|
|
10
|
+
|
|
11
|
+
export type SummaryListSkeletonProps = Omit<ComponentProps<typeof SummaryList>, 'children'> & {
|
|
12
|
+
/** Explicitly disallow children, a skeleton is a placeholder shape, not a content wrapper. */
|
|
13
|
+
children?: never;
|
|
14
|
+
/** Number of rows to show. Defaults to 3. */
|
|
15
|
+
rows?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const SummaryListSkeleton = ({
|
|
19
|
+
className,
|
|
20
|
+
rows = 3,
|
|
21
|
+
...props
|
|
22
|
+
}: SummaryListSkeletonProps) => {
|
|
23
|
+
const rowKeys = ROW_KEYS.slice(0, rows);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<SummaryList aria-hidden className={className} {...props}>
|
|
27
|
+
{rowKeys.map((key) => (
|
|
28
|
+
<SummaryListRow key={key}>
|
|
29
|
+
<SummaryListKey>
|
|
30
|
+
<Skeleton className="cn-summary-list-skeleton-key" />
|
|
31
|
+
</SummaryListKey>
|
|
32
|
+
|
|
33
|
+
<SummaryListValue>
|
|
34
|
+
<Skeleton className="cn-summary-list-skeleton-value" />
|
|
35
|
+
</SummaryListValue>
|
|
36
|
+
</SummaryListRow>
|
|
37
|
+
))}
|
|
38
|
+
</SummaryList>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: Tab list
|
|
3
|
+
h-full: stretches to fill whatever height its parent establishes (e.g. a
|
|
4
|
+
taller header) rather than shrinking to its own content's height, so it
|
|
5
|
+
actually reaches that container's top edge instead of floating wherever
|
|
6
|
+
it naturally lands. items-end then sits each trigger (text + its
|
|
7
|
+
underline, as one rigid unit, see cn-tabs-trigger below) flush against
|
|
8
|
+
the bottom of that now-taller box rather than the top, with no extra gap
|
|
9
|
+
introduced either within the trigger or between the list and whatever
|
|
10
|
+
comes right after it (typically TabsContent). relative + cn-z-top keeps
|
|
11
|
+
the active-tab underline visible over anything else rendered nearby,
|
|
12
|
+
needs its own stacking context since this element isn't necessarily a
|
|
13
|
+
flex/grid item of anything itself. */
|
|
14
|
+
.cn-tabs-list {
|
|
15
|
+
@apply relative flex h-full items-end cn-z-top flex-wrap;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* MARK: Tab trigger
|
|
19
|
+
Column flex so the hidden sizer stacks above the visible text.
|
|
20
|
+
The sizer is bold (matching the active state) with height: 0 so it
|
|
21
|
+
contributes its bold width to the trigger without adding any height.
|
|
22
|
+
This means the trigger is always sized to the bold width, preventing
|
|
23
|
+
siblings from shifting when the active tab changes. */
|
|
24
|
+
.cn-tabs-trigger {
|
|
25
|
+
@apply flex flex-col items-center px-gds-3 py-gds-1 bg-transparent border-none cursor-pointer
|
|
26
|
+
select-none transition-transform duration-150;
|
|
27
|
+
|
|
28
|
+
&:hover:not(:disabled) {
|
|
29
|
+
@apply bg-focus;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&:hover:not([data-state='active'], :disabled) {
|
|
33
|
+
@apply -translate-y-px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&:focus-visible {
|
|
37
|
+
@apply outline-focus;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&[data-disabled] {
|
|
41
|
+
@apply opacity-50 cursor-not-allowed;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Invisible bold copy, sets the width, takes no height */
|
|
46
|
+
.cn-tabs-trigger-sizer {
|
|
47
|
+
@apply font-bold invisible overflow-hidden whitespace-nowrap select-none pointer-events-none
|
|
48
|
+
h-0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Visible text, underline transitions from transparent to body colour on active */
|
|
52
|
+
.cn-tabs-trigger-text {
|
|
53
|
+
@apply text-base text-body underline decoration-transparent decoration-2 underline-offset-4
|
|
54
|
+
transition-[font-weight,text-decoration-color] duration-150;
|
|
55
|
+
line-height: 1.5rem;
|
|
56
|
+
text-underline-offset: var(--spacing-gds-2);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.cn-tabs-trigger[data-state='active'] .cn-tabs-trigger-text {
|
|
60
|
+
@apply font-bold decoration-body;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* MARK: Tab content
|
|
64
|
+
Radix's forceMount makes a TabsContent panel present={forceMount ||
|
|
65
|
+
isSelected}, which is unconditionally true whenever forceMount is set,
|
|
66
|
+
so its own hidden={!present} is unconditionally false too, and React
|
|
67
|
+
omits a hidden={false} attribute from the DOM entirely. forceMount's
|
|
68
|
+
actual contract is "stay mounted always, you handle hiding it
|
|
69
|
+
yourself" via data-state, not "hidden natively while inactive": don't
|
|
70
|
+
reintroduce a [hidden] selector here expecting it to ever match.
|
|
71
|
+
!important still matters here for the same reason it would for
|
|
72
|
+
[hidden]: a consumer's own layout class on TabsContent (e.g. flex/h-full,
|
|
73
|
+
to make a map fill the panel) is a same-origin, equal-or-higher-
|
|
74
|
+
specificity display declaration that would otherwise win. */
|
|
75
|
+
.cn-tabs-content {
|
|
76
|
+
&:focus-visible {
|
|
77
|
+
@apply outline-focus;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&[data-state='inactive'] {
|
|
81
|
+
display: none !important;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from './Tabs';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Tabs> = {
|
|
8
|
+
title: 'Components/Tabs',
|
|
9
|
+
component: Tabs,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof Tabs>;
|
|
14
|
+
|
|
15
|
+
export const Default: Story = {
|
|
16
|
+
render: () => (
|
|
17
|
+
<Tabs defaultValue="overview">
|
|
18
|
+
<TabsList>
|
|
19
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
20
|
+
|
|
21
|
+
<TabsTrigger value="details">Details</TabsTrigger>
|
|
22
|
+
|
|
23
|
+
<TabsTrigger value="history">History</TabsTrigger>
|
|
24
|
+
</TabsList>
|
|
25
|
+
|
|
26
|
+
<TabsContent value="overview">
|
|
27
|
+
<p className="text-body">Overview tab content.</p>
|
|
28
|
+
</TabsContent>
|
|
29
|
+
|
|
30
|
+
<TabsContent value="details">
|
|
31
|
+
<p className="text-body">Details tab content.</p>
|
|
32
|
+
</TabsContent>
|
|
33
|
+
|
|
34
|
+
<TabsContent value="history">
|
|
35
|
+
<p className="text-body">History tab content.</p>
|
|
36
|
+
</TabsContent>
|
|
37
|
+
</Tabs>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const WithDisabledTab: Story = {
|
|
42
|
+
render: () => (
|
|
43
|
+
<Tabs defaultValue="active">
|
|
44
|
+
<TabsList>
|
|
45
|
+
<TabsTrigger value="active">Active</TabsTrigger>
|
|
46
|
+
|
|
47
|
+
<TabsTrigger value="disabled" disabled>
|
|
48
|
+
Disabled
|
|
49
|
+
</TabsTrigger>
|
|
50
|
+
|
|
51
|
+
<TabsTrigger value="another">Another</TabsTrigger>
|
|
52
|
+
</TabsList>
|
|
53
|
+
|
|
54
|
+
<TabsContent value="active">
|
|
55
|
+
<p className="text-body">Active tab content.</p>
|
|
56
|
+
</TabsContent>
|
|
57
|
+
|
|
58
|
+
<TabsContent value="disabled">
|
|
59
|
+
<p className="text-body">Disabled tab content (unreachable).</p>
|
|
60
|
+
</TabsContent>
|
|
61
|
+
|
|
62
|
+
<TabsContent value="another">
|
|
63
|
+
<p className="text-body">Another tab content.</p>
|
|
64
|
+
</TabsContent>
|
|
65
|
+
</Tabs>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const ControlledTabs = () => {
|
|
70
|
+
const [value, setValue] = useState('tab1');
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="flex flex-col gap-gds-2">
|
|
74
|
+
<p className="text-body text-sm">
|
|
75
|
+
Active tab: <strong>{value}</strong>
|
|
76
|
+
</p>
|
|
77
|
+
|
|
78
|
+
<Tabs value={value} onValueChange={setValue}>
|
|
79
|
+
<TabsList>
|
|
80
|
+
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
81
|
+
|
|
82
|
+
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
83
|
+
|
|
84
|
+
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
|
|
85
|
+
</TabsList>
|
|
86
|
+
|
|
87
|
+
<TabsContent value="tab1">
|
|
88
|
+
<p className="text-body">Content for Tab 1.</p>
|
|
89
|
+
</TabsContent>
|
|
90
|
+
|
|
91
|
+
<TabsContent value="tab2">
|
|
92
|
+
<p className="text-body">Content for Tab 2.</p>
|
|
93
|
+
</TabsContent>
|
|
94
|
+
|
|
95
|
+
<TabsContent value="tab3">
|
|
96
|
+
<p className="text-body">Content for Tab 3.</p>
|
|
97
|
+
</TabsContent>
|
|
98
|
+
</Tabs>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const Controlled: Story = {
|
|
104
|
+
render: () => <ControlledTabs />,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const BottomAligned: Story = {
|
|
108
|
+
parameters: {
|
|
109
|
+
docs: {
|
|
110
|
+
description: {
|
|
111
|
+
story:
|
|
112
|
+
'If TabsList itself is given more height than its content needs (here via className="h-20", e.g. sitting inside a taller header bar), the triggers sit flush against its bottom edge rather than floating at the top, with no extra gap introduced between the text and its own underline.',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
render: () => (
|
|
117
|
+
<Tabs defaultValue="overview">
|
|
118
|
+
<TabsList className="h-20 border-b border-divider">
|
|
119
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
120
|
+
|
|
121
|
+
<TabsTrigger value="details">Details</TabsTrigger>
|
|
122
|
+
</TabsList>
|
|
123
|
+
|
|
124
|
+
<TabsContent value="overview">
|
|
125
|
+
<p className="text-body">Overview tab content.</p>
|
|
126
|
+
</TabsContent>
|
|
127
|
+
|
|
128
|
+
<TabsContent value="details">
|
|
129
|
+
<p className="text-body">Details tab content.</p>
|
|
130
|
+
</TabsContent>
|
|
131
|
+
</Tabs>
|
|
132
|
+
),
|
|
133
|
+
};
|