@alepha/react 0.14.2 → 0.14.4

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.
Files changed (57) hide show
  1. package/dist/auth/index.browser.js +29 -14
  2. package/dist/auth/index.browser.js.map +1 -1
  3. package/dist/auth/index.js +960 -195
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/core/index.d.ts +4 -0
  6. package/dist/core/index.d.ts.map +1 -1
  7. package/dist/core/index.js +7 -4
  8. package/dist/core/index.js.map +1 -1
  9. package/dist/head/index.browser.js +59 -19
  10. package/dist/head/index.browser.js.map +1 -1
  11. package/dist/head/index.d.ts +99 -560
  12. package/dist/head/index.d.ts.map +1 -1
  13. package/dist/head/index.js +92 -87
  14. package/dist/head/index.js.map +1 -1
  15. package/dist/router/index.browser.js +30 -15
  16. package/dist/router/index.browser.js.map +1 -1
  17. package/dist/router/index.d.ts +616 -192
  18. package/dist/router/index.d.ts.map +1 -1
  19. package/dist/router/index.js +961 -196
  20. package/dist/router/index.js.map +1 -1
  21. package/package.json +4 -4
  22. package/src/auth/__tests__/$auth.spec.ts +188 -0
  23. package/src/core/__tests__/Router.spec.tsx +169 -0
  24. package/src/core/hooks/useAction.browser.spec.tsx +569 -0
  25. package/src/core/hooks/useAction.ts +11 -0
  26. package/src/form/hooks/useForm.browser.spec.tsx +366 -0
  27. package/src/head/helpers/SeoExpander.spec.ts +203 -0
  28. package/src/head/hooks/useHead.spec.tsx +288 -0
  29. package/src/head/index.ts +11 -28
  30. package/src/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  31. package/src/head/providers/BrowserHeadProvider.ts +25 -19
  32. package/src/head/providers/HeadProvider.ts +76 -10
  33. package/src/head/providers/ServerHeadProvider.ts +22 -138
  34. package/src/i18n/__tests__/integration.spec.tsx +239 -0
  35. package/src/i18n/components/Localize.spec.tsx +357 -0
  36. package/src/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  37. package/src/i18n/providers/I18nProvider.spec.ts +389 -0
  38. package/src/router/__tests__/page-head-browser.browser.spec.ts +91 -0
  39. package/src/router/__tests__/page-head.spec.ts +44 -0
  40. package/src/router/__tests__/seo-head.spec.ts +121 -0
  41. package/src/router/atoms/ssrManifestAtom.ts +60 -0
  42. package/src/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  43. package/src/router/errors/Redirection.ts +1 -1
  44. package/src/router/index.shared.ts +1 -0
  45. package/src/router/index.ts +16 -2
  46. package/src/router/primitives/$page.browser.spec.tsx +702 -0
  47. package/src/router/primitives/$page.spec.tsx +702 -0
  48. package/src/router/primitives/$page.ts +46 -10
  49. package/src/router/providers/ReactBrowserProvider.ts +14 -29
  50. package/src/router/providers/ReactBrowserRouterProvider.ts +5 -0
  51. package/src/router/providers/ReactPageProvider.ts +11 -4
  52. package/src/router/providers/ReactServerProvider.spec.tsx +316 -0
  53. package/src/router/providers/ReactServerProvider.ts +331 -315
  54. package/src/router/providers/ReactServerTemplateProvider.ts +775 -0
  55. package/src/router/providers/SSRManifestProvider.ts +365 -0
  56. package/src/router/services/ReactPageServerService.ts +5 -3
  57. package/src/router/services/ReactRouter.ts +3 -3
