@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,40 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../utils/components';
|
|
4
|
+
import { Spinner } from '../Spinner/Spinner';
|
|
5
|
+
|
|
6
|
+
export type LoadingBoxProps = ComponentProps<'div'> & {
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
title?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const LoadingBox = ({
|
|
12
|
+
loading = false,
|
|
13
|
+
title = 'Loading',
|
|
14
|
+
className,
|
|
15
|
+
children,
|
|
16
|
+
...props
|
|
17
|
+
}: LoadingBoxProps) => (
|
|
18
|
+
<div data-slot="loading-box" className={cn('cn-loading-box', className)} {...props}>
|
|
19
|
+
<div
|
|
20
|
+
data-slot="loading-box-overlay"
|
|
21
|
+
data-loading={loading || undefined}
|
|
22
|
+
inert={!loading}
|
|
23
|
+
role="status"
|
|
24
|
+
className="cn-loading-box-overlay"
|
|
25
|
+
>
|
|
26
|
+
<Spinner aria-hidden className="cn-loading-box-spinner" />
|
|
27
|
+
|
|
28
|
+
<span className="sr-only">{title}</span>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{/* display:contents so this purely exists to carry `inert`, it doesn't
|
|
32
|
+
add a box that could change how children participate in whatever
|
|
33
|
+
layout the consumer's own content establishes. Blocks focus/clicks
|
|
34
|
+
on content that's about to be replaced once loading starts, rather
|
|
35
|
+
than just visually covering it while leaving it reachable. */}
|
|
36
|
+
<div data-slot="loading-box-content" inert={loading} className="contents">
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/*
|
|
3
|
+
MARK: Modal overlay
|
|
4
|
+
Semi-transparent backdrop. Radix sets data-state to open or closed
|
|
5
|
+
which drives enter and exit fade transitions.
|
|
6
|
+
*/
|
|
7
|
+
.cn-modal-overlay {
|
|
8
|
+
@apply bg-black/50 cn-z-overlay;
|
|
9
|
+
|
|
10
|
+
&[data-state='open'] {
|
|
11
|
+
animation: cn-modal-overlay-in 150ms ease-out;
|
|
12
|
+
}
|
|
13
|
+
&[data-state='closed'] {
|
|
14
|
+
animation: cn-modal-overlay-out 150ms ease-in;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@keyframes cn-modal-overlay-in {
|
|
19
|
+
from {
|
|
20
|
+
opacity: 0;
|
|
21
|
+
}
|
|
22
|
+
to {
|
|
23
|
+
opacity: 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes cn-modal-overlay-out {
|
|
28
|
+
from {
|
|
29
|
+
opacity: 1;
|
|
30
|
+
}
|
|
31
|
+
to {
|
|
32
|
+
opacity: 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
MARK: Modal content
|
|
38
|
+
The panel box with white background and GDS form-width border.
|
|
39
|
+
No rounded corners. The fixed position and centring transform are
|
|
40
|
+
applied via Tailwind in the TSX. Only visual appearance is set here.
|
|
41
|
+
|
|
42
|
+
Add a max-w-* class to ModalContent to widen for specific modals.
|
|
43
|
+
*/
|
|
44
|
+
.cn-modal-content {
|
|
45
|
+
@apply panel cn-z-overlay p-gds-4 w-full max-w-sm;
|
|
46
|
+
|
|
47
|
+
&[data-state='open'] {
|
|
48
|
+
animation: cn-modal-content-in 150ms ease-out;
|
|
49
|
+
}
|
|
50
|
+
&[data-state='closed'] {
|
|
51
|
+
animation: cn-modal-content-out 150ms ease-in;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes cn-modal-content-in {
|
|
56
|
+
from {
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
to {
|
|
60
|
+
opacity: 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@keyframes cn-modal-content-out {
|
|
65
|
+
from {
|
|
66
|
+
opacity: 1;
|
|
67
|
+
}
|
|
68
|
+
to {
|
|
69
|
+
opacity: 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/*
|
|
74
|
+
MARK: Modal close button
|
|
75
|
+
Positioned absolutely in the top-right corner of the panel.
|
|
76
|
+
The parent cn-modal-content has position fixed which acts
|
|
77
|
+
as the containing block for absolute children.
|
|
78
|
+
*/
|
|
79
|
+
.cn-modal-close {
|
|
80
|
+
@apply absolute top-gds-2 right-gds-2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* MARK: Modal header and footer */
|
|
84
|
+
|
|
85
|
+
.cn-modal-header {
|
|
86
|
+
@apply flex flex-col gap-gds-1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.cn-modal-footer {
|
|
90
|
+
@apply flex flex-col gap-gds-2 sm:flex-row;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* MARK: Vertical rhythm between sections
|
|
94
|
+
Adjacency-based rather than flex+gap on cn-modal-content itself,
|
|
95
|
+
ModalContent needs to stay a plain block so consumers can turn it into a
|
|
96
|
+
flex row instead (e.g. ErrorModal, an icon beside the content) without
|
|
97
|
+
fighting a hardcoded flex-direction. Applies wherever these three end up
|
|
98
|
+
as direct siblings, regardless of what wraps them, ModalDescription is
|
|
99
|
+
usually a sibling of ModalHeader rather than nested inside it. */
|
|
100
|
+
:where(.cn-modal-header, .cn-modal-description, .cn-modal-footer)
|
|
101
|
+
+ :where(.cn-modal-header, .cn-modal-description, .cn-modal-footer) {
|
|
102
|
+
margin-top: var(--spacing-gds-4);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* MARK: Modal title and description */
|
|
106
|
+
|
|
107
|
+
.cn-modal-title {
|
|
108
|
+
@apply text-xl font-bold text-body;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.cn-modal-description {
|
|
112
|
+
@apply font-normal text-body;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -1,188 +1,69 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useArgs } from 'storybook/preview-api';
|
|
4
|
-
|
|
5
3
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Modal,
|
|
7
|
+
ModalClose,
|
|
8
|
+
ModalContent,
|
|
9
|
+
ModalDescription,
|
|
10
|
+
ModalFooter,
|
|
11
|
+
ModalHeader,
|
|
12
|
+
ModalTitle,
|
|
13
|
+
ModalTrigger,
|
|
14
|
+
} from './Modal';
|
|
15
|
+
import { Button } from '../Button/Button';
|
|
8
16
|
|
|
9
17
|
const meta: Meta<typeof Modal> = {
|
|
10
18
|
title: 'Components/Modal',
|
|
11
19
|
component: Modal,
|
|
20
|
+
tags: ['autodocs'],
|
|
12
21
|
parameters: {
|
|
13
|
-
layout: 'fullscreen',
|
|
14
22
|
docs: {
|
|
15
23
|
description: {
|
|
16
|
-
component: '
|
|
24
|
+
component: 'Use the Modal primitives to display dialog content in an overlay.',
|
|
17
25
|
},
|
|
18
26
|
},
|
|
19
27
|
},
|
|
20
|
-
// removed the auto-docs for now as that will cause all modals to open immediately
|
|
21
|
-
// tags: ['autodocs'],
|
|
22
28
|
argTypes: {
|
|
23
|
-
|
|
24
|
-
control: 'boolean',
|
|
25
|
-
description: 'Whether the modal is open',
|
|
26
|
-
},
|
|
27
|
-
onClose: {
|
|
28
|
-
action: 'onClose',
|
|
29
|
-
description: 'Callback function called when the modal should close',
|
|
30
|
-
},
|
|
31
|
-
children: {
|
|
32
|
-
control: false,
|
|
33
|
-
description: 'Content to display inside the modal',
|
|
34
|
-
},
|
|
29
|
+
children: { control: false },
|
|
35
30
|
},
|
|
36
|
-
decorators: [
|
|
37
|
-
(Story, context) => {
|
|
38
|
-
const [{ isOpen }, updateArgs] = useArgs();
|
|
39
|
-
|
|
40
|
-
const handleClose = () => {
|
|
41
|
-
updateArgs({ isOpen: false });
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Override onClose prop to use local handler
|
|
45
|
-
return <Story args={{ ...context.args, isOpen, onClose: handleClose }} />;
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
31
|
};
|
|
49
32
|
|
|
50
33
|
export default meta;
|
|
34
|
+
|
|
51
35
|
type Story = StoryObj<typeof Modal>;
|
|
52
36
|
|
|
53
37
|
export const Default: Story = {
|
|
54
|
-
args: {
|
|
55
|
-
isOpen: true,
|
|
56
|
-
children: (
|
|
57
|
-
<div>
|
|
58
|
-
<h2 className="mb-4 text-xl font-bold">Modal Title</h2>
|
|
59
|
-
|
|
60
|
-
<p className="mb-4 text-gray-600">
|
|
61
|
-
This is a basic modal with some content. You can close it by clicking the X button,
|
|
62
|
-
pressing Escape, or clicking outside the modal.
|
|
63
|
-
</p>
|
|
64
|
-
|
|
65
|
-
<div className="flex gap-2">
|
|
66
|
-
<button className="px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600">
|
|
67
|
-
Confirm
|
|
68
|
-
</button>
|
|
69
|
-
|
|
70
|
-
<button className="px-4 py-2 text-gray-700 bg-gray-300 rounded hover:bg-gray-400">
|
|
71
|
-
Cancel
|
|
72
|
-
</button>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
),
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
export const Closed: Story = {
|
|
80
|
-
args: {
|
|
81
|
-
isOpen: false,
|
|
82
|
-
children: (
|
|
83
|
-
<div>
|
|
84
|
-
<h2 className="mb-4 text-xl font-bold">You won't see this</h2>
|
|
85
|
-
|
|
86
|
-
<p>This modal is closed, so the content is not visible.</p>
|
|
87
|
-
</div>
|
|
88
|
-
),
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export const SimpleMessage: Story = {
|
|
93
|
-
args: {
|
|
94
|
-
isOpen: true,
|
|
95
|
-
children: (
|
|
96
|
-
<div className="text-center">
|
|
97
|
-
<h3 className="mb-2 text-lg font-semibold">Success!</h3>
|
|
98
|
-
|
|
99
|
-
<p className="text-gray-600">Your action was completed successfully.</p>
|
|
100
|
-
</div>
|
|
101
|
-
),
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
export const Interactive: Story = {
|
|
106
38
|
render: () => {
|
|
107
|
-
const [
|
|
108
|
-
const [selectedModal, setSelectedModal] = useState<string | null>(null);
|
|
109
|
-
|
|
110
|
-
const openModal = (type: string) => {
|
|
111
|
-
setSelectedModal(type);
|
|
112
|
-
setIsOpen(true);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const closeModal = () => {
|
|
116
|
-
setIsOpen(false);
|
|
117
|
-
setSelectedModal(null);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const renderModalContent = () => {
|
|
121
|
-
switch (selectedModal) {
|
|
122
|
-
case 'info':
|
|
123
|
-
return (
|
|
124
|
-
<div>
|
|
125
|
-
<h3 className="mb-2 text-lg font-semibold">Information</h3>
|
|
126
|
-
|
|
127
|
-
<p className="text-gray-600">This is an informational modal.</p>
|
|
128
|
-
</div>
|
|
129
|
-
);
|
|
130
|
-
case 'warning':
|
|
131
|
-
return (
|
|
132
|
-
<div>
|
|
133
|
-
<h3 className="mb-2 text-lg font-semibold text-yellow-600">Warning</h3>
|
|
134
|
-
|
|
135
|
-
<p className="text-gray-600">This action requires confirmation.</p>
|
|
136
|
-
</div>
|
|
137
|
-
);
|
|
138
|
-
case 'error':
|
|
139
|
-
return (
|
|
140
|
-
<div>
|
|
141
|
-
<h3 className="mb-2 text-lg font-semibold text-red-600">Error</h3>
|
|
142
|
-
|
|
143
|
-
<p className="text-gray-600">Something went wrong. Please try again.</p>
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
default:
|
|
147
|
-
return <p>Default modal content</p>;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
39
|
+
const [open, setOpen] = useState(false);
|
|
150
40
|
|
|
151
41
|
return (
|
|
152
|
-
<
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
>
|
|
178
|
-
Error Modal
|
|
179
|
-
</button>
|
|
180
|
-
</div>
|
|
181
|
-
|
|
182
|
-
<Modal isOpen={isOpen} onClose={closeModal}>
|
|
183
|
-
{renderModalContent()}
|
|
184
|
-
</Modal>
|
|
185
|
-
</div>
|
|
42
|
+
<Modal open={open} onOpenChange={setOpen}>
|
|
43
|
+
<ModalTrigger asChild>
|
|
44
|
+
<Button>Open modal</Button>
|
|
45
|
+
</ModalTrigger>
|
|
46
|
+
|
|
47
|
+
<ModalContent>
|
|
48
|
+
<ModalHeader>
|
|
49
|
+
<ModalTitle>Confirm action</ModalTitle>
|
|
50
|
+
</ModalHeader>
|
|
51
|
+
|
|
52
|
+
<ModalDescription>
|
|
53
|
+
Are you sure you want to continue? This action cannot be undone.
|
|
54
|
+
</ModalDescription>
|
|
55
|
+
|
|
56
|
+
<ModalFooter>
|
|
57
|
+
<Button appearance="secondary" onClick={() => setOpen(false)}>
|
|
58
|
+
Cancel
|
|
59
|
+
</Button>
|
|
60
|
+
|
|
61
|
+
<ModalClose asChild>
|
|
62
|
+
<Button>Confirm</Button>
|
|
63
|
+
</ModalClose>
|
|
64
|
+
</ModalFooter>
|
|
65
|
+
</ModalContent>
|
|
66
|
+
</Modal>
|
|
186
67
|
);
|
|
187
68
|
},
|
|
188
69
|
};
|
|
@@ -1,83 +1,140 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { Dialog as DialogPrimitive } from 'radix-ui';
|
|
6
|
+
import { LuX } from 'react-icons/lu';
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
8
|
+
import { cn } from '../../utils/components';
|
|
9
|
+
import { Button } from '../Button/Button';
|
|
10
10
|
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export type ModalProps = ComponentProps<typeof DialogPrimitive.Root>;
|
|
12
|
+
|
|
13
|
+
export const Modal = ({ ...props }: ModalProps) => (
|
|
14
|
+
<DialogPrimitive.Root data-slot="modal" {...props} />
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export type ModalTriggerProps = ComponentProps<typeof DialogPrimitive.Trigger>;
|
|
18
|
+
|
|
19
|
+
export const ModalTrigger = ({ ...props }: ModalTriggerProps) => (
|
|
20
|
+
<DialogPrimitive.Trigger data-slot="modal-trigger" {...props} />
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export type ModalPortalProps = ComponentProps<typeof DialogPrimitive.Portal>;
|
|
24
|
+
|
|
25
|
+
export const ModalPortal = ({ ...props }: ModalPortalProps) => (
|
|
26
|
+
<DialogPrimitive.Portal data-slot="modal-portal" {...props} />
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export type ModalCloseProps = ComponentProps<typeof DialogPrimitive.Close>;
|
|
30
|
+
|
|
31
|
+
export const ModalClose = ({ ...props }: ModalCloseProps) => (
|
|
32
|
+
<DialogPrimitive.Close data-slot="modal-close" {...props} />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export type ModalOverlayProps = ComponentProps<typeof DialogPrimitive.Overlay>;
|
|
36
|
+
|
|
37
|
+
export const ModalOverlay = ({ className, ...props }: ModalOverlayProps) => (
|
|
38
|
+
<DialogPrimitive.Overlay
|
|
39
|
+
data-slot="modal-overlay"
|
|
40
|
+
className={cn('cn-modal-overlay fixed inset-0 isolate', className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export type ModalContentProps = ComponentProps<typeof DialogPrimitive.Content> & {
|
|
46
|
+
showCloseButton?: boolean;
|
|
15
47
|
};
|
|
16
48
|
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
className="relative z-10 w-full max-w-md p-6 bg-white rounded-lg shadow-lg"
|
|
65
|
-
onClick={(event) => {
|
|
66
|
-
// stop event from bubbling to the `dialog` element, as we don't want the modal to close if you click the content
|
|
67
|
-
event.stopPropagation();
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
<button
|
|
71
|
-
onClick={handleClose}
|
|
72
|
-
className="absolute text-gray-600 text-static-xl top-2 right-2 hover:text-black"
|
|
73
|
-
aria-label="Close modal"
|
|
74
|
-
>
|
|
75
|
-
<IoMdCloseCircle size={20} />
|
|
76
|
-
</button>
|
|
77
|
-
{children}
|
|
78
|
-
</div>
|
|
79
|
-
</dialog>,
|
|
80
|
-
document.body,
|
|
81
|
-
)
|
|
82
|
-
: null;
|
|
49
|
+
export const ModalContent = ({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
showCloseButton = true,
|
|
53
|
+
...props
|
|
54
|
+
}: ModalContentProps) => (
|
|
55
|
+
<ModalPortal>
|
|
56
|
+
<ModalOverlay />
|
|
57
|
+
|
|
58
|
+
<DialogPrimitive.Content
|
|
59
|
+
data-slot="modal-content"
|
|
60
|
+
className={cn(
|
|
61
|
+
`cn-modal-content fixed top-1/2 left-1/2 w-full -translate-x-1/2 -translate-y-1/2
|
|
62
|
+
outline-none`,
|
|
63
|
+
className,
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
|
|
69
|
+
{showCloseButton && (
|
|
70
|
+
<div className="cn-modal-close">
|
|
71
|
+
<DialogPrimitive.Close data-slot="modal-close" asChild>
|
|
72
|
+
<Button variant="icon" appearance="ghost">
|
|
73
|
+
<LuX />
|
|
74
|
+
|
|
75
|
+
<span className="sr-only">Close</span>
|
|
76
|
+
</Button>
|
|
77
|
+
</DialogPrimitive.Close>
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
</DialogPrimitive.Content>
|
|
81
|
+
</ModalPortal>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
export type ModalHeaderProps = ComponentProps<'div'>;
|
|
85
|
+
|
|
86
|
+
export const ModalHeader = ({ className, ...props }: ModalHeaderProps) => (
|
|
87
|
+
<div
|
|
88
|
+
data-slot="modal-header"
|
|
89
|
+
className={cn('cn-modal-header flex flex-col', className)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
export type ModalFooterProps = ComponentProps<'div'> & {
|
|
95
|
+
showCloseButton?: boolean;
|
|
83
96
|
};
|
|
97
|
+
|
|
98
|
+
export const ModalFooter = ({
|
|
99
|
+
className,
|
|
100
|
+
showCloseButton = false,
|
|
101
|
+
children,
|
|
102
|
+
...props
|
|
103
|
+
}: ModalFooterProps) => (
|
|
104
|
+
<div
|
|
105
|
+
data-slot="modal-footer"
|
|
106
|
+
className={cn(
|
|
107
|
+
'cn-modal-footer flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
|
108
|
+
className,
|
|
109
|
+
)}
|
|
110
|
+
{...props}
|
|
111
|
+
>
|
|
112
|
+
{children}
|
|
113
|
+
|
|
114
|
+
{showCloseButton && (
|
|
115
|
+
<DialogPrimitive.Close asChild>
|
|
116
|
+
<Button appearance="secondary">Close</Button>
|
|
117
|
+
</DialogPrimitive.Close>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
export type ModalTitleProps = ComponentProps<typeof DialogPrimitive.Title>;
|
|
123
|
+
|
|
124
|
+
export const ModalTitle = ({ className, ...props }: ModalTitleProps) => (
|
|
125
|
+
<DialogPrimitive.Title
|
|
126
|
+
data-slot="modal-title"
|
|
127
|
+
className={cn('cn-modal-title cn-font-heading', className)}
|
|
128
|
+
{...props}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
export type ModalDescriptionProps = ComponentProps<typeof DialogPrimitive.Description>;
|
|
133
|
+
|
|
134
|
+
export const ModalDescription = ({ className, ...props }: ModalDescriptionProps) => (
|
|
135
|
+
<DialogPrimitive.Description
|
|
136
|
+
data-slot="modal-description"
|
|
137
|
+
className={cn('cn-modal-description', className)}
|
|
138
|
+
{...props}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@layer components {
|
|
2
|
+
/* MARK: NoScript banner
|
|
3
|
+
Full-width accent-coloured bar. Intended as the very first element in
|
|
4
|
+
the DOM, above the header, same position as the cookie banner, so it's
|
|
5
|
+
unmissable and consistent across every app. role="region" + aria-label
|
|
6
|
+
keeps it discoverable via landmark navigation even though it sits
|
|
7
|
+
outside both the header and main landmarks. */
|
|
8
|
+
.cn-noscript-banner {
|
|
9
|
+
@apply bg-accent text-on-accent py-gds-4;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* MARK: Inner wrapper
|
|
13
|
+
Centres and constrains content, matching cn-cookie-banner-inner. */
|
|
14
|
+
.cn-noscript-banner-inner {
|
|
15
|
+
@apply mx-auto max-w-page px-gds-4;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { NoScriptBanner } from './NoScriptBanner';
|
|
4
|
+
import { Heading } from '../Heading/Heading';
|
|
5
|
+
import { HeadingGroup } from '../Layout/HeadingGroup/HeadingGroup';
|
|
6
|
+
import { Paragraph } from '../Paragraph/Paragraph';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof NoScriptBanner> = {
|
|
9
|
+
title: 'Components/NoScriptBanner',
|
|
10
|
+
component: NoScriptBanner,
|
|
11
|
+
tags: ['autodocs'],
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: `
|
|
16
|
+
Full-width accent-coloured banner shown only when JavaScript is disabled. Renders inside a real \`<noscript>\` element, so the browser (not React) decides whether to show it, place it as the very first element in the DOM, above the header, the same position as the cookie banner.
|
|
17
|
+
|
|
18
|
+
The title ("JavaScript is not available") is fixed for consistency across apps. Pass children to explain what that means for your specific service.
|
|
19
|
+
|
|
20
|
+
Because Storybook itself runs on JavaScript, the browser will never actually display this component's \`<noscript>\` content here. The story below is a plain, unwrapped reproduction of the same markup for visual reference only, disable JavaScript in your browser's dev tools against a real page to see the genuine behaviour.
|
|
21
|
+
`.trim(),
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
|
|
29
|
+
type Story = StoryObj<typeof NoScriptBanner>;
|
|
30
|
+
|
|
31
|
+
export const Preview: Story = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<div className="cn-noscript-banner">
|
|
34
|
+
<div className="cn-noscript-banner-inner">
|
|
35
|
+
<HeadingGroup>
|
|
36
|
+
<Heading level="h2">JavaScript is not available</Heading>
|
|
37
|
+
|
|
38
|
+
<Paragraph>
|
|
39
|
+
This service needs JavaScript to work. Enable JavaScript in your browser, or contact us
|
|
40
|
+
for support with an alternative way to apply.
|
|
41
|
+
</Paragraph>
|
|
42
|
+
</HeadingGroup>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
),
|
|
46
|
+
};
|