@regardio/react 0.3.2
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/LICENSE +9 -0
- package/README.md +133 -0
- package/dist/components/background-slideshow.d.ts +22 -0
- package/dist/components/background-slideshow.d.ts.map +1 -0
- package/dist/components/background-slideshow.js +76 -0
- package/dist/components/blurry-gradient.d.ts +9 -0
- package/dist/components/blurry-gradient.d.ts.map +1 -0
- package/dist/components/blurry-gradient.js +6 -0
- package/dist/components/box.d.ts +17 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +25 -0
- package/dist/components/carousel.d.ts +26 -0
- package/dist/components/carousel.d.ts.map +1 -0
- package/dist/components/carousel.js +87 -0
- package/dist/components/countdown.d.ts +2 -0
- package/dist/components/countdown.d.ts.map +1 -0
- package/dist/components/countdown.js +48 -0
- package/dist/components/definition-list.d.ts +40 -0
- package/dist/components/definition-list.d.ts.map +1 -0
- package/dist/components/definition-list.js +57 -0
- package/dist/components/generic-error.d.ts +23 -0
- package/dist/components/generic-error.d.ts.map +1 -0
- package/dist/components/generic-error.js +39 -0
- package/dist/components/heading.d.ts +24 -0
- package/dist/components/heading.d.ts.map +1 -0
- package/dist/components/heading.js +29 -0
- package/dist/components/highlight.d.ts +16 -0
- package/dist/components/highlight.d.ts.map +1 -0
- package/dist/components/highlight.js +19 -0
- package/dist/components/icon-button.d.ts +6 -0
- package/dist/components/icon-button.d.ts.map +1 -0
- package/dist/components/icon-button.js +6 -0
- package/dist/components/if.d.ts +8 -0
- package/dist/components/if.d.ts.map +1 -0
- package/dist/components/if.js +16 -0
- package/dist/components/iframe.d.ts +8 -0
- package/dist/components/iframe.d.ts.map +1 -0
- package/dist/components/iframe.js +3 -0
- package/dist/components/item.d.ts +67 -0
- package/dist/components/item.d.ts.map +1 -0
- package/dist/components/item.js +309 -0
- package/dist/components/leaflet-map.d.ts +33 -0
- package/dist/components/leaflet-map.d.ts.map +1 -0
- package/dist/components/leaflet-map.js +195 -0
- package/dist/components/link.d.ts +41 -0
- package/dist/components/link.d.ts.map +1 -0
- package/dist/components/link.js +111 -0
- package/dist/components/link.test.d.ts +2 -0
- package/dist/components/link.test.d.ts.map +1 -0
- package/dist/components/link.test.js +204 -0
- package/dist/components/list-item.d.ts +16 -0
- package/dist/components/list-item.d.ts.map +1 -0
- package/dist/components/list-item.js +19 -0
- package/dist/components/maptiler-map.d.ts +26 -0
- package/dist/components/maptiler-map.d.ts.map +1 -0
- package/dist/components/maptiler-map.js +116 -0
- package/dist/components/markdown-container.d.ts +20 -0
- package/dist/components/markdown-container.d.ts.map +1 -0
- package/dist/components/markdown-container.js +93 -0
- package/dist/components/password-input.d.ts +9 -0
- package/dist/components/password-input.d.ts.map +1 -0
- package/dist/components/password-input.js +13 -0
- package/dist/components/picture.d.ts +26 -0
- package/dist/components/picture.d.ts.map +1 -0
- package/dist/components/picture.js +36 -0
- package/dist/components/protected-email.d.ts +9 -0
- package/dist/components/protected-email.d.ts.map +1 -0
- package/dist/components/protected-email.js +16 -0
- package/dist/components/text.d.ts +17 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +27 -0
- package/dist/components/unordered-list.d.ts +16 -0
- package/dist/components/unordered-list.d.ts.map +1 -0
- package/dist/components/unordered-list.js +21 -0
- package/dist/hooks/use-current-route-data.d.ts +2 -0
- package/dist/hooks/use-current-route-data.d.ts.map +1 -0
- package/dist/hooks/use-current-route-data.js +12 -0
- package/dist/hooks/use-focus-search.d.ts +3 -0
- package/dist/hooks/use-focus-search.d.ts.map +1 -0
- package/dist/hooks/use-focus-search.js +15 -0
- package/dist/hooks/use-matches-data.d.ts +2 -0
- package/dist/hooks/use-matches-data.d.ts.map +1 -0
- package/dist/hooks/use-matches-data.js +11 -0
- package/dist/hooks/use-media-query.d.ts +2 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +16 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-mobile.js +15 -0
- package/dist/hooks/use-nonce.d.ts +5 -0
- package/dist/hooks/use-nonce.d.ts.map +1 -0
- package/dist/hooks/use-nonce.js +9 -0
- package/dist/hooks/use-nonce.test.d.ts +2 -0
- package/dist/hooks/use-nonce.test.d.ts.map +1 -0
- package/dist/hooks/use-nonce.test.js +27 -0
- package/dist/hooks/use-orientation.d.ts +3 -0
- package/dist/hooks/use-orientation.d.ts.map +1 -0
- package/dist/hooks/use-orientation.js +25 -0
- package/dist/hooks/use-user.d.ts +15 -0
- package/dist/hooks/use-user.d.ts.map +1 -0
- package/dist/hooks/use-user.js +17 -0
- package/dist/shadcn/accordion.d.ts +25 -0
- package/dist/shadcn/accordion.d.ts.map +1 -0
- package/dist/shadcn/accordion.js +45 -0
- package/dist/shadcn/alert-dialog.d.ts +73 -0
- package/dist/shadcn/alert-dialog.d.ts.map +1 -0
- package/dist/shadcn/alert-dialog.js +93 -0
- package/dist/shadcn/alert.d.ts +34 -0
- package/dist/shadcn/alert.d.ts.map +1 -0
- package/dist/shadcn/alert.js +42 -0
- package/dist/shadcn/aspect-ratio.d.ts +6 -0
- package/dist/shadcn/aspect-ratio.d.ts.map +1 -0
- package/dist/shadcn/aspect-ratio.js +7 -0
- package/dist/shadcn/avatar.d.ts +16 -0
- package/dist/shadcn/avatar.d.ts.map +1 -0
- package/dist/shadcn/avatar.js +27 -0
- package/dist/shadcn/badge.d.ts +29 -0
- package/dist/shadcn/badge.d.ts.map +1 -0
- package/dist/shadcn/badge.js +29 -0
- package/dist/shadcn/breadcrumb.d.ts +42 -0
- package/dist/shadcn/breadcrumb.d.ts.map +1 -0
- package/dist/shadcn/breadcrumb.js +74 -0
- package/dist/shadcn/button.d.ts +38 -0
- package/dist/shadcn/button.d.ts.map +1 -0
- package/dist/shadcn/button.js +38 -0
- package/dist/shadcn/calendar.d.ts +23 -0
- package/dist/shadcn/calendar.d.ts.map +1 -0
- package/dist/shadcn/calendar.js +163 -0
- package/dist/shadcn/card.d.ts +31 -0
- package/dist/shadcn/card.d.ts.map +1 -0
- package/dist/shadcn/card.js +55 -0
- package/dist/shadcn/carousel.d.ts +51 -0
- package/dist/shadcn/carousel.d.ts.map +1 -0
- package/dist/shadcn/carousel.js +161 -0
- package/dist/shadcn/chart.d.ts +103 -0
- package/dist/shadcn/chart.d.ts.map +1 -0
- package/dist/shadcn/chart.js +248 -0
- package/dist/shadcn/checkbox.d.ts +8 -0
- package/dist/shadcn/checkbox.d.ts.map +1 -0
- package/dist/shadcn/checkbox.js +22 -0
- package/dist/shadcn/collapsible.d.ts +16 -0
- package/dist/shadcn/collapsible.d.ts.map +1 -0
- package/dist/shadcn/collapsible.js +19 -0
- package/dist/shadcn/command.d.ts +61 -0
- package/dist/shadcn/command.d.ts.map +1 -0
- package/dist/shadcn/command.js +123 -0
- package/dist/shadcn/context-menu.d.ts +107 -0
- package/dist/shadcn/context-menu.d.ts.map +1 -0
- package/dist/shadcn/context-menu.js +151 -0
- package/dist/shadcn/dialog.d.ts +57 -0
- package/dist/shadcn/dialog.d.ts.map +1 -0
- package/dist/shadcn/dialog.js +96 -0
- package/dist/shadcn/drawer.d.ts +54 -0
- package/dist/shadcn/drawer.d.ts.map +1 -0
- package/dist/shadcn/drawer.js +96 -0
- package/dist/shadcn/dropdown-menu.d.ts +110 -0
- package/dist/shadcn/dropdown-menu.d.ts.map +1 -0
- package/dist/shadcn/dropdown-menu.js +152 -0
- package/dist/shadcn/form.d.ts +59 -0
- package/dist/shadcn/form.d.ts.map +1 -0
- package/dist/shadcn/form.js +99 -0
- package/dist/shadcn/hover-card.d.ts +20 -0
- package/dist/shadcn/hover-card.d.ts.map +1 -0
- package/dist/shadcn/hover-card.js +26 -0
- package/dist/shadcn/input-otp.d.ts +25 -0
- package/dist/shadcn/input-otp.d.ts.map +1 -0
- package/dist/shadcn/input-otp.js +54 -0
- package/dist/shadcn/input.d.ts +14 -0
- package/dist/shadcn/input.d.ts.map +1 -0
- package/dist/shadcn/input.js +48 -0
- package/dist/shadcn/label.d.ts +8 -0
- package/dist/shadcn/label.d.ts.map +1 -0
- package/dist/shadcn/label.js +16 -0
- package/dist/shadcn/menubar.d.ts +108 -0
- package/dist/shadcn/menubar.d.ts.map +1 -0
- package/dist/shadcn/menubar.js +174 -0
- package/dist/shadcn/navigation-menu.d.ts +79 -0
- package/dist/shadcn/navigation-menu.d.ts.map +1 -0
- package/dist/shadcn/navigation-menu.js +108 -0
- package/dist/shadcn/pagination.d.ts +45 -0
- package/dist/shadcn/pagination.d.ts.map +1 -0
- package/dist/shadcn/pagination.js +83 -0
- package/dist/shadcn/popover.d.ts +19 -0
- package/dist/shadcn/popover.d.ts.map +1 -0
- package/dist/shadcn/popover.js +29 -0
- package/dist/shadcn/progress.d.ts +9 -0
- package/dist/shadcn/progress.d.ts.map +1 -0
- package/dist/shadcn/progress.js +17 -0
- package/dist/shadcn/radio-group.d.ts +12 -0
- package/dist/shadcn/radio-group.d.ts.map +1 -0
- package/dist/shadcn/radio-group.js +32 -0
- package/dist/shadcn/resizable.d.ts +20 -0
- package/dist/shadcn/resizable.d.ts.map +1 -0
- package/dist/shadcn/resizable.js +32 -0
- package/dist/shadcn/scroll-area.d.ts +16 -0
- package/dist/shadcn/scroll-area.d.ts.map +1 -0
- package/dist/shadcn/scroll-area.js +40 -0
- package/dist/shadcn/select.d.ts +63 -0
- package/dist/shadcn/select.d.ts.map +1 -0
- package/dist/shadcn/select.js +120 -0
- package/dist/shadcn/separator.d.ts +10 -0
- package/dist/shadcn/separator.d.ts.map +1 -0
- package/dist/shadcn/separator.js +18 -0
- package/dist/shadcn/sheet.d.ts +48 -0
- package/dist/shadcn/sheet.d.ts.map +1 -0
- package/dist/shadcn/sheet.js +99 -0
- package/dist/shadcn/sidebar.d.ts +200 -0
- package/dist/shadcn/sidebar.d.ts.map +1 -0
- package/dist/shadcn/sidebar.js +528 -0
- package/dist/shadcn/skeleton.d.ts +6 -0
- package/dist/shadcn/skeleton.d.ts.map +1 -0
- package/dist/shadcn/skeleton.js +11 -0
- package/dist/shadcn/slider.d.ts +12 -0
- package/dist/shadcn/slider.d.ts.map +1 -0
- package/dist/shadcn/slider.js +50 -0
- package/dist/shadcn/sonner.d.ts +7 -0
- package/dist/shadcn/sonner.d.ts.map +1 -0
- package/dist/shadcn/sonner.js +20 -0
- package/dist/shadcn/switch.d.ts +8 -0
- package/dist/shadcn/switch.d.ts.map +1 -0
- package/dist/shadcn/switch.js +22 -0
- package/dist/shadcn/table.d.ts +35 -0
- package/dist/shadcn/table.d.ts.map +1 -0
- package/dist/shadcn/table.js +73 -0
- package/dist/shadcn/tabs.d.ts +20 -0
- package/dist/shadcn/tabs.d.ts.map +1 -0
- package/dist/shadcn/tabs.js +40 -0
- package/dist/shadcn/textarea.d.ts +7 -0
- package/dist/shadcn/textarea.d.ts.map +1 -0
- package/dist/shadcn/textarea.js +14 -0
- package/dist/shadcn/toggle-group.d.ts +22 -0
- package/dist/shadcn/toggle-group.d.ts.map +1 -0
- package/dist/shadcn/toggle-group.js +43 -0
- package/dist/shadcn/toggle.d.ts +29 -0
- package/dist/shadcn/toggle.d.ts.map +1 -0
- package/dist/shadcn/toggle.js +31 -0
- package/dist/shadcn/tooltip.d.ts +20 -0
- package/dist/shadcn/tooltip.d.ts.map +1 -0
- package/dist/shadcn/tooltip.js +40 -0
- package/dist/stories/BackgroundSlideshow.stories.d.ts +9 -0
- package/dist/stories/BackgroundSlideshow.stories.d.ts.map +1 -0
- package/dist/stories/BackgroundSlideshow.stories.js +60 -0
- package/dist/stories/BlurryGradient.stories.d.ts +11 -0
- package/dist/stories/BlurryGradient.stories.d.ts.map +1 -0
- package/dist/stories/BlurryGradient.stories.js +53 -0
- package/dist/stories/Box.stories.d.ts +13 -0
- package/dist/stories/Box.stories.d.ts.map +1 -0
- package/dist/stories/Box.stories.js +50 -0
- package/dist/stories/Carousel.stories.d.ts +9 -0
- package/dist/stories/Carousel.stories.d.ts.map +1 -0
- package/dist/stories/Carousel.stories.js +30 -0
- package/dist/stories/Countdown.stories.d.ts +7 -0
- package/dist/stories/Countdown.stories.d.ts.map +1 -0
- package/dist/stories/Countdown.stories.js +11 -0
- package/dist/stories/DefinitionList.stories.d.ts +9 -0
- package/dist/stories/DefinitionList.stories.d.ts.map +1 -0
- package/dist/stories/DefinitionList.stories.js +20 -0
- package/dist/stories/GenericError.stories.d.ts +9 -0
- package/dist/stories/GenericError.stories.d.ts.map +1 -0
- package/dist/stories/GenericError.stories.js +39 -0
- package/dist/stories/Heading.stories.d.ts +15 -0
- package/dist/stories/Heading.stories.d.ts.map +1 -0
- package/dist/stories/Heading.stories.js +61 -0
- package/dist/stories/Highlight.stories.d.ts +8 -0
- package/dist/stories/Highlight.stories.d.ts.map +1 -0
- package/dist/stories/Highlight.stories.js +19 -0
- package/dist/stories/IconButton.stories.d.ts +10 -0
- package/dist/stories/IconButton.stories.d.ts.map +1 -0
- package/dist/stories/IconButton.stories.js +32 -0
- package/dist/stories/If.stories.d.ts +13 -0
- package/dist/stories/If.stories.d.ts.map +1 -0
- package/dist/stories/If.stories.js +54 -0
- package/dist/stories/Iframe.stories.d.ts +8 -0
- package/dist/stories/Iframe.stories.d.ts.map +1 -0
- package/dist/stories/Iframe.stories.js +23 -0
- package/dist/stories/Item.stories.d.ts +10 -0
- package/dist/stories/Item.stories.d.ts.map +1 -0
- package/dist/stories/Item.stories.js +32 -0
- package/dist/stories/Link.stories.d.ts +10 -0
- package/dist/stories/Link.stories.d.ts.map +1 -0
- package/dist/stories/Link.stories.js +36 -0
- package/dist/stories/ListItem.stories.d.ts +9 -0
- package/dist/stories/ListItem.stories.d.ts.map +1 -0
- package/dist/stories/ListItem.stories.js +25 -0
- package/dist/stories/MarkdownContainer.stories.d.ts +11 -0
- package/dist/stories/MarkdownContainer.stories.d.ts.map +1 -0
- package/dist/stories/MarkdownContainer.stories.js +66 -0
- package/dist/stories/PasswordInput.stories.d.ts +9 -0
- package/dist/stories/PasswordInput.stories.d.ts.map +1 -0
- package/dist/stories/PasswordInput.stories.js +27 -0
- package/dist/stories/Picture.stories.d.ts +10 -0
- package/dist/stories/Picture.stories.d.ts.map +1 -0
- package/dist/stories/Picture.stories.js +46 -0
- package/dist/stories/ProtectedEmail.stories.d.ts +10 -0
- package/dist/stories/ProtectedEmail.stories.d.ts.map +1 -0
- package/dist/stories/ProtectedEmail.stories.js +34 -0
- package/dist/stories/Text.stories.d.ts +10 -0
- package/dist/stories/Text.stories.d.ts.map +1 -0
- package/dist/stories/Text.stories.js +31 -0
- package/dist/stories/UnorderedList.stories.d.ts +10 -0
- package/dist/stories/UnorderedList.stories.d.ts.map +1 -0
- package/dist/stories/UnorderedList.stories.js +24 -0
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +1 -0
- package/dist/utils/author.d.ts +9 -0
- package/dist/utils/author.d.ts.map +1 -0
- package/dist/utils/author.js +37 -0
- package/dist/utils/author.test.d.ts +2 -0
- package/dist/utils/author.test.d.ts.map +1 -0
- package/dist/utils/author.test.js +46 -0
- package/dist/utils/cn.d.ts +5 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/cn.js +10 -0
- package/dist/utils/cn.test.d.ts +2 -0
- package/dist/utils/cn.test.d.ts.map +1 -0
- package/dist/utils/cn.test.js +33 -0
- package/dist/utils/is-route-active.d.ts +3 -0
- package/dist/utils/is-route-active.d.ts.map +1 -0
- package/dist/utils/is-route-active.js +54 -0
- package/dist/utils/is-route-active.test.d.ts +2 -0
- package/dist/utils/is-route-active.test.d.ts.map +1 -0
- package/dist/utils/is-route-active.test.js +61 -0
- package/dist/utils/locale.d.ts +22 -0
- package/dist/utils/locale.d.ts.map +1 -0
- package/dist/utils/locale.js +25 -0
- package/dist/utils/locale.test.d.ts +2 -0
- package/dist/utils/locale.test.d.ts.map +1 -0
- package/dist/utils/locale.test.js +99 -0
- package/dist/utils/text.d.ts +7 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +102 -0
- package/dist/utils/text.test.d.ts +2 -0
- package/dist/utils/text.test.d.ts.map +1 -0
- package/dist/utils/text.test.js +122 -0
- package/package.json +117 -0
- package/src/components/background-slideshow.tsx +172 -0
- package/src/components/blurry-gradient.tsx +97 -0
- package/src/components/box.tsx +45 -0
- package/src/components/carousel.tsx +197 -0
- package/src/components/countdown.tsx +82 -0
- package/src/components/definition-list.tsx +90 -0
- package/src/components/generic-error.tsx +104 -0
- package/src/components/heading.tsx +60 -0
- package/src/components/highlight.tsx +53 -0
- package/src/components/icon-button.tsx +20 -0
- package/src/components/if.tsx +29 -0
- package/src/components/iframe.tsx +19 -0
- package/src/components/item.tsx +340 -0
- package/src/components/leaflet-map.tsx +294 -0
- package/src/components/link.test.tsx +387 -0
- package/src/components/link.tsx +206 -0
- package/src/components/list-item.tsx +30 -0
- package/src/components/maptiler-map.tsx +181 -0
- package/src/components/markdown-container.tsx +153 -0
- package/src/components/password-input.tsx +49 -0
- package/src/components/picture.tsx +107 -0
- package/src/components/protected-email.tsx +49 -0
- package/src/components/text.tsx +38 -0
- package/src/components/unordered-list.tsx +32 -0
- package/src/hooks/use-current-route-data.ts +18 -0
- package/src/hooks/use-focus-search.ts +20 -0
- package/src/hooks/use-matches-data.ts +19 -0
- package/src/hooks/use-media-query.ts +33 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/hooks/use-nonce.test.ts +35 -0
- package/src/hooks/use-nonce.ts +20 -0
- package/src/hooks/use-orientation.ts +48 -0
- package/src/hooks/use-user.tsx +72 -0
- package/src/stories/BackgroundSlideshow.stories.tsx +68 -0
- package/src/stories/BlurryGradient.stories.tsx +62 -0
- package/src/stories/Box.stories.tsx +83 -0
- package/src/stories/Carousel.stories.tsx +95 -0
- package/src/stories/Countdown.stories.tsx +16 -0
- package/src/stories/DefinitionList.stories.tsx +51 -0
- package/src/stories/GenericError.stories.tsx +58 -0
- package/src/stories/Heading.stories.tsx +82 -0
- package/src/stories/Highlight.stories.tsx +28 -0
- package/src/stories/IconButton.stories.tsx +90 -0
- package/src/stories/If.stories.tsx +72 -0
- package/src/stories/Iframe.stories.tsx +29 -0
- package/src/stories/Item.stories.tsx +79 -0
- package/src/stories/Link.stories.tsx +58 -0
- package/src/stories/ListItem.stories.tsx +37 -0
- package/src/stories/MarkdownContainer.stories.tsx +76 -0
- package/src/stories/PasswordInput.stories.tsx +34 -0
- package/src/stories/Picture.stories.tsx +54 -0
- package/src/stories/ProtectedEmail.stories.tsx +57 -0
- package/src/stories/Text.stories.tsx +44 -0
- package/src/stories/UnorderedList.stories.tsx +73 -0
- package/src/styles/storybook.css +1 -0
- package/src/styles/tailwind.css +7 -0
- package/src/test-setup.ts +1 -0
- package/src/utils/author.test.ts +54 -0
- package/src/utils/author.tsx +73 -0
- package/src/utils/cn.test.ts +48 -0
- package/src/utils/cn.ts +14 -0
- package/src/utils/is-route-active.test.ts +80 -0
- package/src/utils/is-route-active.ts +100 -0
- package/src/utils/locale.test.ts +148 -0
- package/src/utils/locale.ts +127 -0
- package/src/utils/text.test.ts +152 -0
- package/src/utils/text.tsx +209 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { checkIfRouteIsActive, isRouteActive } from './is-route-active';
|
|
3
|
+
|
|
4
|
+
describe('isRouteActive', () => {
|
|
5
|
+
test('returns true when path matches currentPath exactly', () => {
|
|
6
|
+
expect(isRouteActive('/about', '/about')).toBe(true);
|
|
7
|
+
expect(isRouteActive('/contact', '/contact')).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('returns false when paths are different', () => {
|
|
11
|
+
expect(isRouteActive('/about', '/contact')).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('with end=true (default), only matches exact path', () => {
|
|
15
|
+
expect(isRouteActive('/about', '/about/team', true)).toBe(false);
|
|
16
|
+
expect(isRouteActive('/about', '/about', true)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('with end=false, matches parent paths', () => {
|
|
20
|
+
expect(isRouteActive('/about', '/about/team', false)).toBe(true);
|
|
21
|
+
expect(isRouteActive('/about', '/about/team/members', false)).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('with end as function, uses function result', () => {
|
|
25
|
+
const endFn = (path: string) => path.includes('team');
|
|
26
|
+
expect(isRouteActive('/about', '/about/team', endFn)).toBe(false);
|
|
27
|
+
expect(isRouteActive('/about', '/about/contact', endFn)).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('root path only matches root', () => {
|
|
31
|
+
expect(isRouteActive('/', '/')).toBe(true);
|
|
32
|
+
expect(isRouteActive('/', '/about', true)).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('checkIfRouteIsActive', () => {
|
|
37
|
+
test('returns true for exact match', () => {
|
|
38
|
+
expect(checkIfRouteIsActive('/about', '/about')).toBe(true);
|
|
39
|
+
expect(checkIfRouteIsActive('/users/123', '/users/123')).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('returns false when root is target but current is not root', () => {
|
|
43
|
+
expect(checkIfRouteIsActive('/', '/about')).toBe(false);
|
|
44
|
+
expect(checkIfRouteIsActive('/', '/users/123')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('returns true for root when current is root', () => {
|
|
48
|
+
expect(checkIfRouteIsActive('/', '/')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('returns false when target is not in current path', () => {
|
|
52
|
+
expect(checkIfRouteIsActive('/about', '/contact')).toBe(false);
|
|
53
|
+
expect(checkIfRouteIsActive('/users', '/posts')).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('with depth=1, matches when segments match within depth', () => {
|
|
57
|
+
// depth=1 means matchingSegments > segments.length - 0, i.e., matchingSegments > 1
|
|
58
|
+
// /about has 1 segment, /about/team has 2 segments, 1 matching
|
|
59
|
+
// 1 > 1 - 0 = 1 > 1 = false
|
|
60
|
+
expect(checkIfRouteIsActive('/about', '/about/team', 1)).toBe(false);
|
|
61
|
+
expect(checkIfRouteIsActive('/about', '/about/team/members', 1)).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('with depth=2, matches one level deep', () => {
|
|
65
|
+
// depth=2 means matchingSegments > segments.length - 1
|
|
66
|
+
// /about has 1 segment, 1 > 1 - 1 = 1 > 0 = true
|
|
67
|
+
expect(checkIfRouteIsActive('/about', '/about/team', 2)).toBe(true);
|
|
68
|
+
expect(checkIfRouteIsActive('/about', '/about/team/members', 2)).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('with depth=3, matches two levels deep', () => {
|
|
72
|
+
expect(checkIfRouteIsActive('/about', '/about/team', 3)).toBe(true);
|
|
73
|
+
expect(checkIfRouteIsActive('/about', '/about/team/members', 3)).toBe(true);
|
|
74
|
+
expect(checkIfRouteIsActive('/about', '/about/team/members/details', 3)).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('ignores query parameters in current route', () => {
|
|
78
|
+
expect(checkIfRouteIsActive('/about', '/about?foo=bar')).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const ROOT_PATH = '/';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @name isRouteActive
|
|
5
|
+
* @description A function to check if a route is active. This is used to
|
|
6
|
+
* @param end
|
|
7
|
+
* @param path
|
|
8
|
+
* @param currentPath
|
|
9
|
+
*/
|
|
10
|
+
export function isRouteActive(
|
|
11
|
+
path: string,
|
|
12
|
+
currentPath: string,
|
|
13
|
+
end?: boolean | ((path: string) => boolean) | undefined,
|
|
14
|
+
) {
|
|
15
|
+
// if the path is the same as the current path, we return true
|
|
16
|
+
if (path === currentPath) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// if the end prop is a function, we call it with the current path
|
|
21
|
+
if (typeof end === 'function') {
|
|
22
|
+
return !end(currentPath);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// otherwise - we use the evaluateIsRouteActive function
|
|
26
|
+
const defaultEnd = end ?? true;
|
|
27
|
+
const oneLevelDeep = 1;
|
|
28
|
+
const threeLevelsDeep = 3;
|
|
29
|
+
|
|
30
|
+
// how far down should segments be matched?
|
|
31
|
+
const depth = defaultEnd ? oneLevelDeep : threeLevelsDeep;
|
|
32
|
+
|
|
33
|
+
return checkIfRouteIsActive(path, currentPath, depth);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @name checkIfRouteIsActive
|
|
38
|
+
* @description A function to check if a route is active. This is used to
|
|
39
|
+
* highlight the active link in the navigation.
|
|
40
|
+
* @param targetLink - The link to check against
|
|
41
|
+
* @param currentRoute - the current route
|
|
42
|
+
* @param depth - how far down should segments be matched?
|
|
43
|
+
*/
|
|
44
|
+
export function checkIfRouteIsActive(targetLink: string, currentRoute: string, depth = 1) {
|
|
45
|
+
// we remove any eventual query param from the route's URL
|
|
46
|
+
const currentRoutePath = currentRoute.split('?')[0] ?? '';
|
|
47
|
+
|
|
48
|
+
if (!isRoot(currentRoutePath) && isRoot(targetLink)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!currentRoutePath.includes(targetLink)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const isSameRoute = targetLink === currentRoutePath;
|
|
57
|
+
|
|
58
|
+
if (isSameRoute) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return hasMatchingSegments(targetLink, currentRoutePath, depth);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function splitIntoSegments(href: string) {
|
|
66
|
+
return href.split('/').filter(Boolean);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function hasMatchingSegments(targetLink: string, currentRoute: string, depth: number) {
|
|
70
|
+
const segments = splitIntoSegments(targetLink);
|
|
71
|
+
const matchingSegments = numberOfMatchingSegments(currentRoute, segments);
|
|
72
|
+
|
|
73
|
+
if (targetLink === currentRoute) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// how far down should segments be matched?
|
|
78
|
+
// - if depth = 1 => only highlight the links of the immediate parent
|
|
79
|
+
// - if depth = 2 => for url = /account match /account/organization/members
|
|
80
|
+
return matchingSegments > segments.length - (depth - 1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function numberOfMatchingSegments(href: string, segments: string[]) {
|
|
84
|
+
let count = 0;
|
|
85
|
+
|
|
86
|
+
for (const segment of splitIntoSegments(href)) {
|
|
87
|
+
// for as long as the segments match, keep counting + 1
|
|
88
|
+
if (segments.includes(segment)) {
|
|
89
|
+
count += 1;
|
|
90
|
+
} else {
|
|
91
|
+
return count;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return count;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isRoot(path: string) {
|
|
99
|
+
return path === ROOT_PATH;
|
|
100
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { LanguageDetectorLingui } from '@regardio/js/intl/language-detector';
|
|
2
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
3
|
+
import { createLocaleConfig } from './locale';
|
|
4
|
+
|
|
5
|
+
describe('createLocaleConfig', () => {
|
|
6
|
+
const originalEnvMode = import.meta.env.MODE;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
// Reset MODE between tests
|
|
10
|
+
import.meta.env.MODE = originalEnvMode;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('creates config with default options', async () => {
|
|
14
|
+
const config = createLocaleConfig({
|
|
15
|
+
defaultLocale: 'en',
|
|
16
|
+
supportedLocales: ['en', 'de'],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Verify cookie was created with correct default options
|
|
20
|
+
expect(config.localeCookie).toBeDefined();
|
|
21
|
+
expect(config.localeCookie.name).toBe('locale');
|
|
22
|
+
|
|
23
|
+
// Create a mock request with a cookie
|
|
24
|
+
const request = new Request('https://example.com', {
|
|
25
|
+
headers: {
|
|
26
|
+
Cookie: await config.localeCookie.serialize('en'),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Parse the cookie to verify it works correctly
|
|
31
|
+
const parsedValue = await config.localeCookie.parse(request.headers.get('Cookie'));
|
|
32
|
+
expect(parsedValue).toBe('en');
|
|
33
|
+
|
|
34
|
+
// Verify language detector was created correctly
|
|
35
|
+
expect(config.languageDetector).toBeInstanceOf(LanguageDetectorLingui);
|
|
36
|
+
|
|
37
|
+
// Test language detector behavior instead of accessing private properties
|
|
38
|
+
const mockRequest = new Request('https://example.com/en/page', {
|
|
39
|
+
headers: { 'Accept-Language': 'de' },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// With default order (urlPath first), it should detect 'en' from URL path
|
|
43
|
+
const detectedLocale = await config.languageDetector.getLocale(mockRequest);
|
|
44
|
+
expect(detectedLocale).toBe('en');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('creates config with custom cookie options', async () => {
|
|
48
|
+
const config = createLocaleConfig({
|
|
49
|
+
cookieOptions: {
|
|
50
|
+
httpOnly: true,
|
|
51
|
+
maxAge: 3600, // 1 hour
|
|
52
|
+
name: 'custom-locale',
|
|
53
|
+
sameSite: 'strict',
|
|
54
|
+
secure: true,
|
|
55
|
+
},
|
|
56
|
+
defaultLocale: 'fr',
|
|
57
|
+
supportedLocales: ['fr', 'es'],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Verify cookie name
|
|
61
|
+
expect(config.localeCookie.name).toBe('custom-locale');
|
|
62
|
+
|
|
63
|
+
// Test cookie serialization to verify options indirectly
|
|
64
|
+
const cookieHeader = await config.localeCookie.serialize('fr');
|
|
65
|
+
console.log('Custom cookie header:', cookieHeader);
|
|
66
|
+
expect(typeof cookieHeader).toBe('string');
|
|
67
|
+
// Check for cookie name and value without assuming exact format
|
|
68
|
+
expect(cookieHeader.includes('custom-locale=')).toBe(true);
|
|
69
|
+
expect(/samesite=strict/i.test(cookieHeader)).toBe(true);
|
|
70
|
+
expect(/httponly/i.test(cookieHeader)).toBe(true);
|
|
71
|
+
expect(/secure/i.test(cookieHeader)).toBe(true);
|
|
72
|
+
expect(/max-age=3600/i.test(cookieHeader)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('creates config with custom detection order', async () => {
|
|
76
|
+
const config = createLocaleConfig({
|
|
77
|
+
defaultLocale: 'de',
|
|
78
|
+
detectionOptions: {
|
|
79
|
+
order: ['header', 'cookie', 'urlPath'],
|
|
80
|
+
},
|
|
81
|
+
supportedLocales: ['de', 'en'],
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Test detection order by creating requests that would give different results
|
|
85
|
+
// based on detection order
|
|
86
|
+
|
|
87
|
+
// This request has 'en' in URL path but 'de' in Accept-Language header
|
|
88
|
+
const mockRequest = new Request('https://example.com/en/page', {
|
|
89
|
+
headers: { 'Accept-Language': 'de' },
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// With custom order (header first), it should detect 'de' from header
|
|
93
|
+
// instead of 'en' from URL path
|
|
94
|
+
const detectedLocale = await config.languageDetector.getLocale(mockRequest);
|
|
95
|
+
expect(detectedLocale).toBe('de');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('secure cookie defaults to true in production environment', async () => {
|
|
99
|
+
// Mock production environment
|
|
100
|
+
import.meta.env.MODE = 'production';
|
|
101
|
+
|
|
102
|
+
const config = createLocaleConfig({
|
|
103
|
+
defaultLocale: 'en',
|
|
104
|
+
supportedLocales: ['en', 'de'],
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Test cookie serialization to verify secure option indirectly
|
|
108
|
+
const cookieHeader = await config.localeCookie.serialize('en');
|
|
109
|
+
console.log('Production cookie header:', cookieHeader);
|
|
110
|
+
expect(typeof cookieHeader).toBe('string');
|
|
111
|
+
// Check for Secure flag without assuming exact format
|
|
112
|
+
expect(/secure/i.test(cookieHeader)).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('secure cookie defaults to false in development environment', async () => {
|
|
116
|
+
// Mock development environment
|
|
117
|
+
import.meta.env.MODE = 'development';
|
|
118
|
+
|
|
119
|
+
const config = createLocaleConfig({
|
|
120
|
+
defaultLocale: 'en',
|
|
121
|
+
supportedLocales: ['en', 'de'],
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Test cookie serialization to verify secure option indirectly
|
|
125
|
+
const cookieHeader = await config.localeCookie.serialize('en');
|
|
126
|
+
console.log('Development cookie header:', cookieHeader);
|
|
127
|
+
expect(typeof cookieHeader).toBe('string');
|
|
128
|
+
// Check for absence of Secure flag without assuming exact format
|
|
129
|
+
expect(/secure/i.test(cookieHeader)).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('handles readonly supportedLocales array', async () => {
|
|
133
|
+
// Create a readonly array
|
|
134
|
+
const supportedLocales = ['en', 'de'] as const;
|
|
135
|
+
|
|
136
|
+
const config = createLocaleConfig({
|
|
137
|
+
defaultLocale: 'en',
|
|
138
|
+
supportedLocales,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Test that both languages are detected correctly
|
|
142
|
+
const enRequest = new Request('https://example.com/en/page');
|
|
143
|
+
expect(await config.languageDetector.getLocale(enRequest)).toBe('en');
|
|
144
|
+
|
|
145
|
+
const deRequest = new Request('https://example.com/de/page');
|
|
146
|
+
expect(await config.languageDetector.getLocale(deRequest)).toBe('de');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { LanguageDetectorLingui } from '@regardio/js/intl/language-detector';
|
|
2
|
+
import { createCookie } from 'react-router';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for creating locale utilities
|
|
6
|
+
*/
|
|
7
|
+
export interface LocaleConfigOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Default locale to use as fallback
|
|
10
|
+
*/
|
|
11
|
+
defaultLocale: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* List of supported locales
|
|
15
|
+
*/
|
|
16
|
+
supportedLocales: readonly string[];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Cookie options for storing locale preference
|
|
20
|
+
*/
|
|
21
|
+
cookieOptions?: {
|
|
22
|
+
/**
|
|
23
|
+
* Name of the cookie (default: 'locale')
|
|
24
|
+
*/
|
|
25
|
+
name?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Max age of the cookie in seconds (default: 1 year)
|
|
29
|
+
*/
|
|
30
|
+
maxAge?: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether the cookie should be HTTP only (default: false)
|
|
34
|
+
*/
|
|
35
|
+
httpOnly?: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether the cookie should be secure (default: true in production)
|
|
39
|
+
*/
|
|
40
|
+
secure?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* SameSite attribute for the cookie (default: 'lax')
|
|
44
|
+
*/
|
|
45
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detection options for the language detector
|
|
50
|
+
*/
|
|
51
|
+
detectionOptions?: {
|
|
52
|
+
/**
|
|
53
|
+
* Order of detection methods (default: ['urlPath', 'cookie', 'header'])
|
|
54
|
+
*/
|
|
55
|
+
order?: Array<'urlPath' | 'cookie' | 'header'>;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Result of createLocaleConfig
|
|
61
|
+
*/
|
|
62
|
+
export interface LocaleConfig {
|
|
63
|
+
/**
|
|
64
|
+
* Cookie for storing locale preference
|
|
65
|
+
*/
|
|
66
|
+
localeCookie: ReturnType<typeof createCookie>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Language detector instance
|
|
70
|
+
*/
|
|
71
|
+
languageDetector: LanguageDetectorLingui;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create locale configuration utilities
|
|
76
|
+
*
|
|
77
|
+
* This function creates a unified locale cookie and language detector
|
|
78
|
+
* based on the provided configuration options.
|
|
79
|
+
*
|
|
80
|
+
* @param options Configuration options
|
|
81
|
+
* @returns Locale configuration utilities
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* // In your app's configuration file
|
|
86
|
+
* const { localeCookie, languageDetector } = createLocaleConfig({
|
|
87
|
+
* defaultLocale: 'en',
|
|
88
|
+
* supportedLocales: ['en', 'de', 'fr'],
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export function createLocaleConfig(options: LocaleConfigOptions): LocaleConfig {
|
|
93
|
+
const { defaultLocale, supportedLocales, cookieOptions = {}, detectionOptions = {} } = options;
|
|
94
|
+
|
|
95
|
+
const {
|
|
96
|
+
name = 'locale',
|
|
97
|
+
maxAge = 60 * 60 * 24 * 365, // 1 year
|
|
98
|
+
httpOnly = false,
|
|
99
|
+
secure = import.meta.env.MODE !== 'development',
|
|
100
|
+
sameSite = 'lax',
|
|
101
|
+
} = cookieOptions;
|
|
102
|
+
|
|
103
|
+
const { order = ['urlPath', 'cookie', 'header'] } = detectionOptions;
|
|
104
|
+
|
|
105
|
+
// Create cookie for storing locale preference
|
|
106
|
+
const localeCookie = createCookie(name, {
|
|
107
|
+
httpOnly,
|
|
108
|
+
maxAge,
|
|
109
|
+
sameSite,
|
|
110
|
+
secure,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Create language detector instance
|
|
114
|
+
const languageDetector = new LanguageDetectorLingui({
|
|
115
|
+
detection: {
|
|
116
|
+
cookie: localeCookie,
|
|
117
|
+
fallbackLanguage: defaultLocale,
|
|
118
|
+
order,
|
|
119
|
+
supportedLanguages: [...supportedLocales],
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
languageDetector,
|
|
125
|
+
localeCookie,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { replaceSpecialChars, shy, toBoolean, typographicQuotes } from './text';
|
|
3
|
+
|
|
4
|
+
describe('toBoolean', () => {
|
|
5
|
+
test('returns true for boolean true', () => {
|
|
6
|
+
expect(toBoolean(true)).toBe(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('returns false for boolean false', () => {
|
|
10
|
+
expect(toBoolean(false)).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('returns true for string "true"', () => {
|
|
14
|
+
expect(toBoolean('true')).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('returns true for string "1"', () => {
|
|
18
|
+
expect(toBoolean('1')).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('returns false for string "false"', () => {
|
|
22
|
+
expect(toBoolean('false')).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('returns false for string "0"', () => {
|
|
26
|
+
expect(toBoolean('0')).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('returns false for null', () => {
|
|
30
|
+
expect(toBoolean(null)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('returns false for undefined', () => {
|
|
34
|
+
expect(toBoolean(undefined)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('returns false for other strings', () => {
|
|
38
|
+
expect(toBoolean('yes')).toBe(false);
|
|
39
|
+
expect(toBoolean('no')).toBe(false);
|
|
40
|
+
expect(toBoolean('')).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('shy', () => {
|
|
45
|
+
test('returns null for null input', () => {
|
|
46
|
+
expect(shy(null)).toBe(null);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('replaces ­ with soft hyphen in strings', () => {
|
|
50
|
+
const result = shy('hello­world');
|
|
51
|
+
expect(result).toBe('hello\u00ADworld');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('handles strings without ­', () => {
|
|
55
|
+
expect(shy('hello world')).toBe('hello world');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('handles multiple ­ occurrences', () => {
|
|
59
|
+
const result = shy('one­two­three');
|
|
60
|
+
expect(result).toBe('one\u00ADtwo\u00ADthree');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('replaceSpecialChars', () => {
|
|
65
|
+
test('replaces straight quotes for de locale', () => {
|
|
66
|
+
const result = replaceSpecialChars('"Hello"', 'de');
|
|
67
|
+
const str = String(result);
|
|
68
|
+
// Should not contain straight quotes anymore
|
|
69
|
+
expect(str).not.toContain('"Hello"');
|
|
70
|
+
expect(str).toContain('Hello');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('replaces straight quotes for de-DE locale', () => {
|
|
74
|
+
const result = replaceSpecialChars('"Hello"', 'de-DE');
|
|
75
|
+
const str = String(result);
|
|
76
|
+
expect(str).not.toContain('"Hello"');
|
|
77
|
+
expect(str).toContain('Hello');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('replaces straight quotes for en locale', () => {
|
|
81
|
+
const result = replaceSpecialChars('"Hello"', 'en');
|
|
82
|
+
const str = String(result);
|
|
83
|
+
// Should not contain straight quotes anymore
|
|
84
|
+
expect(str).not.toContain('"Hello"');
|
|
85
|
+
expect(str).toContain('Hello');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('handles multiple quoted strings', () => {
|
|
89
|
+
const result = replaceSpecialChars('"one" and "two"', 'de');
|
|
90
|
+
const str = String(result);
|
|
91
|
+
expect(str).toContain('one');
|
|
92
|
+
expect(str).toContain('two');
|
|
93
|
+
expect(str).toContain('and');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('also applies shy replacement', () => {
|
|
97
|
+
const result = replaceSpecialChars('"Hello­World"', 'en');
|
|
98
|
+
expect(String(result)).toContain('\u00AD');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('typographicQuotes', () => {
|
|
103
|
+
test('replaces double quotes with English curly quotes', () => {
|
|
104
|
+
const result = typographicQuotes('"Hello"', 'en');
|
|
105
|
+
expect(result).toBe('\u201CHello\u201D');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('replaces double quotes with German quotes', () => {
|
|
109
|
+
const result = typographicQuotes('"Hello"', 'de');
|
|
110
|
+
expect(result).toBe('\u201EHello\u201D');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('replaces double quotes with French guillemets', () => {
|
|
114
|
+
const result = typographicQuotes('"Hello"', 'fr');
|
|
115
|
+
expect(result).toBe('\u00AB Hello \u00BB');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('handles de-DE locale (falls back to de)', () => {
|
|
119
|
+
const result = typographicQuotes('"Hello"', 'de-DE');
|
|
120
|
+
expect(result).toBe('\u201EHello\u201D');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('handles de-CH locale (Swiss German with guillemets)', () => {
|
|
124
|
+
const result = typographicQuotes('"Hello"', 'de-CH');
|
|
125
|
+
expect(result).toBe('\u00ABHello\u00BB');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('handles unknown locale (falls back to English)', () => {
|
|
129
|
+
const result = typographicQuotes('"Hello"', 'xx-YY');
|
|
130
|
+
expect(result).toBe('\u201CHello\u201D');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('replaces single quotes', () => {
|
|
134
|
+
const result = typographicQuotes("'Hello'", 'en');
|
|
135
|
+
expect(result).toBe('\u2018Hello\u2019');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('handles multiple quoted strings', () => {
|
|
139
|
+
const result = typographicQuotes('"one" and "two"', 'de');
|
|
140
|
+
expect(result).toBe('\u201Eone\u201D and \u201Etwo\u201D');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('handles Japanese quotes', () => {
|
|
144
|
+
const result = typographicQuotes('"Hello"', 'ja');
|
|
145
|
+
expect(result).toBe('\u300CHello\u300D');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('preserves text without quotes', () => {
|
|
149
|
+
const result = typographicQuotes('Hello World', 'en');
|
|
150
|
+
expect(result).toBe('Hello World');
|
|
151
|
+
});
|
|
152
|
+
});
|