@regardio/react 0.5.1 → 0.5.7
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/README.md +1 -3
- package/dist/hooks/{use-current-route-data/index.js → use-current-route-data.js} +1 -1
- package/dist/hooks/{use-focus-search/index.js → use-focus-search.js} +1 -1
- package/dist/hooks/{use-matches-data/index.js → use-matches-data.js} +1 -1
- package/dist/hooks/{use-media-query/index.js → use-media-query.js} +1 -1
- package/dist/hooks/{use-mobile/index.js → use-mobile.js} +1 -1
- package/dist/hooks/{use-nonce/index.d.ts → use-nonce.d.ts} +2 -1
- package/dist/hooks/{use-nonce/index.js → use-nonce.js} +2 -2
- package/dist/hooks/{use-orientation/index.d.ts → use-orientation.d.ts} +1 -1
- package/dist/hooks/{use-orientation/index.js → use-orientation.js} +1 -1
- package/dist/hooks/{use-user/index.js → use-user.js} +1 -1
- package/package.json +143 -131
- package/src/background-slideshow/background-slideshow.stories.tsx +69 -0
- package/src/carousel/carousel.stories.tsx +46 -0
- package/src/hooks/{use-nonce/use-nonce.ts → use-nonce.ts} +0 -10
- package/src/link/link.stories.tsx +51 -0
- package/src/link/link.test.tsx +169 -0
- package/src/markdown-container/markdown-container.stories.tsx +55 -0
- package/src/utils/text/text.test.tsx +110 -0
- package/src/hooks/use-current-route-data/index.ts +0 -1
- package/src/hooks/use-focus-search/index.ts +0 -1
- package/src/hooks/use-matches-data/index.ts +0 -1
- package/src/hooks/use-media-query/index.ts +0 -1
- package/src/hooks/use-mobile/index.ts +0 -1
- package/src/hooks/use-nonce/index.ts +0 -1
- package/src/hooks/use-orientation/index.ts +0 -1
- package/src/hooks/use-user/index.ts +0 -2
- /package/dist/hooks/{use-current-route-data/index.d.ts → use-current-route-data.d.ts} +0 -0
- /package/dist/hooks/{use-focus-search/index.d.ts → use-focus-search.d.ts} +0 -0
- /package/dist/hooks/{use-matches-data/index.d.ts → use-matches-data.d.ts} +0 -0
- /package/dist/hooks/{use-media-query/index.d.ts → use-media-query.d.ts} +0 -0
- /package/dist/hooks/{use-mobile/index.d.ts → use-mobile.d.ts} +0 -0
- /package/dist/hooks/{use-user/index.d.ts → use-user.d.ts} +0 -0
- /package/src/hooks/{use-current-route-data/use-current-route-data.ts → use-current-route-data.ts} +0 -0
- /package/src/hooks/{use-focus-search/use-focus-search.ts → use-focus-search.ts} +0 -0
- /package/src/hooks/{use-matches-data/use-matches-data.ts → use-matches-data.ts} +0 -0
- /package/src/hooks/{use-media-query/use-media-query.ts → use-media-query.ts} +0 -0
- /package/src/hooks/{use-mobile/use-mobile.ts → use-mobile.ts} +0 -0
- /package/src/hooks/{use-orientation/use-orientation.ts → use-orientation.ts} +0 -0
- /package/src/hooks/{use-user/use-user.tsx → use-user.tsx} +0 -0
package/README.md
CHANGED
|
@@ -65,7 +65,6 @@ import '@regardio/react/tailwind.css';
|
|
|
65
65
|
| `Box` | Flexible container with variant-based styling |
|
|
66
66
|
| `Carousel` | Embla-powered carousel with navigation controls |
|
|
67
67
|
| `Countdown` | Dynamic countdown timer display |
|
|
68
|
-
| `DefinitionList` | Semantic `<dl>` with `Dt` and `Dd` sub-components |
|
|
69
68
|
| `GenericError` | React Router error boundary with i18n support |
|
|
70
69
|
| `Heading` | Semantic headings (h1-h6) with consistent styling |
|
|
71
70
|
| `Highlight` | Text highlighting with customizable styles |
|
|
@@ -75,14 +74,13 @@ import '@regardio/react/tailwind.css';
|
|
|
75
74
|
| `Item` | Grid item with theme colors and link support |
|
|
76
75
|
| `LeafletMap` | Leaflet map integration |
|
|
77
76
|
| `Link` | React Router link with external URL detection |
|
|
78
|
-
| `
|
|
77
|
+
| `List` | Compound list component with Root and Item |
|
|
79
78
|
| `MaptilerMap` | MapTiler SDK integration |
|
|
80
79
|
| `MarkdownContainer` | MDX/Markdown renderer with typography processing |
|
|
81
80
|
| `PasswordInput` | Password field with visibility toggle |
|
|
82
81
|
| `Picture` | Responsive images with srcset generation |
|
|
83
82
|
| `ProtectedEmail` | Email obfuscation for spam protection |
|
|
84
83
|
| `Text` | Typography component with variants |
|
|
85
|
-
| `UnorderedList` | Styled unordered list with variants |
|
|
86
84
|
|
|
87
85
|
### Hooks
|
|
88
86
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useLocation, useMatches } from 'react-router';
|
|
3
3
|
|
|
4
|
-
// src/hooks/use-current-route-data
|
|
4
|
+
// src/hooks/use-current-route-data.ts
|
|
5
5
|
function useCurrentRouteData() {
|
|
6
6
|
const location = useLocation();
|
|
7
7
|
const matchingRoutes = useMatches();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useMatches } from 'react-router';
|
|
3
3
|
|
|
4
|
-
// src/hooks/use-matches-data
|
|
4
|
+
// src/hooks/use-matches-data.ts
|
|
5
5
|
function useMatchesData(id) {
|
|
6
6
|
const matchingRoutes = useMatches();
|
|
7
7
|
const route = useMemo(() => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
|
|
3
|
+
declare const NonceContext: react.Context<string>;
|
|
3
4
|
declare const NonceProvider: react.Provider<string>;
|
|
4
5
|
declare const useNonce: () => string;
|
|
5
6
|
|
|
6
|
-
export { NonceProvider, useNonce };
|
|
7
|
+
export { NonceContext, NonceProvider, useNonce };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
|
-
// src/hooks/use-nonce
|
|
3
|
+
// src/hooks/use-nonce.ts
|
|
4
4
|
var NonceContext = createContext("");
|
|
5
5
|
var NonceProvider = NonceContext.Provider;
|
|
6
6
|
var useNonce = () => useContext(NonceContext);
|
|
7
7
|
|
|
8
|
-
export { NonceProvider, useNonce };
|
|
8
|
+
export { NonceContext, NonceProvider, useNonce };
|
package/package.json
CHANGED
|
@@ -1,194 +1,149 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
|
-
"
|
|
3
|
+
"name": "@regardio/react",
|
|
4
|
+
"version": "0.5.7",
|
|
5
|
+
"private": false,
|
|
6
|
+
"description": "Regardio React UI components",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"react",
|
|
9
|
+
"components",
|
|
10
|
+
"ui",
|
|
11
|
+
"regardio",
|
|
12
|
+
"tailwindcss"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/regardio/react/blob/main/README.md",
|
|
4
15
|
"bugs": {
|
|
5
16
|
"url": "https://github.com/regardio/react/issues"
|
|
6
17
|
},
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"@maptiler/sdk": "3.9.0",
|
|
11
|
-
"@mdx-js/react": "3.1.1",
|
|
12
|
-
"@regardio/js": "0.5.0",
|
|
13
|
-
"@regardio/tailwind": "0.1.2",
|
|
14
|
-
"@supabase/supabase-js": "2.90.0",
|
|
15
|
-
"cmdk": "1.1.1",
|
|
16
|
-
"embla-carousel": "8.6.0",
|
|
17
|
-
"embla-carousel-react": "8.6.0",
|
|
18
|
-
"input-otp": "1.4.2",
|
|
19
|
-
"intl-parse-accept-language": "1.0.0",
|
|
20
|
-
"leaflet": "alpha",
|
|
21
|
-
"lucide-react": "0.562.0",
|
|
22
|
-
"markdown-to-jsx": "9.5.0",
|
|
23
|
-
"react": "19.2.3",
|
|
24
|
-
"react-day-picker": "9.13.0",
|
|
25
|
-
"react-dom": "19.2.3",
|
|
26
|
-
"react-hook-form": "7.70.0",
|
|
27
|
-
"react-resizable-panels": "4.3.0",
|
|
28
|
-
"react-router": "7.11.0",
|
|
29
|
-
"tailwind-variants": "3.2.2",
|
|
30
|
-
"vaul": "1.1.2",
|
|
31
|
-
"zod": "4.3.5"
|
|
32
|
-
},
|
|
33
|
-
"description": "Regardio React UI components",
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@regardio/dev": "1.10.3",
|
|
36
|
-
"@storybook/addon-a11y": "10.1.11",
|
|
37
|
-
"@storybook/addon-docs": "10.1.11",
|
|
38
|
-
"@storybook/addon-vitest": "10.1.11",
|
|
39
|
-
"@storybook/react-vite": "10.1.11",
|
|
40
|
-
"@tailwindcss/vite": "4.1.18",
|
|
41
|
-
"@testing-library/jest-dom": "6.9.1",
|
|
42
|
-
"@testing-library/react": "16.3.1",
|
|
43
|
-
"@total-typescript/ts-reset": "0.6.1",
|
|
44
|
-
"@types/leaflet": "1.9.21",
|
|
45
|
-
"@types/node": "25.0.3",
|
|
46
|
-
"@types/react": "19.2.7",
|
|
47
|
-
"@types/react-dom": "19.2.3",
|
|
48
|
-
"@vitejs/plugin-react": "5.1.2",
|
|
49
|
-
"@vitest/browser-playwright": "4.0.16",
|
|
50
|
-
"@vitest/coverage-v8": "4.0.16",
|
|
51
|
-
"@vitest/ui": "4.0.16",
|
|
52
|
-
"jsdom": "27.4.0",
|
|
53
|
-
"playwright": "1.57.0",
|
|
54
|
-
"storybook": "10.1.11",
|
|
55
|
-
"tailwindcss": "4.1.18",
|
|
56
|
-
"tsup": "8.5.1",
|
|
57
|
-
"typescript": "5.9.3",
|
|
58
|
-
"vite": "7.3.1",
|
|
59
|
-
"vitest": "4.0.16"
|
|
60
|
-
},
|
|
61
|
-
"engines": {
|
|
62
|
-
"node": ">=18"
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/regardio/react.git"
|
|
63
21
|
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Bernd Matzner <bernd.matzner@regard.io>",
|
|
24
|
+
"sideEffects": [
|
|
25
|
+
"./src/tailwind.css"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
64
28
|
"exports": {
|
|
65
29
|
"./background-slideshow": {
|
|
66
|
-
"
|
|
67
|
-
"
|
|
30
|
+
"types": "./dist/background-slideshow/index.d.ts",
|
|
31
|
+
"import": "./dist/background-slideshow/index.js"
|
|
68
32
|
},
|
|
69
33
|
"./blurry-gradient": {
|
|
70
|
-
"
|
|
71
|
-
"
|
|
34
|
+
"types": "./dist/blurry-gradient/index.d.ts",
|
|
35
|
+
"import": "./dist/blurry-gradient/index.js"
|
|
72
36
|
},
|
|
73
37
|
"./carousel": {
|
|
74
|
-
"
|
|
75
|
-
"
|
|
38
|
+
"types": "./dist/carousel/index.d.ts",
|
|
39
|
+
"import": "./dist/carousel/index.js"
|
|
76
40
|
},
|
|
77
41
|
"./countdown": {
|
|
78
|
-
"
|
|
79
|
-
"
|
|
42
|
+
"types": "./dist/countdown/index.d.ts",
|
|
43
|
+
"import": "./dist/countdown/index.js"
|
|
80
44
|
},
|
|
81
45
|
"./generic-error": {
|
|
82
|
-
"
|
|
83
|
-
"
|
|
46
|
+
"types": "./dist/generic-error/index.d.ts",
|
|
47
|
+
"import": "./dist/generic-error/index.js"
|
|
84
48
|
},
|
|
85
49
|
"./grid": {
|
|
86
|
-
"
|
|
87
|
-
"
|
|
50
|
+
"types": "./dist/grid/index.d.ts",
|
|
51
|
+
"import": "./dist/grid/index.js"
|
|
88
52
|
},
|
|
89
53
|
"./heading": {
|
|
90
|
-
"
|
|
91
|
-
"
|
|
54
|
+
"types": "./dist/heading/index.d.ts",
|
|
55
|
+
"import": "./dist/heading/index.js"
|
|
92
56
|
},
|
|
93
57
|
"./highlight": {
|
|
94
|
-
"
|
|
95
|
-
"
|
|
58
|
+
"types": "./dist/highlight/index.d.ts",
|
|
59
|
+
"import": "./dist/highlight/index.js"
|
|
96
60
|
},
|
|
97
61
|
"./hooks/use-current-route-data": {
|
|
98
|
-
"
|
|
99
|
-
"
|
|
62
|
+
"types": "./dist/hooks/use-current-route-data.d.ts",
|
|
63
|
+
"import": "./dist/hooks/use-current-route-data.js"
|
|
100
64
|
},
|
|
101
65
|
"./hooks/use-focus-search": {
|
|
102
|
-
"
|
|
103
|
-
"
|
|
66
|
+
"types": "./dist/hooks/use-focus-search.d.ts",
|
|
67
|
+
"import": "./dist/hooks/use-focus-search.js"
|
|
104
68
|
},
|
|
105
69
|
"./hooks/use-matches-data": {
|
|
106
|
-
"
|
|
107
|
-
"
|
|
70
|
+
"types": "./dist/hooks/use-matches-data.d.ts",
|
|
71
|
+
"import": "./dist/hooks/use-matches-data.js"
|
|
108
72
|
},
|
|
109
73
|
"./hooks/use-media-query": {
|
|
110
|
-
"
|
|
111
|
-
"
|
|
74
|
+
"types": "./dist/hooks/use-media-query.d.ts",
|
|
75
|
+
"import": "./dist/hooks/use-media-query.js"
|
|
112
76
|
},
|
|
113
77
|
"./hooks/use-mobile": {
|
|
114
|
-
"
|
|
115
|
-
"
|
|
78
|
+
"types": "./dist/hooks/use-mobile.d.ts",
|
|
79
|
+
"import": "./dist/hooks/use-mobile.js"
|
|
116
80
|
},
|
|
117
81
|
"./hooks/use-nonce": {
|
|
118
|
-
"
|
|
119
|
-
"
|
|
82
|
+
"types": "./dist/hooks/use-nonce.d.ts",
|
|
83
|
+
"import": "./dist/hooks/use-nonce.js"
|
|
120
84
|
},
|
|
121
85
|
"./hooks/use-orientation": {
|
|
122
|
-
"
|
|
123
|
-
"
|
|
86
|
+
"types": "./dist/hooks/use-orientation.d.ts",
|
|
87
|
+
"import": "./dist/hooks/use-orientation.js"
|
|
124
88
|
},
|
|
125
89
|
"./hooks/use-user": {
|
|
126
|
-
"
|
|
127
|
-
"
|
|
90
|
+
"types": "./dist/hooks/use-user.d.ts",
|
|
91
|
+
"import": "./dist/hooks/use-user.js"
|
|
128
92
|
},
|
|
129
93
|
"./icon-button": {
|
|
130
|
-
"
|
|
131
|
-
"
|
|
94
|
+
"types": "./dist/icon-button/index.d.ts",
|
|
95
|
+
"import": "./dist/icon-button/index.js"
|
|
132
96
|
},
|
|
133
97
|
"./if": {
|
|
134
|
-
"
|
|
135
|
-
"
|
|
98
|
+
"types": "./dist/if/index.d.ts",
|
|
99
|
+
"import": "./dist/if/index.js"
|
|
136
100
|
},
|
|
137
101
|
"./iframe": {
|
|
138
|
-
"
|
|
139
|
-
"
|
|
102
|
+
"types": "./dist/iframe/index.d.ts",
|
|
103
|
+
"import": "./dist/iframe/index.js"
|
|
140
104
|
},
|
|
141
105
|
"./link": {
|
|
142
|
-
"
|
|
143
|
-
"
|
|
106
|
+
"types": "./dist/link/index.d.ts",
|
|
107
|
+
"import": "./dist/link/index.js"
|
|
144
108
|
},
|
|
145
109
|
"./list": {
|
|
146
|
-
"
|
|
147
|
-
"
|
|
110
|
+
"types": "./dist/list/index.d.ts",
|
|
111
|
+
"import": "./dist/list/index.js"
|
|
148
112
|
},
|
|
149
113
|
"./markdown-container": {
|
|
150
|
-
"
|
|
151
|
-
"
|
|
114
|
+
"types": "./dist/markdown-container/index.d.ts",
|
|
115
|
+
"import": "./dist/markdown-container/index.js"
|
|
152
116
|
},
|
|
153
117
|
"./password-input": {
|
|
154
|
-
"
|
|
155
|
-
"
|
|
118
|
+
"types": "./dist/password-input/index.d.ts",
|
|
119
|
+
"import": "./dist/password-input/index.js"
|
|
156
120
|
},
|
|
157
121
|
"./picture": {
|
|
158
|
-
"
|
|
159
|
-
"
|
|
122
|
+
"types": "./dist/picture/index.d.ts",
|
|
123
|
+
"import": "./dist/picture/index.js"
|
|
160
124
|
},
|
|
161
125
|
"./protected-email": {
|
|
162
|
-
"
|
|
163
|
-
"
|
|
126
|
+
"types": "./dist/protected-email/index.d.ts",
|
|
127
|
+
"import": "./dist/protected-email/index.js"
|
|
164
128
|
},
|
|
165
129
|
"./tailwind.css": "./src/tailwind.css",
|
|
166
130
|
"./text": {
|
|
167
|
-
"
|
|
168
|
-
"
|
|
131
|
+
"types": "./dist/text/index.d.ts",
|
|
132
|
+
"import": "./dist/text/index.js"
|
|
169
133
|
},
|
|
170
134
|
"./utils/author": {
|
|
171
|
-
"
|
|
172
|
-
"
|
|
135
|
+
"types": "./dist/utils/author/index.d.ts",
|
|
136
|
+
"import": "./dist/utils/author/index.js"
|
|
173
137
|
},
|
|
174
138
|
"./utils/text": {
|
|
175
|
-
"
|
|
176
|
-
"
|
|
139
|
+
"types": "./dist/utils/text/index.d.ts",
|
|
140
|
+
"import": "./dist/utils/text/index.js"
|
|
177
141
|
}
|
|
178
142
|
},
|
|
179
|
-
"files": [
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"name": "@regardio/react",
|
|
184
|
-
"private": false,
|
|
185
|
-
"publishConfig": {
|
|
186
|
-
"access": "public"
|
|
187
|
-
},
|
|
188
|
-
"repository": {
|
|
189
|
-
"type": "git",
|
|
190
|
-
"url": "git+https://github.com/regardio/react.git"
|
|
191
|
-
},
|
|
143
|
+
"files": [
|
|
144
|
+
"dist",
|
|
145
|
+
"src"
|
|
146
|
+
],
|
|
192
147
|
"scripts": {
|
|
193
148
|
"build": "tsup && post-build-exports --preserve \"./tailwind.css\" && pnpm fix",
|
|
194
149
|
"clean": "exec-clean .turbo dist",
|
|
@@ -196,13 +151,14 @@
|
|
|
196
151
|
"fix": "exec-p fix:*",
|
|
197
152
|
"fix:biome": "lint-biome check --write --unsafe .",
|
|
198
153
|
"fix:md": "lint-md --fix",
|
|
154
|
+
"fix:pkg": "lint-package --fix",
|
|
199
155
|
"lint": "exec-p lint:*",
|
|
200
156
|
"lint:biome": "lint-biome check .",
|
|
201
157
|
"lint:md": "lint-md",
|
|
158
|
+
"lint:pkg": "lint-package",
|
|
202
159
|
"prepare": "exec-husky",
|
|
203
160
|
"release": "flow-release",
|
|
204
|
-
"report": "
|
|
205
|
-
"report:unit": "vitest run --reporter html",
|
|
161
|
+
"report": "vitest run --coverage",
|
|
206
162
|
"storybook": "storybook dev -p 6006",
|
|
207
163
|
"storybook:build": "storybook build -o storybook-static",
|
|
208
164
|
"test": "exec-s test:*",
|
|
@@ -210,7 +166,63 @@
|
|
|
210
166
|
"typecheck": "exec-tsc --noEmit",
|
|
211
167
|
"version": "flow-changeset version"
|
|
212
168
|
},
|
|
213
|
-
"
|
|
214
|
-
|
|
215
|
-
|
|
169
|
+
"dependencies": {
|
|
170
|
+
"@base-ui/react": "1.0.0",
|
|
171
|
+
"@maptiler/leaflet-maptilersdk": "4.1.1",
|
|
172
|
+
"@maptiler/sdk": "3.9.0",
|
|
173
|
+
"@mdx-js/react": "3.1.1",
|
|
174
|
+
"@regardio/js": "0.6.0",
|
|
175
|
+
"@regardio/tailwind": "0.2.0",
|
|
176
|
+
"@supabase/supabase-js": "2.90.1",
|
|
177
|
+
"cmdk": "1.1.1",
|
|
178
|
+
"embla-carousel": "8.6.0",
|
|
179
|
+
"embla-carousel-react": "8.6.0",
|
|
180
|
+
"input-otp": "1.4.2",
|
|
181
|
+
"intl-parse-accept-language": "1.0.0",
|
|
182
|
+
"leaflet": "alpha",
|
|
183
|
+
"lucide-react": "0.562.0",
|
|
184
|
+
"markdown-to-jsx": "9.5.7",
|
|
185
|
+
"react": "19.2.3",
|
|
186
|
+
"react-day-picker": "9.13.0",
|
|
187
|
+
"react-dom": "19.2.3",
|
|
188
|
+
"react-hook-form": "7.71.0",
|
|
189
|
+
"react-resizable-panels": "4.4.0",
|
|
190
|
+
"react-router": "7.12.0",
|
|
191
|
+
"tailwind-variants": "3.2.2",
|
|
192
|
+
"vaul": "1.1.2",
|
|
193
|
+
"zod": "4.3.5"
|
|
194
|
+
},
|
|
195
|
+
"devDependencies": {
|
|
196
|
+
"@regardio/dev": "1.11.4",
|
|
197
|
+
"@storybook/addon-a11y": "10.1.11",
|
|
198
|
+
"@storybook/addon-docs": "10.1.11",
|
|
199
|
+
"@storybook/addon-vitest": "10.1.11",
|
|
200
|
+
"@storybook/react-vite": "10.1.11",
|
|
201
|
+
"@tailwindcss/vite": "4.1.18",
|
|
202
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
203
|
+
"@testing-library/react": "16.3.1",
|
|
204
|
+
"@total-typescript/ts-reset": "0.6.1",
|
|
205
|
+
"@types/leaflet": "1.9.21",
|
|
206
|
+
"@types/node": "25.0.8",
|
|
207
|
+
"@types/react": "19.2.8",
|
|
208
|
+
"@types/react-dom": "19.2.3",
|
|
209
|
+
"@vitejs/plugin-react": "5.1.2",
|
|
210
|
+
"@vitest/browser-playwright": "4.0.17",
|
|
211
|
+
"@vitest/coverage-v8": "4.0.17",
|
|
212
|
+
"@vitest/ui": "4.0.17",
|
|
213
|
+
"jsdom": "27.4.0",
|
|
214
|
+
"playwright": "1.57.0",
|
|
215
|
+
"storybook": "10.1.11",
|
|
216
|
+
"tailwindcss": "4.1.18",
|
|
217
|
+
"tsup": "8.5.1",
|
|
218
|
+
"typescript": "5.9.3",
|
|
219
|
+
"vite": "7.3.1",
|
|
220
|
+
"vitest": "4.0.17"
|
|
221
|
+
},
|
|
222
|
+
"engines": {
|
|
223
|
+
"node": ">=18"
|
|
224
|
+
},
|
|
225
|
+
"publishConfig": {
|
|
226
|
+
"access": "public"
|
|
227
|
+
}
|
|
216
228
|
}
|
|
@@ -66,3 +66,72 @@ export const FastTransition: Story = {
|
|
|
66
66
|
transitionDuration: 1000,
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
|
+
|
|
70
|
+
export const SingleImage: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=Single',
|
|
73
|
+
className: 'h-[400px] w-full',
|
|
74
|
+
images: [sampleImages[0] as ImageData],
|
|
75
|
+
locale: 'en',
|
|
76
|
+
slideshow: true,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const EmptyImages: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
baseUrl: 'https://via.placeholder.com/{format}',
|
|
83
|
+
className: 'h-[400px] w-full bg-gray-200',
|
|
84
|
+
images: [],
|
|
85
|
+
locale: 'en',
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const WithFilter: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=Filtered',
|
|
92
|
+
className: 'h-[400px] w-full',
|
|
93
|
+
filter: (img: ImageData) => img.hu > 100,
|
|
94
|
+
images: sampleImages,
|
|
95
|
+
locale: 'en',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const GermanLocale: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=German',
|
|
102
|
+
className: 'h-[400px] w-full',
|
|
103
|
+
images: sampleImages,
|
|
104
|
+
locale: 'de',
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const ShortTransition: Story = {
|
|
109
|
+
args: {
|
|
110
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=Short',
|
|
111
|
+
className: 'h-[400px] w-full',
|
|
112
|
+
images: sampleImages,
|
|
113
|
+
locale: 'en',
|
|
114
|
+
slideshowInterval: 1000,
|
|
115
|
+
transitionDuration: 2000,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const MediumTransition: Story = {
|
|
120
|
+
args: {
|
|
121
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=Medium',
|
|
122
|
+
className: 'h-[400px] w-full',
|
|
123
|
+
images: sampleImages,
|
|
124
|
+
locale: 'en',
|
|
125
|
+
transitionDuration: 4000,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const LongTransition: Story = {
|
|
130
|
+
args: {
|
|
131
|
+
baseUrl: 'https://via.placeholder.com/{format}?text=Long',
|
|
132
|
+
className: 'h-[400px] w-full',
|
|
133
|
+
images: sampleImages,
|
|
134
|
+
locale: 'en',
|
|
135
|
+
transitionDuration: 5000,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -87,3 +87,49 @@ export const ManySlides: Story = {
|
|
|
87
87
|
</Carousel.Root>
|
|
88
88
|
),
|
|
89
89
|
};
|
|
90
|
+
|
|
91
|
+
export const VerticalOrientation: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<Carousel.Root
|
|
94
|
+
className="w-full max-w-md h-[400px]"
|
|
95
|
+
orientation="vertical"
|
|
96
|
+
>
|
|
97
|
+
<Carousel.Content className="flex-col gap-4">
|
|
98
|
+
<Carousel.Item>
|
|
99
|
+
<SlideCard>Vertical Slide 1</SlideCard>
|
|
100
|
+
</Carousel.Item>
|
|
101
|
+
<Carousel.Item>
|
|
102
|
+
<SlideCard>Vertical Slide 2</SlideCard>
|
|
103
|
+
</Carousel.Item>
|
|
104
|
+
<Carousel.Item>
|
|
105
|
+
<SlideCard>Vertical Slide 3</SlideCard>
|
|
106
|
+
</Carousel.Item>
|
|
107
|
+
</Carousel.Content>
|
|
108
|
+
</Carousel.Root>
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const WithOptions: Story = {
|
|
113
|
+
render: () => (
|
|
114
|
+
<Carousel.Root
|
|
115
|
+
className="w-full max-w-md"
|
|
116
|
+
opts={{ align: 'start', loop: true }}
|
|
117
|
+
>
|
|
118
|
+
<Carousel.Content className="gap-4">
|
|
119
|
+
<Carousel.Item>
|
|
120
|
+
<SlideCard>Loop Slide 1</SlideCard>
|
|
121
|
+
</Carousel.Item>
|
|
122
|
+
<Carousel.Item>
|
|
123
|
+
<SlideCard>Loop Slide 2</SlideCard>
|
|
124
|
+
</Carousel.Item>
|
|
125
|
+
<Carousel.Item>
|
|
126
|
+
<SlideCard>Loop Slide 3</SlideCard>
|
|
127
|
+
</Carousel.Item>
|
|
128
|
+
</Carousel.Content>
|
|
129
|
+
<div className="flex justify-center gap-4 mt-4">
|
|
130
|
+
<Carousel.Previous className="px-4 py-2 bg-gray-200 rounded">←</Carousel.Previous>
|
|
131
|
+
<Carousel.Next className="px-4 py-2 bg-gray-200 rounded">→</Carousel.Next>
|
|
132
|
+
</div>
|
|
133
|
+
</Carousel.Root>
|
|
134
|
+
),
|
|
135
|
+
};
|
|
@@ -8,13 +8,3 @@ export const NonceContext = createContext<string>('');
|
|
|
8
8
|
export const NonceProvider = NonceContext.Provider;
|
|
9
9
|
|
|
10
10
|
export const useNonce = () => useContext(NonceContext);
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Generate a cryptographically secure nonce for CSP.
|
|
14
|
-
* @returns A base64-encoded random nonce
|
|
15
|
-
*/
|
|
16
|
-
export function generateNonce(): string {
|
|
17
|
-
const array = new Uint8Array(16);
|
|
18
|
-
crypto.getRandomValues(array);
|
|
19
|
-
return btoa(String.fromCharCode(...array));
|
|
20
|
-
}
|
|
@@ -56,3 +56,54 @@ export const AllVariants: Story = {
|
|
|
56
56
|
</div>
|
|
57
57
|
),
|
|
58
58
|
};
|
|
59
|
+
|
|
60
|
+
export const TelephoneLink: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
children: 'Call Us',
|
|
63
|
+
to: 'tel:+1234567890',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const MailtoLink: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
children: 'Email Us',
|
|
70
|
+
to: 'mailto:hello@example.com',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const HashLink: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
children: 'Jump to Section',
|
|
77
|
+
to: '#section-id',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const WithSearchAndHash: Story = {
|
|
82
|
+
args: {
|
|
83
|
+
children: 'Link with Query',
|
|
84
|
+
to: { hash: '#results', pathname: '/search', search: '?q=test' },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const EmptyPath: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
children: 'No destination',
|
|
91
|
+
to: '',
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const WithArrow: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
arrow: 'rarr',
|
|
98
|
+
children: 'Link with Arrow',
|
|
99
|
+
to: '/arrow',
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const ButtonVariant: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
children: 'Button Style Link',
|
|
106
|
+
to: '/button',
|
|
107
|
+
variant: 'button',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
+
import { MemoryRouter } from 'react-router';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { Link, LinkBase, MarkdownLink, PathResolverProvider } from './link';
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
cleanup();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const renderWithRouter = (ui: React.ReactNode) => {
|
|
12
|
+
return render(<MemoryRouter>{ui}</MemoryRouter>);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
describe('LinkBase', () => {
|
|
16
|
+
it('renders internal link with NavLink', () => {
|
|
17
|
+
renderWithRouter(<LinkBase to="/about">About</LinkBase>);
|
|
18
|
+
expect(screen.getByText('About')).toBeDefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders external http link as anchor', () => {
|
|
22
|
+
renderWithRouter(<LinkBase to="https://example.com">External</LinkBase>);
|
|
23
|
+
const link = screen.getByText('External');
|
|
24
|
+
expect(link.tagName).toBe('A');
|
|
25
|
+
expect(link.getAttribute('href')).toBe('https://example.com');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders mailto link as anchor', () => {
|
|
29
|
+
renderWithRouter(<LinkBase to="mailto:test@example.com">Email</LinkBase>);
|
|
30
|
+
const link = screen.getByText('Email');
|
|
31
|
+
expect(link.tagName).toBe('A');
|
|
32
|
+
expect(link.getAttribute('href')).toBe('mailto:test@example.com');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders tel link as anchor', () => {
|
|
36
|
+
renderWithRouter(<LinkBase to="tel:+1234567890">Call</LinkBase>);
|
|
37
|
+
const link = screen.getByText('Call');
|
|
38
|
+
expect(link.tagName).toBe('A');
|
|
39
|
+
expect(link.getAttribute('href')).toBe('tel:+1234567890');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('uses pathResolver when routeKey is provided', () => {
|
|
43
|
+
const resolver = vi.fn().mockReturnValue('/resolved-path');
|
|
44
|
+
renderWithRouter(
|
|
45
|
+
<PathResolverProvider value={resolver}>
|
|
46
|
+
<LinkBase routeKey="home">Home</LinkBase>
|
|
47
|
+
</PathResolverProvider>,
|
|
48
|
+
);
|
|
49
|
+
expect(resolver).toHaveBeenCalledWith('home');
|
|
50
|
+
expect(screen.getByText('Home')).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('handles object to prop with pathname, search, and hash', () => {
|
|
54
|
+
renderWithRouter(
|
|
55
|
+
<LinkBase to={{ hash: '#section', pathname: '/page', search: '?q=test' }}>Complex</LinkBase>,
|
|
56
|
+
);
|
|
57
|
+
expect(screen.getByText('Complex')).toBeDefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('renders children only when path is empty', () => {
|
|
61
|
+
renderWithRouter(<LinkBase>No Link</LinkBase>);
|
|
62
|
+
expect(screen.getByText('No Link')).toBeDefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('calls onClick handler', () => {
|
|
66
|
+
const handleClick = vi.fn();
|
|
67
|
+
renderWithRouter(
|
|
68
|
+
<LinkBase
|
|
69
|
+
onClick={handleClick}
|
|
70
|
+
to="/test"
|
|
71
|
+
>
|
|
72
|
+
Click Me
|
|
73
|
+
</LinkBase>,
|
|
74
|
+
);
|
|
75
|
+
fireEvent.click(screen.getByText('Click Me'));
|
|
76
|
+
expect(handleClick).toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('opens external http link in new window', () => {
|
|
80
|
+
const windowOpen = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
81
|
+
renderWithRouter(<LinkBase to="https://example.com">External</LinkBase>);
|
|
82
|
+
fireEvent.click(screen.getByText('External'));
|
|
83
|
+
expect(windowOpen).toHaveBeenCalledWith('https://example.com', '_blank', 'noopener,noreferrer');
|
|
84
|
+
windowOpen.mockRestore();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('scrolls to element for hash links', () => {
|
|
88
|
+
const scrollIntoView = vi.fn();
|
|
89
|
+
const element = document.createElement('div');
|
|
90
|
+
element.id = 'section';
|
|
91
|
+
element.scrollIntoView = scrollIntoView;
|
|
92
|
+
document.body.appendChild(element);
|
|
93
|
+
|
|
94
|
+
renderWithRouter(<LinkBase to="#section">Jump</LinkBase>);
|
|
95
|
+
fireEvent.click(screen.getByText('Jump'));
|
|
96
|
+
expect(scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
|
|
97
|
+
|
|
98
|
+
document.body.removeChild(element);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('handles hash link when element not found', () => {
|
|
102
|
+
renderWithRouter(<LinkBase to="#nonexistent">Jump</LinkBase>);
|
|
103
|
+
fireEvent.click(screen.getByText('Jump'));
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('does not prevent default for tel links', () => {
|
|
107
|
+
renderWithRouter(<LinkBase to="tel:+1234567890">Call</LinkBase>);
|
|
108
|
+
const link = screen.getByText('Call');
|
|
109
|
+
const event = fireEvent.click(link);
|
|
110
|
+
expect(event).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('respects defaultPrevented in onClick', () => {
|
|
114
|
+
const handleClick = vi.fn((e: React.MouseEvent) => e.preventDefault());
|
|
115
|
+
const windowOpen = vi.spyOn(window, 'open').mockImplementation(() => null);
|
|
116
|
+
|
|
117
|
+
renderWithRouter(
|
|
118
|
+
<LinkBase
|
|
119
|
+
onClick={handleClick}
|
|
120
|
+
to="https://example.com"
|
|
121
|
+
>
|
|
122
|
+
External
|
|
123
|
+
</LinkBase>,
|
|
124
|
+
);
|
|
125
|
+
fireEvent.click(screen.getByText('External'));
|
|
126
|
+
expect(handleClick).toHaveBeenCalled();
|
|
127
|
+
expect(windowOpen).not.toHaveBeenCalled();
|
|
128
|
+
|
|
129
|
+
windowOpen.mockRestore();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('Link', () => {
|
|
134
|
+
it('renders with variant', () => {
|
|
135
|
+
renderWithRouter(
|
|
136
|
+
<Link
|
|
137
|
+
to="/test"
|
|
138
|
+
variant="button"
|
|
139
|
+
>
|
|
140
|
+
Button Link
|
|
141
|
+
</Link>,
|
|
142
|
+
);
|
|
143
|
+
expect(screen.getByText('Button Link')).toBeDefined();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('renders with arrow', () => {
|
|
147
|
+
renderWithRouter(
|
|
148
|
+
<Link
|
|
149
|
+
arrow="rarr"
|
|
150
|
+
to="/test"
|
|
151
|
+
>
|
|
152
|
+
Arrow Link
|
|
153
|
+
</Link>,
|
|
154
|
+
);
|
|
155
|
+
expect(screen.getByText('Arrow Link')).toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('MarkdownLink', () => {
|
|
160
|
+
it('renders Link when href is provided', () => {
|
|
161
|
+
renderWithRouter(<MarkdownLink href="/page">Markdown</MarkdownLink>);
|
|
162
|
+
expect(screen.getByText('Markdown')).toBeDefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('returns null when href is not provided', () => {
|
|
166
|
+
const { container } = renderWithRouter(<MarkdownLink>No Link</MarkdownLink>);
|
|
167
|
+
expect(container.innerHTML).toBe('');
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -74,3 +74,58 @@ export const WithCharacterLimit: Story = {
|
|
|
74
74
|
locale: 'en',
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
|
+
|
|
78
|
+
export const WithMDXComponents: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
children: `# Custom Components
|
|
81
|
+
|
|
82
|
+
This text uses custom MDX components.`,
|
|
83
|
+
locale: 'en',
|
|
84
|
+
mdxComponents: {},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const WithMarkdownOverrides: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
children: `# Overridden Heading
|
|
91
|
+
|
|
92
|
+
This paragraph has custom styling applied.`,
|
|
93
|
+
locale: 'en',
|
|
94
|
+
markdownOverrides: {
|
|
95
|
+
h1: {
|
|
96
|
+
props: {
|
|
97
|
+
className: 'text-4xl font-bold text-blue-600',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const WithInternalLink: Story = {
|
|
105
|
+
args: {
|
|
106
|
+
children: 'Check out [our page](https://regardio.com/about) for more info.',
|
|
107
|
+
locale: 'en',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const WithExternalLink: Story = {
|
|
112
|
+
args: {
|
|
113
|
+
children: 'Visit [external site](https://example.com) for details.',
|
|
114
|
+
locale: 'en',
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const EmptyContent: Story = {
|
|
119
|
+
args: {
|
|
120
|
+
children: '',
|
|
121
|
+
locale: 'en',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const WithClassName: Story = {
|
|
126
|
+
args: {
|
|
127
|
+
children: '**Bold text** in a styled container.',
|
|
128
|
+
className: 'bg-gray-100 p-4 rounded',
|
|
129
|
+
locale: 'en',
|
|
130
|
+
},
|
|
131
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { isValidElement, type ReactNode } from 'react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { lowerCaseSzett, replaceSpecialChars, shy, wrapSentences } from './text';
|
|
5
|
+
|
|
6
|
+
describe('lowerCaseSzett', () => {
|
|
7
|
+
it('wraps ß in lowercase span for strings', () => {
|
|
8
|
+
const result = lowerCaseSzett('Straße') as ReactNode[];
|
|
9
|
+
expect(Array.isArray(result)).toBe(true);
|
|
10
|
+
expect(result).toHaveLength(3);
|
|
11
|
+
expect(result[0]).toBe('Stra');
|
|
12
|
+
expect(isValidElement(result[1])).toBe(true);
|
|
13
|
+
expect((result[1] as { props: { className: string } }).props.className).toBe('lowercase');
|
|
14
|
+
expect((result[1] as { props: { children: string } }).props.children).toBe('ß');
|
|
15
|
+
expect(result[2]).toBe('e');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('handles strings without ß', () => {
|
|
19
|
+
const result = lowerCaseSzett('Hello') as ReactNode[];
|
|
20
|
+
expect(Array.isArray(result)).toBe(true);
|
|
21
|
+
expect(result).toEqual(['Hello']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('handles React elements with children containing ß', () => {
|
|
25
|
+
const element = <div>Straße</div>;
|
|
26
|
+
const result = lowerCaseSzett(element);
|
|
27
|
+
expect(isValidElement(result)).toBe(true);
|
|
28
|
+
const children = (result as { props: { children: ReactNode[] } }).props.children;
|
|
29
|
+
expect(Array.isArray(children)).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('handles nested React elements', () => {
|
|
33
|
+
const element = (
|
|
34
|
+
<div>
|
|
35
|
+
<span>Große Straße</span>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
const result = lowerCaseSzett(element);
|
|
39
|
+
expect(isValidElement(result)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('handles arrays of children', () => {
|
|
43
|
+
const result = lowerCaseSzett(['Straße', ' und ', 'Größe']);
|
|
44
|
+
expect(Array.isArray(result)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns non-string/element values as-is', () => {
|
|
48
|
+
expect(lowerCaseSzett(null)).toBeNull();
|
|
49
|
+
expect(lowerCaseSzett(undefined)).toBeUndefined();
|
|
50
|
+
expect(lowerCaseSzett(123 as unknown as string)).toBe(123);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('shy', () => {
|
|
55
|
+
it('returns null for null input', () => {
|
|
56
|
+
expect(shy(null)).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('replaces soft hyphens in strings', () => {
|
|
60
|
+
const input = 'Weihnachts\u00ADspende';
|
|
61
|
+
const result = shy(input);
|
|
62
|
+
expect(typeof result).toBe('string');
|
|
63
|
+
expect(result).toContain('\u00AD');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('handles React elements with soft hyphens', () => {
|
|
67
|
+
const element = <div>Weihnachtsspende</div>;
|
|
68
|
+
const result = shy(element);
|
|
69
|
+
expect(isValidElement(result)).toBe(true);
|
|
70
|
+
const children = (result as { props: { children: string } }).props.children;
|
|
71
|
+
expect(children).toBe('Weihnachtsspende');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('handles nested React elements with soft hyphens', () => {
|
|
75
|
+
const element = (
|
|
76
|
+
<div>
|
|
77
|
+
<span>Weihnachtsspende</span>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
const result = shy(element);
|
|
81
|
+
expect(isValidElement(result)).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('handles arrays in React nodes', () => {
|
|
85
|
+
const element = <div>{['Weihnachtsspende', ' ', 'Testwort']}</div>;
|
|
86
|
+
const result = shy(element);
|
|
87
|
+
expect(isValidElement(result)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('replaceSpecialChars', () => {
|
|
92
|
+
it('applies typographic quotes and shy', () => {
|
|
93
|
+
const result = replaceSpecialChars('"Hello"', 'de');
|
|
94
|
+
expect(typeof result).toBe('string');
|
|
95
|
+
expect(result).toBe('\u201EHello\u201D');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('wrapSentences', () => {
|
|
100
|
+
it('wraps each sentence in a span', () => {
|
|
101
|
+
const result = wrapSentences('First sentence. Second sentence.');
|
|
102
|
+
expect(Array.isArray(result)).toBe(true);
|
|
103
|
+
expect((result as ReactNode[]).length).toBeGreaterThanOrEqual(2);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('handles single sentence', () => {
|
|
107
|
+
const result = wrapSentences('Just one sentence.');
|
|
108
|
+
expect(Array.isArray(result)).toBe(true);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useCurrentRouteData } from './use-current-route-data';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useFocusSearch } from './use-focus-search';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useMatchesData } from './use-matches-data';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useMediaQuery } from './use-media-query';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useIsMobile } from './use-mobile';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { NonceProvider, useNonce } from './use-nonce';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { useOrientation } from './use-orientation';
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/src/hooks/{use-current-route-data/use-current-route-data.ts → use-current-route-data.ts}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|