@jasperoosthoek/react-toolbox 0.8.1 → 0.9.1

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 (63) hide show
  1. package/change-log.md +330 -312
  2. package/dist/components/buttons/ConfirmButton.d.ts +2 -2
  3. package/dist/components/buttons/DeleteConfirmButton.d.ts +2 -2
  4. package/dist/components/buttons/IconButtons.d.ts +40 -41
  5. package/dist/components/errors/Errors.d.ts +1 -2
  6. package/dist/components/forms/FormField.d.ts +22 -0
  7. package/dist/components/forms/FormFields.d.ts +1 -56
  8. package/dist/components/forms/FormModal.d.ts +7 -34
  9. package/dist/components/forms/FormModalProvider.d.ts +25 -15
  10. package/dist/components/forms/FormProvider.d.ts +66 -0
  11. package/dist/components/forms/fields/FormBadgesSelection.d.ts +26 -0
  12. package/dist/components/forms/fields/FormCheckbox.d.ts +7 -0
  13. package/dist/components/forms/fields/FormDropdown.d.ts +19 -0
  14. package/dist/components/forms/fields/FormInput.d.ts +17 -0
  15. package/dist/components/forms/fields/FormSelect.d.ts +12 -0
  16. package/dist/components/forms/fields/index.d.ts +5 -0
  17. package/dist/components/indicators/CheckIndicator.d.ts +1 -2
  18. package/dist/components/indicators/LoadingIndicator.d.ts +4 -4
  19. package/dist/components/login/LoginPage.d.ts +1 -1
  20. package/dist/components/tables/DataTable.d.ts +2 -2
  21. package/dist/components/tables/DragAndDropList.d.ts +2 -2
  22. package/dist/components/tables/SearchBox.d.ts +2 -2
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.js +2 -2
  25. package/dist/index.js.LICENSE.txt +0 -4
  26. package/dist/localization/LocalizationContext.d.ts +1 -1
  27. package/dist/utils/hooks.d.ts +1 -1
  28. package/dist/utils/timeAndDate.d.ts +5 -2
  29. package/dist/utils/utils.d.ts +3 -3
  30. package/package.json +10 -11
  31. package/src/__tests__/buttons.test.tsx +545 -0
  32. package/src/__tests__/errors.test.tsx +339 -0
  33. package/src/__tests__/forms.test.tsx +3021 -0
  34. package/src/__tests__/hooks.test.tsx +413 -0
  35. package/src/__tests__/indicators.test.tsx +284 -0
  36. package/src/__tests__/localization.test.tsx +462 -0
  37. package/src/__tests__/login.test.tsx +417 -0
  38. package/src/__tests__/setupTests.ts +328 -0
  39. package/src/__tests__/tables.test.tsx +609 -0
  40. package/src/__tests__/timeAndDate.test.tsx +308 -0
  41. package/src/__tests__/utils.test.tsx +422 -0
  42. package/src/components/forms/FormField.tsx +92 -0
  43. package/src/components/forms/FormFields.tsx +3 -423
  44. package/src/components/forms/FormModal.tsx +168 -243
  45. package/src/components/forms/FormModalProvider.tsx +164 -85
  46. package/src/components/forms/FormProvider.tsx +218 -0
  47. package/src/components/forms/fields/FormBadgesSelection.tsx +108 -0
  48. package/src/components/forms/fields/FormCheckbox.tsx +76 -0
  49. package/src/components/forms/fields/FormDropdown.tsx +123 -0
  50. package/src/components/forms/fields/FormInput.tsx +114 -0
  51. package/src/components/forms/fields/FormSelect.tsx +47 -0
  52. package/src/components/forms/fields/index.ts +6 -0
  53. package/src/index.ts +32 -29
  54. package/src/localization/LocalizationContext.tsx +156 -131
  55. package/src/localization/localization.ts +131 -131
  56. package/src/utils/hooks.ts +108 -94
  57. package/src/utils/timeAndDate.ts +33 -4
  58. package/src/utils/utils.ts +74 -66
  59. package/dist/components/forms/CreateEditModal.d.ts +0 -41
  60. package/dist/components/forms/CreateEditModalProvider.d.ts +0 -41
  61. package/dist/components/forms/FormFields.test.d.ts +0 -4
  62. package/dist/login/Login.d.ts +0 -70
  63. package/src/components/forms/FormFields.test.tsx +0 -107