@@ -0,0 +1,357 @@
1
+ import { AlephaContext } from "@alepha/react";
2
+ import { I18nProvider, Localize } from "../index.ts";
3
+ import { Alepha, t } from "alepha";
4
+ import { DateTimeProvider } from "alepha/datetime";
5
+ import type { ReactNode } from "react";
6
+ import { renderToString } from "react-dom/server";
7
+ import { describe, expect, it } from "vitest";
8
+
9
+ describe("<Localize/>", () => {
10
+ const setup = () => {
11
+ const alepha = Alepha.create();
12
+ return {
13
+ i18n: alepha.inject(I18nProvider),
14
+ dateTime: alepha.inject(DateTimeProvider),
15
+ alepha,
16
+ render: (children: ReactNode) =>
17
+ renderToString(
18
+ <AlephaContext value={alepha}>{children}</AlephaContext>,
19
+ ),
20
+ };
21
+ };
22
+
23
+ it("should format date", () => {
24
+ const { render, i18n } = setup();
25
+ const date = new Date("2024-09-23T23:45:00Z");
26
+ expect(render(<Localize value={date} />)).toBe("9/24/2024");
27
+ i18n.setLang("fr");
28
+ expect(render(<Localize value={date} />)).toBe("24/09/2024");
29
+ });
30
+
31
+ it("should format number", () => {
32
+ const { render, i18n } = setup();
33
+ const number = 1234567.89;
34
+ expect(render(<Localize value={number} />)).toBe("1,234,567.89");
35
+ i18n.setLang("de");
36
+ expect(render(<Localize value={number} />)).toBe("1.234.567,89");
37
+ i18n.setLang("fr");
38
+ expect(render(<Localize value={number} />)).toBe("1\u202f234\u202f567,89");
39
+ });
40
+
41
+ // it("should format typebox error", async () => {
42
+ // const { render, i18n, alepha } = setup();
43
+ // const boom = async () => alepha.codec.decode(t.number(), "..");
44
+ // const error = await boom().catch((err) => err);
45
+ // expect(render(<Localize value={error} />)).toBe("must be number");
46
+ // await i18n.setLang("fr");
47
+ // expect(render(<Localize value={error} />)).toBe("doit être number");
48
+ // });
49
+
50
+ describe("number formatting options", () => {
51
+ it("should format currency", () => {
52
+ const { render } = setup();
53
+ const number = 1234.56;
54
+ expect(
55
+ render(
56
+ <Localize
57
+ value={number}
58
+ number={{ style: "currency", currency: "USD" }}
59
+ />,
60
+ ),
61
+ ).toBe("$1,234.56");
62
+ });
63
+
64
+ it("should format currency with locale", () => {
65
+ const { render, i18n } = setup();
66
+ const number = 1234.56;
67
+ i18n.setLang("fr");
68
+ expect(
69
+ render(
70
+ <Localize
71
+ value={number}
72
+ number={{ style: "currency", currency: "EUR" }}
73
+ />,
74
+ ),
75
+ ).toBe("1\u202f234,56\u00a0€");
76
+ });
77
+
78
+ it("should format percentage", () => {
79
+ const { render } = setup();
80
+ const number = 0.1234;
81
+ expect(
82
+ render(<Localize value={number} number={{ style: "percent" }} />),
83
+ ).toBe("12%");
84
+ });
85
+
86
+ it("should format with minimum fraction digits", () => {
87
+ const { render } = setup();
88
+ const number = 10;
89
+ expect(
90
+ render(
91
+ <Localize value={number} number={{ minimumFractionDigits: 2 }} />,
92
+ ),
93
+ ).toBe("10.00");
94
+ });
95
+
96
+ it("should format with maximum fraction digits", () => {
97
+ const { render } = setup();
98
+ const number = 10.123456;
99
+ expect(
100
+ render(
101
+ <Localize value={number} number={{ maximumFractionDigits: 2 }} />,
102
+ ),
103
+ ).toBe("10.12");
104
+ });
105
+
106
+ it("should format with notation", () => {
107
+ const { render } = setup();
108
+ const number = 1000000;
109
+ expect(
110
+ render(<Localize value={number} number={{ notation: "compact" }} />),
111
+ ).toBe("1M");
112
+ });
113
+ });
114
+
115
+ describe("date formatting options", () => {
116
+ it("should format with dayjs format string - LLL", () => {
117
+ const { render, i18n, dateTime } = setup();
118
+ const date = dateTime.utc("2024-09-24T00:45:00Z").toDate();
119
+ const formatted = render(<Localize value={date} date="LLL" />);
120
+ // Format depends on local timezone, so just check it contains the key parts
121
+ expect(formatted).toContain("September");
122
+ expect(formatted).toContain("2024");
123
+ i18n.setLang("fr");
124
+ const frFormatted = render(<Localize value={date} date="LLL" />);
125
+ expect(frFormatted).toContain("septembre");
126
+ expect(frFormatted).toContain("2024");
127
+ });
128
+
129
+ it("should format with dayjs format string - YYYY-MM-DD", () => {
130
+ const { render } = setup();
131
+ const date = new Date("2024-09-23T23:45:00Z");
132
+ expect(render(<Localize value={date} date="YYYY-MM-DD" />)).toBe(
133
+ "2024-09-24",
134
+ );
135
+ });
136
+
137
+ it("should format with dayjs format string - dddd, MMMM D YYYY", () => {
138
+ const { render } = setup();
139
+ const date = new Date("2024-09-23T23:45:00Z");
140
+ expect(render(<Localize value={date} date="dddd, MMMM D YYYY" />)).toBe(
141
+ "Tuesday, September 24 2024",
142
+ );
143
+ });
144
+
145
+ it("should format with fromNow for Date", () => {
146
+ const { render, dateTime } = setup();
147
+ dateTime.pause();
148
+ const date = dateTime.now().subtract(2, "hour").toDate();
149
+ const result = render(<Localize value={date} date="fromNow" />);
150
+ expect(result).toBe("2 hours ago");
151
+ });
152
+
153
+ it("should format with fromNow for DateTime", () => {
154
+ const { render, dateTime } = setup();
155
+ dateTime.pause();
156
+ const date = dateTime.now().subtract(3, "day");
157
+ const result = render(<Localize value={date} date="fromNow" />);
158
+ expect(result).toBe("3 days ago");
159
+ });
160
+
161
+ it("should format DateTime with dayjs format string", () => {
162
+ const { render, dateTime } = setup();
163
+ const date = dateTime.of("2024-09-23T23:45:00Z");
164
+ expect(render(<Localize value={date} date="YYYY-MM-DD" />)).toBe(
165
+ "2024-09-24",
166
+ );
167
+ });
168
+
169
+ it("should format with Intl.DateTimeFormatOptions", () => {
170
+ const { render } = setup();
171
+ const date = new Date("2024-09-23T23:45:00Z");
172
+ expect(
173
+ render(
174
+ <Localize
175
+ value={date}
176
+ date={{
177
+ weekday: "long",
178
+ year: "numeric",
179
+ month: "long",
180
+ day: "numeric",
181
+ }}
182
+ />,
183
+ ),
184
+ ).toBe("Tuesday, September 24, 2024");
185
+ });
186
+
187
+ it("should format with Intl.DateTimeFormatOptions and locale", () => {
188
+ const { render, i18n } = setup();
189
+ const date = new Date("2024-09-23T23:45:00Z");
190
+ i18n.setLang("fr");
191
+ expect(
192
+ render(
193
+ <Localize
194
+ value={date}
195
+ date={{
196
+ weekday: "long",
197
+ year: "numeric",
198
+ month: "long",
199
+ day: "numeric",
200
+ }}
201
+ />,
202
+ ),
203
+ ).toBe("mardi 24 septembre 2024");
204
+ });
205
+
206
+ it("should format DateTime with Intl.DateTimeFormatOptions", () => {
207
+ const { render, dateTime } = setup();
208
+ const date = dateTime.utc("2024-09-24T00:45:00Z");
209
+ const formatted = render(
210
+ <Localize
211
+ value={date}
212
+ date={{
213
+ hour: "2-digit",
214
+ minute: "2-digit",
215
+ hour12: true,
216
+ }}
217
+ />,
218
+ );
219
+ // Format depends on local timezone, so just verify it contains time parts
220
+ expect(formatted).toMatch(/\d{1,2}:\d{2}\s(AM|PM)/);
221
+ });
222
+ });
223
+
224
+ describe("string date parsing", () => {
225
+ it("should parse string date when dateOptions is provided", () => {
226
+ const { render } = setup();
227
+ const dateString = "2024-09-24T12:00:00Z";
228
+ expect(render(<Localize value={dateString} date="YYYY-MM-DD" />)).toBe(
229
+ "2024-09-24",
230
+ );
231
+ });
232
+
233
+ it("should parse string date with format options", () => {
234
+ const { render } = setup();
235
+ const dateString = "2024-09-24T12:00:00Z";
236
+ expect(render(<Localize value={dateString} date="LLL" />)).toContain(
237
+ "September",
238
+ );
239
+ });
240
+
241
+ it("should parse string date with timezone", () => {
242
+ const { render } = setup();
243
+ const dateString = "2024-09-24T12:00:00Z";
244
+ expect(
245
+ render(
246
+ <Localize
247
+ value={dateString}
248
+ timezone="America/New_York"
249
+ date="YYYY-MM-DD HH:mm"
250
+ />,
251
+ ),
252
+ ).toBe("2024-09-24 08:00");
253
+ });
254
+
255
+ it("should return string as-is when no dateOptions provided", () => {
256
+ const { render } = setup();
257
+ const dateString = "2024-09-24T12:00:00Z";
258
+ expect(render(<Localize value={dateString} />)).toBe(dateString);
259
+ });
260
+ });
261
+
262
+ describe("timezone formatting", () => {
263
+ it("should format Date with timezone using Intl", () => {
264
+ const { render } = setup();
265
+ const date = new Date("2024-09-24T12:00:00Z");
266
+ expect(
267
+ render(<Localize value={date} timezone="America/New_York" />),
268
+ ).toBe("9/24/2024");
269
+ });
270
+
271
+ it("should format Date with timezone and dateOptions", () => {
272
+ const { render } = setup();
273
+ const date = new Date("2024-09-24T12:00:00Z");
274
+ expect(
275
+ render(
276
+ <Localize
277
+ value={date}
278
+ timezone="America/New_York"
279
+ date={{
280
+ hour: "2-digit",
281
+ minute: "2-digit",
282
+ hour12: true,
283
+ timeZoneName: "short",
284
+ }}
285
+ />,
286
+ ),
287
+ ).toContain("EDT");
288
+ });
289
+
290
+ it("should format Date with timezone using dayjs format", () => {
291
+ const { render } = setup();
292
+ const date = new Date("2024-09-24T12:00:00Z");
293
+ const formatted = render(
294
+ <Localize
295
+ value={date}
296
+ timezone="America/New_York"
297
+ date="YYYY-MM-DD HH:mm"
298
+ />,
299
+ );
300
+ expect(formatted).toBe("2024-09-24 08:00");
301
+ });
302
+
303
+ it("should format DateTime with timezone using dayjs format", () => {
304
+ const { render, dateTime } = setup();
305
+ const date = dateTime.utc("2024-09-24T12:00:00Z");
306
+ const formatted = render(
307
+ <Localize
308
+ value={date}
309
+ timezone="Europe/Paris"
310
+ date="YYYY-MM-DD HH:mm"
311
+ />,
312
+ );
313
+ expect(formatted).toBe("2024-09-24 14:00");
314
+ });
315
+
316
+ it("should format DateTime with timezone and Intl options", () => {
317
+ const { render, dateTime } = setup();
318
+ const date = dateTime.utc("2024-09-24T12:00:00Z");
319
+ const formatted = render(
320
+ <Localize
321
+ value={date}
322
+ timezone="Asia/Tokyo"
323
+ date={{
324
+ hour: "2-digit",
325
+ minute: "2-digit",
326
+ hour12: false,
327
+ }}
328
+ />,
329
+ );
330
+ expect(formatted).toBe("21:00");
331
+ });
332
+
333
+ it("should format DateTime with timezone using LLL format", () => {
334
+ const { render, dateTime } = setup();
335
+ const date = dateTime.utc("2024-09-24T12:00:00Z");
336
+ const formatted = render(
337
+ <Localize value={date} timezone="America/Los_Angeles" date="LLL" />,
338
+ );
339
+ expect(formatted).toContain("September");
340
+ expect(formatted).toContain("2024");
341
+ expect(formatted).toContain("5:00 AM");
342
+ });
343
+
344
+ it("should respect locale and timezone together", () => {
345
+ const { render, i18n, dateTime } = setup();
346
+ const date = dateTime.utc("2024-09-24T12:00:00Z");
347
+ i18n.setLang("fr");
348
+ const formatted = render(
349
+ <Localize value={date} timezone="Europe/Paris" date="LLL" />,
350
+ );
351
+ console.log(formatted);
352
+ expect(formatted).toContain("septembre");
353
+ expect(formatted).toContain("2024");
354
+ expect(formatted).toContain("14:00");
355
+ });
356
+ });
357
+ });