@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
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ExtendProps } from '../../types/utils';
|
|
4
|
-
import { cn } from '../../utils';
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
hasError?: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type TextAreaProps = ExtendProps<'textarea', Props>;
|
|
11
|
-
|
|
12
|
-
export const TextArea = ({ hasError, className, ...props }: TextAreaProps) => {
|
|
13
|
-
return (
|
|
14
|
-
<textarea
|
|
15
|
-
{...props}
|
|
16
|
-
className={cn(
|
|
17
|
-
'border p-1 disabled:opacity-60 disabled:bg-gray-100',
|
|
18
|
-
hasError ? 'border-error' : 'border-black',
|
|
19
|
-
className,
|
|
20
|
-
)}
|
|
21
|
-
/>
|
|
22
|
-
);
|
|
23
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Copyright } from './Copyright';
|
|
2
|
-
import { Licence } from './Licence';
|
|
3
|
-
import { MetaLinks } from './MetaLinks';
|
|
4
|
-
|
|
5
|
-
export const Footer = () => (
|
|
6
|
-
<footer className="border-2 border-t-gray-200 bg-gray-100 p-3 flex items-center justify-center">
|
|
7
|
-
<div
|
|
8
|
-
className="grid w-full max-w-7xl gap-2 grid-cols-1 md:grid-cols-[1fr_min-content]
|
|
9
|
-
grid-rows-[min-content_min-content_min-content] md:grid-rows-[min-content_min-content]"
|
|
10
|
-
>
|
|
11
|
-
<nav className="row-start-1 col-start-1 md:col-span-2 w-full pb-2">
|
|
12
|
-
<h2 className="sr-only">Support Links</h2>
|
|
13
|
-
|
|
14
|
-
<MetaLinks />
|
|
15
|
-
</nav>
|
|
16
|
-
|
|
17
|
-
<div className="row-start-2 col-start-1 pt-1">
|
|
18
|
-
<Licence />
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<div className="row-start-3 col-start-1 md:row-start-2 md:col-start-2 w-full h-min">
|
|
22
|
-
<Copyright />
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
</footer>
|
|
26
|
-
);
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { OglLogo } from '../../images/OglLogo';
|
|
2
|
-
import { ExternalLink } from '../../link/ExternalLink';
|
|
3
|
-
|
|
4
|
-
export const Licence = () => (
|
|
5
|
-
<div className="flex gap-2 w-full items-center">
|
|
6
|
-
<OglLogo className="inline h-9 w-9" aria-hidden />
|
|
7
|
-
|
|
8
|
-
<span className="text-xs text-black break-words flex-auto">
|
|
9
|
-
All content is available under the{' '}
|
|
10
|
-
<ExternalLink
|
|
11
|
-
className="inline-flex break-words min-w-0 text-black"
|
|
12
|
-
href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/"
|
|
13
|
-
>
|
|
14
|
-
Open Government Licence v3.0
|
|
15
|
-
</ExternalLink>
|
|
16
|
-
, except where otherwise stated.
|
|
17
|
-
</span>
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Header } from './Header';
|
|
2
|
-
import { render, screen } from '../../../test/renderers';
|
|
3
|
-
import type { NavLink } from '../../../types/navigation';
|
|
4
|
-
|
|
5
|
-
const NAV_LINKS: NavLink[] = [
|
|
6
|
-
{ label: 'Home', url: '/', openInNewTab: false },
|
|
7
|
-
{ label: 'API', url: '/api-docs', openInNewTab: false },
|
|
8
|
-
{ label: 'Support', url: 'https://example.com/support', openInNewTab: true },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
describe('Header', () => {
|
|
12
|
-
it('renders the primary brand links and app name', async () => {
|
|
13
|
-
render(
|
|
14
|
-
<Header
|
|
15
|
-
credentials={null}
|
|
16
|
-
dspUrl="https://example.com"
|
|
17
|
-
appName="Wetlands Inventory"
|
|
18
|
-
navLinks={NAV_LINKS}
|
|
19
|
-
/>,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
expect(
|
|
23
|
-
screen.getByRole('link', {
|
|
24
|
-
name: /department for environment food & rural affairs/i,
|
|
25
|
-
}),
|
|
26
|
-
).toBeInTheDocument();
|
|
27
|
-
|
|
28
|
-
expect(screen.getByRole('link', { name: /data services platform/i })).toBeInTheDocument();
|
|
29
|
-
expect(screen.getByRole('link', { name: /wetlands inventory/i })).toBeInTheDocument();
|
|
30
|
-
|
|
31
|
-
const loginLink = await screen.findByRole('link', { name: /login/i });
|
|
32
|
-
|
|
33
|
-
expect(loginLink).toBeInTheDocument();
|
|
34
|
-
expect(screen.getByRole('button', { name: /open menu/i })).toBeInTheDocument();
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { ComponentType } from 'react';
|
|
2
|
-
|
|
3
|
-
import { FaHouse } from 'react-icons/fa6';
|
|
4
|
-
|
|
5
|
-
import { HeaderAuthClient } from './HeaderAuthClient';
|
|
6
|
-
import { HeaderNavClient } from './HeaderNavClient';
|
|
7
|
-
import type { Credentials } from '../../../types/auth';
|
|
8
|
-
import type { NavLink } from '../../../types/navigation';
|
|
9
|
-
import { DefraLogo } from '../../images/DefraLogo';
|
|
10
|
-
import { EaLogo } from '../../images/EaLogo';
|
|
11
|
-
import { ExternalLink } from '../../link/ExternalLink';
|
|
12
|
-
import { Link } from '../../link/Link';
|
|
13
|
-
|
|
14
|
-
type HeaderProps = {
|
|
15
|
-
credentials: Credentials | null;
|
|
16
|
-
dspUrl: string;
|
|
17
|
-
appName: string;
|
|
18
|
-
navLinks: NavLink[];
|
|
19
|
-
// headerAuthClientComponent: ComponentType<{ credentials: Credentials | null; hostname: string }>;
|
|
20
|
-
headerNavClientComponent?: ComponentType<{ navLinks: NavLink[] }>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const Header = ({
|
|
24
|
-
credentials,
|
|
25
|
-
dspUrl,
|
|
26
|
-
appName,
|
|
27
|
-
navLinks,
|
|
28
|
-
// headerAuthClientComponent,
|
|
29
|
-
headerNavClientComponent = HeaderNavClient,
|
|
30
|
-
}: HeaderProps) => {
|
|
31
|
-
const HeaderNav = headerNavClientComponent;
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<header className="bg-black w-full text-white border-b-2 border-brand">
|
|
35
|
-
<div
|
|
36
|
-
className="grid grid-rows-[min-content_min-content] grid-cols-2 md:grid-cols-3 gap-x-4
|
|
37
|
-
gap-y-2 md:gap-y-0 py-2 px-3 border-b border-gray-500"
|
|
38
|
-
>
|
|
39
|
-
<ExternalLink
|
|
40
|
-
href="https://www.gov.uk/government/organisations/department-for-environment-food-rural-affairs"
|
|
41
|
-
className="justify-center text-white visited:text-white active:text-black hover:text-white
|
|
42
|
-
text-base"
|
|
43
|
-
>
|
|
44
|
-
<span className="flex flex-col sm:flex-row sm:items-center gap-1 flex-1">
|
|
45
|
-
<DefraLogo className="w-[50px] h-[50px]" />
|
|
46
|
-
|
|
47
|
-
<span>Department for Environment Food & Rural Affairs</span>
|
|
48
|
-
</span>
|
|
49
|
-
</ExternalLink>
|
|
50
|
-
|
|
51
|
-
<ExternalLink
|
|
52
|
-
href="/"
|
|
53
|
-
className="flex gap-1 text-base items-center text-white md:justify-self-center
|
|
54
|
-
justify-self-end h-min self-center visited:text-white active:text-black
|
|
55
|
-
hover:text-white"
|
|
56
|
-
>
|
|
57
|
-
<FaHouse className="shrink-0" />
|
|
58
|
-
|
|
59
|
-
<span>Data Services Platform</span>
|
|
60
|
-
</ExternalLink>
|
|
61
|
-
|
|
62
|
-
{dspUrl ? <HeaderAuthClient hostname={dspUrl} credentials={credentials} /> : <></>}
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div
|
|
66
|
-
className="w-full bg-white grid grid-cols-[1fr_min-content]
|
|
67
|
-
2xs:grid-cols-[min-content_minmax(0,_1fr)_min-content] grid-rows-1 sm:grid-rows-none py-2
|
|
68
|
-
px-1 sm:px-3 items-center gap-y-4 gap-2"
|
|
69
|
-
>
|
|
70
|
-
<ExternalLink
|
|
71
|
-
href="https://www.gov.uk/government/organisations/environment-agency"
|
|
72
|
-
className="justify-self-start text-black hidden 2xs:block min-w-32"
|
|
73
|
-
>
|
|
74
|
-
<EaLogo className="text-nowrap overflow-hidden pl-2" />
|
|
75
|
-
</ExternalLink>
|
|
76
|
-
|
|
77
|
-
<Link
|
|
78
|
-
href="/"
|
|
79
|
-
className="text-center font-bold text-base sm:text-lg text-black visited:text-black
|
|
80
|
-
active:text-black hover:text-black"
|
|
81
|
-
>
|
|
82
|
-
{appName}
|
|
83
|
-
</Link>
|
|
84
|
-
|
|
85
|
-
{/* <HeaderNavClient navLinks={navLinks} /> */}
|
|
86
|
-
<HeaderNav navLinks={navLinks} />
|
|
87
|
-
</div>
|
|
88
|
-
</header>
|
|
89
|
-
);
|
|
90
|
-
};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { HeaderAuthClient } from './HeaderAuthClient';
|
|
2
|
-
import { render, screen } from '../../../test/renderers';
|
|
3
|
-
import type { Credentials } from '../../../types/auth';
|
|
4
|
-
|
|
5
|
-
const AUTH_CREDENTIALS: Credentials = {
|
|
6
|
-
token: 'token-123',
|
|
7
|
-
user: {
|
|
8
|
-
name: 'Jamie Taylor',
|
|
9
|
-
email: 'jamie.taylor@example.com',
|
|
10
|
-
groupInfoIds: ['group-1'],
|
|
11
|
-
},
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
describe('HeaderAuthClient', () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
globalThis.history.pushState({}, '', '/header-test');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('renders guest state with login link', async () => {
|
|
20
|
-
render(<HeaderAuthClient hostname="https://example.com" credentials={null} />);
|
|
21
|
-
|
|
22
|
-
expect(screen.getByText(/welcome,/i)).toBeInTheDocument();
|
|
23
|
-
expect(screen.getByText(/guest/i)).toBeInTheDocument();
|
|
24
|
-
|
|
25
|
-
const loginLink = await screen.findByRole('link', { name: /login/i });
|
|
26
|
-
|
|
27
|
-
expect(loginLink).toHaveAttribute(
|
|
28
|
-
'href',
|
|
29
|
-
`https://example.com/login?redirect-uri=${encodeURIComponent(globalThis.location.href)}`,
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('renders authenticated state with logout link', async () => {
|
|
34
|
-
render(<HeaderAuthClient hostname="https://example.com" credentials={AUTH_CREDENTIALS} />);
|
|
35
|
-
|
|
36
|
-
expect(screen.getByText(/jamie.taylor@example.com/i)).toBeInTheDocument();
|
|
37
|
-
|
|
38
|
-
const logoutLink = await screen.findByRole('link', { name: /logout/i });
|
|
39
|
-
|
|
40
|
-
expect(logoutLink).toHaveAttribute(
|
|
41
|
-
'href',
|
|
42
|
-
`https://example.com/api/logout?redirect-uri=${encodeURIComponent(globalThis.location.href)}`,
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
import type { Credentials } from '../../../types/auth';
|
|
6
|
-
import { Link } from '../../link/Link';
|
|
7
|
-
|
|
8
|
-
type HeaderAuthClientProps = {
|
|
9
|
-
credentials: Credentials | null;
|
|
10
|
-
hostname: string;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const HeaderAuthClient = ({ hostname, credentials }: HeaderAuthClientProps) => {
|
|
14
|
-
const [isClient, setIsClient] = useState(false);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
setIsClient(true);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<span
|
|
22
|
-
className="flex gap-2 justify-self-start text-base col-span-2 row-start-2 col-start-1
|
|
23
|
-
md:row-start-1 md:col-start-3 h-min md:justify-self-end self-center items-center"
|
|
24
|
-
>
|
|
25
|
-
<span className="text-base border-r border-white pr-2 wrap break-anywhere sm:break-word">
|
|
26
|
-
<span>Welcome,</span> {credentials ? credentials.user.email : 'Guest'}
|
|
27
|
-
</span>
|
|
28
|
-
|
|
29
|
-
{isClient ? (
|
|
30
|
-
<Link
|
|
31
|
-
href={`${hostname}/${credentials ? 'api/logout' : 'login'}?redirect-uri=${encodeURIComponent(window.location.href)}`}
|
|
32
|
-
className="text-white visited:text-white active:text-black hover:text-white"
|
|
33
|
-
prefetch={false}
|
|
34
|
-
>
|
|
35
|
-
<span className="text-base">{credentials ? 'Logout' : 'Login'}</span>
|
|
36
|
-
</Link>
|
|
37
|
-
) : null}
|
|
38
|
-
</span>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { HeaderNavClient } from './HeaderNavClient';
|
|
2
|
-
import { render, screen, userEvent } from '../../../test/renderers';
|
|
3
|
-
import type { NavLink } from '../../../types/navigation';
|
|
4
|
-
|
|
5
|
-
const NAV_LINKS: NavLink[] = [
|
|
6
|
-
{ label: 'Home', url: '/', openInNewTab: false },
|
|
7
|
-
{ label: 'API', url: '/api-docs', openInNewTab: false },
|
|
8
|
-
{ label: 'Support', url: 'https://example.com/support', openInNewTab: true },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
describe('HeaderNavClient', () => {
|
|
12
|
-
it('renders both navigation regions', () => {
|
|
13
|
-
render(<HeaderNavClient navLinks={NAV_LINKS} />);
|
|
14
|
-
|
|
15
|
-
expect(
|
|
16
|
-
screen.getByRole('navigation', { name: /small screen navigation/i }),
|
|
17
|
-
).toBeInTheDocument();
|
|
18
|
-
expect(
|
|
19
|
-
screen.getByRole('navigation', { name: /large screen navigation/i }),
|
|
20
|
-
).toBeInTheDocument();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('shows menu items in the small-screen dropdown', async () => {
|
|
24
|
-
render(<HeaderNavClient navLinks={NAV_LINKS} />);
|
|
25
|
-
|
|
26
|
-
const user = userEvent.setup();
|
|
27
|
-
|
|
28
|
-
await user.click(screen.getByRole('button', { name: /open menu/i }));
|
|
29
|
-
|
|
30
|
-
expect(await screen.findByRole('menuitem', { name: /home/i })).toBeInTheDocument();
|
|
31
|
-
expect(await screen.findByRole('menuitem', { name: /api/i })).toBeInTheDocument();
|
|
32
|
-
expect(await screen.findByRole('menuitem', { name: /support/i })).toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('renders large-screen links for navigation', () => {
|
|
36
|
-
render(<HeaderNavClient navLinks={NAV_LINKS} />);
|
|
37
|
-
|
|
38
|
-
const supportLink = screen.getByRole('link', { name: /support/i });
|
|
39
|
-
|
|
40
|
-
expect(supportLink).toHaveAttribute('href', 'https://example.com/support');
|
|
41
|
-
expect(screen.getByRole('link', { name: /home/i })).toBeInTheDocument();
|
|
42
|
-
expect(screen.getByRole('link', { name: /api/i })).toBeInTheDocument();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { NavLink } from '../../../types/navigation';
|
|
4
|
-
import { DropdownMenu, type DropdownMenuItem } from '../../dropdown/DropdownMenu';
|
|
5
|
-
import { ExternalLink } from '../../link/ExternalLink';
|
|
6
|
-
import { Link } from '../../link/Link';
|
|
7
|
-
|
|
8
|
-
type HeaderNavClientProps = {
|
|
9
|
-
navLinks: NavLink[];
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const ExternalNavItem = ({ label, url, icon, ...props }: DropdownMenuItem<NavLink>) => (
|
|
13
|
-
<ExternalLink href={url} {...props}>
|
|
14
|
-
{icon ?? null}
|
|
15
|
-
<span>{label}</span>
|
|
16
|
-
</ExternalLink>
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const InternalNavItem = ({ label, url, icon, ...props }: DropdownMenuItem<NavLink>) => (
|
|
20
|
-
<Link {...props} href={url}>
|
|
21
|
-
{icon ?? null}
|
|
22
|
-
<span>{label}</span>
|
|
23
|
-
</Link>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const NavItem = ({ url, openInNewTab, ...props }: DropdownMenuItem<NavLink>) => {
|
|
27
|
-
const isExternal = /^https?:\/\//.test(url);
|
|
28
|
-
const newTabProps = openInNewTab ? { target: '_blank' as const, rel: 'noopener noreferrer' } : {};
|
|
29
|
-
|
|
30
|
-
if (isExternal) {
|
|
31
|
-
return <ExternalNavItem url={url} {...newTabProps} {...props} />;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return <InternalNavItem url={url} {...newTabProps} {...props} />;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export const HeaderNavClient = ({ navLinks }: HeaderNavClientProps) => {
|
|
38
|
-
return (
|
|
39
|
-
<>
|
|
40
|
-
<nav className="block sm:hidden" aria-label="Small Screen Navigation">
|
|
41
|
-
<DropdownMenu
|
|
42
|
-
items={navLinks}
|
|
43
|
-
itemRenderer={NavItem}
|
|
44
|
-
itemClassName="text-black cursor-pointer px-2 py-1 gap-1 text-base flex items-center shrink-0"
|
|
45
|
-
/>
|
|
46
|
-
</nav>
|
|
47
|
-
|
|
48
|
-
<nav
|
|
49
|
-
className="hidden sm:block col-span-full sm:col-span-1 sm:justify-self-end sm:gap-4 min-w-0
|
|
50
|
-
px-2 gap-2"
|
|
51
|
-
aria-label="Large Screen Navigation"
|
|
52
|
-
>
|
|
53
|
-
<ul className="flex flex-row gap-2 order-1 shrink-0">
|
|
54
|
-
{navLinks.map((link) => (
|
|
55
|
-
<li key={link.label} className="w-full py-1">
|
|
56
|
-
<NavItem
|
|
57
|
-
{...link}
|
|
58
|
-
className="text-black visited:text-black active:text-black hover:text-black gap-1
|
|
59
|
-
text-base flex items-center justify-center shrink-0 w-max"
|
|
60
|
-
/>
|
|
61
|
-
</li>
|
|
62
|
-
))}
|
|
63
|
-
</ul>
|
|
64
|
-
</nav>
|
|
65
|
-
</>
|
|
66
|
-
);
|
|
67
|
-
};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
|
|
3
|
-
import { ExternalLink } from './ExternalLink';
|
|
4
|
-
|
|
5
|
-
describe('ExternalLink', () => {
|
|
6
|
-
it('renders with children', () => {
|
|
7
|
-
render(<ExternalLink href="https://example.com">Visit Example</ExternalLink>);
|
|
8
|
-
|
|
9
|
-
const link = screen.getByRole('link', { name: /Visit Example/i });
|
|
10
|
-
|
|
11
|
-
expect(link).toBeInTheDocument();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('sets target="_blank" to open in new tab', () => {
|
|
15
|
-
render(<ExternalLink href="https://example.com">External Link</ExternalLink>);
|
|
16
|
-
|
|
17
|
-
const link = screen.getByRole('link');
|
|
18
|
-
|
|
19
|
-
expect(link).toHaveAttribute('target', '_blank');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('sets rel="noopener noreferrer" for security', () => {
|
|
23
|
-
render(<ExternalLink href="https://example.com">External Link</ExternalLink>);
|
|
24
|
-
|
|
25
|
-
const link = screen.getByRole('link');
|
|
26
|
-
|
|
27
|
-
expect(link).toHaveAttribute('rel', 'noopener noreferrer');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('includes screen reader text indicating new tab', () => {
|
|
31
|
-
render(<ExternalLink href="https://example.com">Visit Site</ExternalLink>);
|
|
32
|
-
|
|
33
|
-
// The accessible name should include the "(opens in new tab)" text
|
|
34
|
-
const link = screen.getByRole('link', { name: /Visit Site\(opens in new tab\)/i });
|
|
35
|
-
|
|
36
|
-
expect(link).toBeInTheDocument();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('applies custom className', () => {
|
|
40
|
-
render(
|
|
41
|
-
<ExternalLink href="https://example.com" className="custom-class">
|
|
42
|
-
Link
|
|
43
|
-
</ExternalLink>,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const link = screen.getByRole('link');
|
|
47
|
-
|
|
48
|
-
expect(link).toHaveClass('custom-class');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('preserves default link styling classes', () => {
|
|
52
|
-
render(<ExternalLink href="https://example.com">Link</ExternalLink>);
|
|
53
|
-
|
|
54
|
-
const link = screen.getByRole('link');
|
|
55
|
-
|
|
56
|
-
expect(link).toHaveClass('text-link');
|
|
57
|
-
expect(link).toHaveClass('underline');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('applies href attribute correctly', () => {
|
|
61
|
-
render(<ExternalLink href="https://example.com">Link</ExternalLink>);
|
|
62
|
-
|
|
63
|
-
const link = screen.getByRole('link');
|
|
64
|
-
|
|
65
|
-
expect(link).toHaveAttribute('href', 'https://example.com');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('forwards additional props to anchor element', () => {
|
|
69
|
-
render(
|
|
70
|
-
<ExternalLink href="https://example.com" data-testid="external-link">
|
|
71
|
-
Link
|
|
72
|
-
</ExternalLink>,
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const link = screen.getByTestId('external-link');
|
|
76
|
-
|
|
77
|
-
expect(link).toBeInTheDocument();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('renders with complex children', () => {
|
|
81
|
-
render(
|
|
82
|
-
<ExternalLink href="https://example.com">
|
|
83
|
-
<span>Complex </span>
|
|
84
|
-
|
|
85
|
-
<strong>Content</strong>
|
|
86
|
-
</ExternalLink>,
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const link = screen.getByRole('link');
|
|
90
|
-
|
|
91
|
-
expect(link).toBeInTheDocument();
|
|
92
|
-
expect(link).toHaveTextContent('Complex Content');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('screen reader text is visually hidden but accessible', () => {
|
|
96
|
-
render(<ExternalLink href="https://example.com">Link</ExternalLink>);
|
|
97
|
-
|
|
98
|
-
const srText = screen.getByText('(opens in new tab)', { exact: false });
|
|
99
|
-
|
|
100
|
-
expect(srText).toHaveClass('sr-only');
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
import type { ExtendProps } from '../../types/utils';
|
|
4
|
-
import { cn } from '../../utils';
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
children: ReactNode;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type ExternalLinkProps = ExtendProps<'a', Props>;
|
|
11
|
-
|
|
12
|
-
export const ExternalLink = ({ href, className, children, ...props }: ExternalLinkProps) => (
|
|
13
|
-
<a
|
|
14
|
-
{...props}
|
|
15
|
-
className={cn(
|
|
16
|
-
`cursor-pointer text-link hover:decoration-[max(3px,_.1875rem,_.12em)] hover:text-link-hover
|
|
17
|
-
visited:text-link-visited focus:decoration-[max(3px,_.1875rem,_.12em)]
|
|
18
|
-
decoration-[max(1px,_.0625rem)] underline-offset-[0.1578em] underline outline-none
|
|
19
|
-
focus:text-focus-text focus:bg-focus inline-block`,
|
|
20
|
-
className,
|
|
21
|
-
)}
|
|
22
|
-
href={href}
|
|
23
|
-
rel="noopener noreferrer"
|
|
24
|
-
target="_blank"
|
|
25
|
-
>
|
|
26
|
-
{children}
|
|
27
|
-
<span className="sr-only"> (opens in new tab)</span>
|
|
28
|
-
</a>
|
|
29
|
-
);
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import NextLink from 'next/link';
|
|
2
|
-
|
|
3
|
-
import type { ExtendProps } from '../../types/utils';
|
|
4
|
-
import { cn } from '../../utils';
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
href: string | object;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type LinkProps = ExtendProps<typeof NextLink, Props>;
|
|
11
|
-
|
|
12
|
-
export const Link = ({ href, className, children, ...props }: LinkProps) => (
|
|
13
|
-
<NextLink
|
|
14
|
-
{...props}
|
|
15
|
-
className={cn(
|
|
16
|
-
`cursor-pointer text-link hover:decoration-[max(3px,_.1875rem,_.12em)] hover:text-link-hover
|
|
17
|
-
visited:text-link-visited active:text-black focus:decoration-[max(3px,_.1875rem,_.12em)]
|
|
18
|
-
decoration-[max(1px,_.0625rem)] underline-offset-[0.1578em] underline outline-none
|
|
19
|
-
focus:text-focus-text focus:bg-focus inline-block`,
|
|
20
|
-
className,
|
|
21
|
-
)}
|
|
22
|
-
href={href}
|
|
23
|
-
>
|
|
24
|
-
<>{children}</>
|
|
25
|
-
</NextLink>
|
|
26
|
-
);
|