@@ -0,0 +1,462 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import {
4
+ LocalizationProvider,
5
+ LocalizationContext,
6
+ useLocalization,
7
+ combineLocalization
8
+ } from '../localization/LocalizationContext';
9
+ import {
10
+ defaultLocalization,
11
+ defaultLanguages
12
+ } from '../localization/localization';
13
+
14
+ describe('Localization Tests', () => {
15
+ afterEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+
19
+ describe('defaultLocalization and defaultLanguages', () => {
20
+ it('should export default languages', () => {
21
+ expect(defaultLanguages).toBeDefined();
22
+ expect(defaultLanguages.en).toBe('English');
23
+ expect(defaultLanguages.fr).toBe('Français');
24
+ expect(defaultLanguages.nl).toBe('Nederlands');
25
+ });
26
+
27
+ it('should export default localization strings', () => {
28
+ expect(defaultLocalization).toBeDefined();
29
+ expect(defaultLocalization.en).toBeDefined();
30
+ expect(defaultLocalization.fr).toBeDefined();
31
+ expect(defaultLocalization.nl).toBeDefined();
32
+ });
33
+
34
+ it('should have consistent keys across all languages', () => {
35
+ const englishKeys = Object.keys(defaultLocalization.en);
36
+ const frenchKeys = Object.keys(defaultLocalization.fr);
37
+ const dutchKeys = Object.keys(defaultLocalization.nl);
38
+
39
+ expect(frenchKeys).toEqual(englishKeys);
40
+ expect(dutchKeys).toEqual(englishKeys);
41
+ });
42
+
43
+ it('should have all required localization keys', () => {
44
+ const requiredKeys = [
45
+ 'select',
46
+ 'search',
47
+ 'no_information_to_display',
48
+ 'information_is_being_loaded',
49
+ 'delete',
50
+ 'are_you_sure',
51
+ 'close',
52
+ 'save',
53
+ 'cancel',
54
+ 'ok',
55
+ 'login',
56
+ 'required_field',
57
+ 'choose_one',
58
+ 'everything',
59
+ 'number_of_rows',
60
+ ];
61
+
62
+ requiredKeys.forEach(key => {
63
+ expect(defaultLocalization.en[key]).toBeDefined();
64
+ expect(defaultLocalization.fr[key]).toBeDefined();
65
+ expect(defaultLocalization.nl[key]).toBeDefined();
66
+ });
67
+ });
68
+ });
69
+
70
+ describe('combineLocalization', () => {
71
+ it('should combine multiple localization objects', () => {
72
+ const additional1 = {
73
+ en: { custom_key_1: 'Custom 1' },
74
+ fr: { custom_key_1: 'Personnalisé 1' },
75
+ };
76
+
77
+ const additional2 = {
78
+ en: { custom_key_2: 'Custom 2' },
79
+ fr: { custom_key_2: 'Personnalisé 2' },
80
+ es: { custom_key_2: 'Personalizado 2' },
81
+ };
82
+
83
+ const result = combineLocalization(additional1, additional2);
84
+
85
+ expect(result).toEqual({
86
+ en: {
87
+ custom_key_1: 'Custom 1',
88
+ custom_key_2: 'Custom 2'
89
+ },
90
+ fr: {
91
+ custom_key_1: 'Personnalisé 1',
92
+ custom_key_2: 'Personnalisé 2'
93
+ },
94
+ es: {
95
+ custom_key_2: 'Personalizado 2'
96
+ }
97
+ });
98
+ });
99
+
100
+ it('should handle overlapping keys (last one wins)', () => {
101
+ const first = {
102
+ en: { shared_key: 'First Value' },
103
+ };
104
+
105
+ const second = {
106
+ en: { shared_key: 'Second Value' },
107
+ };
108
+
109
+ const result = combineLocalization(first, second);
110
+
111
+ expect(result.en.shared_key).toBe('Second Value');
112
+ });
113
+
114
+ it('should handle empty localization objects', () => {
115
+ const result = combineLocalization({}, {});
116
+ expect(result).toEqual({});
117
+ });
118
+
119
+ it('should extract unique language keys', () => {
120
+ const loc1 = { en: { key1: 'value1' } };
121
+ const loc2 = { fr: { key2: 'value2' } };
122
+ const loc3 = { es: { key3: 'value3' } };
123
+
124
+ const result = combineLocalization(loc1, loc2, loc3);
125
+
126
+ expect(Object.keys(result)).toEqual(['en', 'fr', 'es']);
127
+ });
128
+ });
129
+
130
+ describe('LocalizationProvider', () => {
131
+ it('should render with default props', () => {
132
+ expect(() => {
133
+ render(
134
+ <LocalizationProvider>
135
+ <div>Test Content</div>
136
+ </LocalizationProvider>
137
+ );
138
+ }).not.toThrow();
139
+ });
140
+
141
+ it('should render with custom language', () => {
142
+ expect(() => {
143
+ render(
144
+ <LocalizationProvider lang="fr">
145
+ <div>Test Content</div>
146
+ </LocalizationProvider>
147
+ );
148
+ }).not.toThrow();
149
+ });
150
+
151
+ it('should render with additional localization', () => {
152
+ const additionalLoc = {
153
+ en: { custom_key: 'Custom English' },
154
+ fr: { custom_key: 'Français personnalisé' },
155
+ };
156
+
157
+ expect(() => {
158
+ render(
159
+ <LocalizationProvider
160
+ lang="en"
161
+ localization={additionalLoc}
162
+ >
163
+ <div>Test Content</div>
164
+ </LocalizationProvider>
165
+ );
166
+ }).not.toThrow();
167
+ });
168
+
169
+ it('should render with custom languages', () => {
170
+ const customLanguages = ['en', 'es', 'de'];
171
+
172
+ expect(() => {
173
+ render(
174
+ <LocalizationProvider
175
+ languages={customLanguages}
176
+ >
177
+ <div>Test Content</div>
178
+ </LocalizationProvider>
179
+ );
180
+ }).not.toThrow();
181
+ });
182
+
183
+ it('should render with all props', () => {
184
+ const additionalLoc = {
185
+ en: { custom: 'Custom' },
186
+ es: { custom: 'Personalizado' },
187
+ };
188
+
189
+ expect(() => {
190
+ render(
191
+ <LocalizationProvider
192
+ lang="es"
193
+ localization={additionalLoc}
194
+ languages={['en', 'es']}
195
+ customProp="test"
196
+ >
197
+ <div>Test Content</div>
198
+ </LocalizationProvider>
199
+ );
200
+ }).not.toThrow();
201
+ });
202
+ });
203
+
204
+ describe('useLocalization Hook', () => {
205
+ it('should provide localization context', () => {
206
+ const TestComponent = () => {
207
+ const { lang, strings, text, setLanguage } = useLocalization();
208
+
209
+ return (
210
+ <div>
211
+ <span data-testid="current-lang">{lang}</span>
212
+ <span data-testid="close-string">{strings.getString('close')}</span>
213
+ <button onClick={() => setLanguage('fr')} data-testid="change-lang">
214
+ Change Language
215
+ </button>
216
+ </div>
217
+ );
218
+ };
219
+
220
+ const { getByTestId } = render(
221
+ <LocalizationProvider lang="en">
222
+ <TestComponent />
223
+ </LocalizationProvider>
224
+ );
225
+
226
+ expect(getByTestId('current-lang')).toHaveTextContent('en');
227
+ expect(getByTestId('close-string')).toHaveTextContent('Close');
228
+ });
229
+
230
+ it('should handle language changes', () => {
231
+ const TestComponent = () => {
232
+ const { lang, setLanguage, languages } = useLocalization();
233
+
234
+ return (
235
+ <div>
236
+ <span data-testid="current-lang">{lang}</span>
237
+ <span data-testid="available-langs">{languages.join(',')}</span>
238
+ <button onClick={() => setLanguage('fr')} data-testid="set-french">
239
+ Set French
240
+ </button>
241
+ <button onClick={() => setLanguage('invalid')} data-testid="set-invalid">
242
+ Set Invalid
243
+ </button>
244
+ </div>
245
+ );
246
+ };
247
+
248
+ const { getByTestId } = render(
249
+ <LocalizationProvider lang="en">
250
+ <TestComponent />
251
+ </LocalizationProvider>
252
+ );
253
+
254
+ expect(getByTestId('current-lang')).toHaveTextContent('en');
255
+
256
+ fireEvent.click(getByTestId('set-french'));
257
+ expect(getByTestId('current-lang')).toHaveTextContent('fr');
258
+
259
+ // Should not change to invalid language
260
+ fireEvent.click(getByTestId('set-invalid'));
261
+ expect(getByTestId('current-lang')).toHaveTextContent('fr');
262
+ });
263
+
264
+ it('should provide text function for template literals', () => {
265
+ const TestComponent = () => {
266
+ const { text } = useLocalization();
267
+
268
+ return (
269
+ <div>
270
+ <span data-testid="text-result">
271
+ {text`close`}
272
+ </span>
273
+ </div>
274
+ );
275
+ };
276
+
277
+ const { getByTestId } = render(
278
+ <LocalizationProvider>
279
+ <TestComponent />
280
+ </LocalizationProvider>
281
+ );
282
+
283
+ expect(getByTestId('text-result')).toHaveTextContent('Close');
284
+ });
285
+
286
+ it('should provide textByLang function', () => {
287
+ const TestComponent = () => {
288
+ const { textByLang } = useLocalization();
289
+
290
+ return (
291
+ <div>
292
+ <span data-testid="english-text">
293
+ {textByLang('en')`close`}
294
+ </span>
295
+ <span data-testid="french-text">
296
+ {textByLang('fr')`close`}
297
+ </span>
298
+ </div>
299
+ );
300
+ };
301
+
302
+ const { getByTestId } = render(
303
+ <LocalizationProvider>
304
+ <TestComponent />
305
+ </LocalizationProvider>
306
+ );
307
+
308
+ expect(getByTestId('english-text')).toHaveTextContent('Close');
309
+ expect(getByTestId('french-text')).toHaveTextContent('Fermer');
310
+ });
311
+
312
+ it('should handle setLocalization function', () => {
313
+ const TestComponent = () => {
314
+ const { setLocalization, strings } = useLocalization();
315
+
316
+ const addCustomLocalization = () => {
317
+ setLocalization({
318
+ en: { custom_key: 'Custom Value' }
319
+ });
320
+ };
321
+
322
+ return (
323
+ <div>
324
+ <button onClick={addCustomLocalization} data-testid="add-custom">
325
+ Add Custom
326
+ </button>
327
+ <span data-testid="custom-value">
328
+ {strings.getString('custom_key') || 'Not Found'}
329
+ </span>
330
+ </div>
331
+ );
332
+ };
333
+
334
+ const { getByTestId } = render(
335
+ <LocalizationProvider>
336
+ <TestComponent />
337
+ </LocalizationProvider>
338
+ );
339
+
340
+ expect(getByTestId('custom-value')).toHaveTextContent('custom_key');
341
+
342
+ fireEvent.click(getByTestId('add-custom'));
343
+ expect(getByTestId('custom-value')).toHaveTextContent('Custom Value');
344
+ });
345
+ });
346
+
347
+ describe('LocalizationContext Error Handling', () => {
348
+ it('should handle context usage outside provider', () => {
349
+ const TestComponent = () => {
350
+ const { setLanguage, text, textByLang, setLocalization } = useLocalization();
351
+
352
+ return (
353
+ <div>
354
+ <button onClick={() => setLanguage('en')} data-testid="set-lang">
355
+ Set Language
356
+ </button>
357
+ <button onClick={() => text`test`} data-testid="text">
358
+ Text
359
+ </button>
360
+ <button onClick={() => textByLang('en')`test`} data-testid="text-by-lang">
361
+ Text By Lang
362
+ </button>
363
+ <button onClick={() => setLocalization({})} data-testid="set-localization">
364
+ Set Localization
365
+ </button>
366
+ </div>
367
+ );
368
+ };
369
+
370
+ // Mock console.error to capture error messages
371
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
372
+
373
+ const { getByTestId } = render(<TestComponent />);
374
+
375
+ fireEvent.click(getByTestId('set-lang'));
376
+ fireEvent.click(getByTestId('text'));
377
+ fireEvent.click(getByTestId('text-by-lang'));
378
+ fireEvent.click(getByTestId('set-localization'));
379
+
380
+ expect(consoleSpy).toHaveBeenCalledWith(
381
+ 'This function should only be used in a child of LocalizationProvider.'
382
+ );
383
+
384
+ consoleSpy.mockRestore();
385
+ });
386
+ });
387
+
388
+ describe('Advanced Localization Features', () => {
389
+ it('should handle function-based localization strings', () => {
390
+ const customLocalization = {
391
+ en: {
392
+ greeting: (name: string) => `Hello, ${name}!`,
393
+ count: (num: number) => `You have ${num} item${num !== 1 ? 's' : ''}`,
394
+ }
395
+ };
396
+
397
+ const TestComponent = () => {
398
+ const { textByLang } = useLocalization();
399
+
400
+ return (
401
+ <div>
402
+ <span data-testid="greeting">
403
+ {textByLang('en')`greeting`}
404
+ </span>
405
+ <span data-testid="count">
406
+ {textByLang('en')`count`}
407
+ </span>
408
+ </div>
409
+ );
410
+ };
411
+
412
+ const { getByTestId } = render(
413
+ <LocalizationProvider localization={customLocalization}>
414
+ <TestComponent />
415
+ </LocalizationProvider>
416
+ );
417
+
418
+ // Since we're testing the template literal syntax, these should return the key names
419
+ expect(getByTestId('greeting')).toHaveTextContent('greeting');
420
+ expect(getByTestId('count')).toHaveTextContent('count');
421
+ });
422
+
423
+ it('should handle missing localization keys', () => {
424
+ const TestComponent = () => {
425
+ const { strings, textByLang } = useLocalization();
426
+
427
+ return (
428
+ <div>
429
+ <span data-testid="missing-string">
430
+ {strings.getString('non_existent_key')}
431
+ </span>
432
+ <span data-testid="missing-text">
433
+ {textByLang('en')`non_existent_key`}
434
+ </span>
435
+ </div>
436
+ );
437
+ };
438
+
439
+ const { getByTestId } = render(
440
+ <LocalizationProvider>
441
+ <TestComponent />
442
+ </LocalizationProvider>
443
+ );
444
+
445
+ expect(getByTestId('missing-text')).toHaveTextContent('non_existent_key');
446
+ });
447
+ });
448
+
449
+ describe('Component Export Verification', () => {
450
+ it('should export all localization components and functions', () => {
451
+ expect(typeof LocalizationProvider).toBe('function');
452
+ expect(typeof LocalizationContext).toBe('object');
453
+ expect(typeof useLocalization).toBe('function');
454
+ expect(typeof combineLocalization).toBe('function');
455
+ });
456
+
457
+ it('should export localization data', () => {
458
+ expect(typeof defaultLocalization).toBe('object');
459
+ expect(typeof defaultLanguages).toBe('object');
460
+ });
461
+ });
462
+ });