@abstraks-dev/ui-library 1.1.26 → 1.1.29

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 (109) hide show
  1. package/dist/__tests__/AccountBox.test.js +61 -0
  2. package/dist/__tests__/AccountCircle.test.js +69 -0
  3. package/dist/__tests__/Alert.test.js +144 -0
  4. package/dist/__tests__/ArrowIcon.test.js +61 -0
  5. package/dist/__tests__/ArrowRight.test.js +12 -7
  6. package/dist/__tests__/BookOpen.test.js +61 -0
  7. package/dist/__tests__/Camera.test.js +69 -0
  8. package/dist/__tests__/CaretDown.test.js +69 -0
  9. package/dist/__tests__/ChevronDown.test.js +13 -8
  10. package/dist/__tests__/Comment.test.js +69 -0
  11. package/dist/__tests__/Ellipses.test.js +61 -0
  12. package/dist/__tests__/Explore.test.js +61 -0
  13. package/dist/__tests__/FileInput.test.js +148 -0
  14. package/dist/__tests__/Filter.test.js +69 -0
  15. package/dist/__tests__/Form.test.js +471 -0
  16. package/dist/__tests__/Group.test.js +61 -0
  17. package/dist/__tests__/GroupReview.test.js +61 -0
  18. package/dist/__tests__/Hamburger.test.js +69 -0
  19. package/dist/__tests__/Header.test.js +211 -0
  20. package/dist/__tests__/Heart.test.js +69 -0
  21. package/dist/__tests__/Home.test.js +69 -0
  22. package/dist/__tests__/LoadingSpinner.test.js +78 -0
  23. package/dist/__tests__/LogOut.test.js +69 -0
  24. package/dist/__tests__/Magnify.test.js +69 -0
  25. package/dist/__tests__/News.test.js +61 -0
  26. package/dist/__tests__/Review.test.js +61 -0
  27. package/dist/__tests__/SaveIcon.test.js +61 -0
  28. package/dist/__tests__/Search.test.js +101 -0
  29. package/dist/__tests__/utils/accessibility.test.js +361 -0
  30. package/dist/__tests__/utils/inputValidation-core.test.js +80 -0
  31. package/dist/__tests__/utils/validation-core.test.js +123 -0
  32. package/dist/__tests__/utils/validation.test.js +362 -0
  33. package/dist/components/Alert.js +104 -0
  34. package/dist/components/FileInput.js +96 -0
  35. package/dist/components/Form.js +27 -3
  36. package/dist/components/Header.js +1 -1
  37. package/dist/components/Search.js +238 -0
  38. package/dist/icons/AccountBox.js +33 -0
  39. package/dist/icons/AccountCircle.js +33 -0
  40. package/dist/icons/ArrowIcon.js +3 -2
  41. package/dist/icons/ArrowRight.js +23 -12
  42. package/dist/icons/BookOpen.js +33 -0
  43. package/dist/icons/Camera.js +33 -0
  44. package/dist/icons/CaretDown.js +33 -0
  45. package/dist/icons/ChevronDown.js +19 -9
  46. package/dist/icons/Comment.js +33 -0
  47. package/dist/icons/Explore.js +33 -0
  48. package/dist/icons/Filter.js +33 -0
  49. package/dist/icons/Group.js +33 -0
  50. package/dist/icons/GroupReview.js +33 -0
  51. package/dist/icons/Hamburger.js +14 -20
  52. package/dist/icons/Heart.js +33 -0
  53. package/dist/icons/Home.js +33 -0
  54. package/dist/icons/LoadingSpinner.js +3 -2
  55. package/dist/icons/LogOut.js +33 -0
  56. package/dist/icons/Magnify.js +33 -0
  57. package/dist/icons/News.js +33 -0
  58. package/dist/icons/Review.js +35 -0
  59. package/dist/icons/SaveIcon.js +3 -2
  60. package/dist/icons/index.js +112 -0
  61. package/dist/index.js +41 -1
  62. package/dist/styles/_variables.scss +5 -0
  63. package/dist/styles/alert.css +218 -0
  64. package/dist/styles/alert.css.map +1 -0
  65. package/dist/styles/alert.scss +128 -0
  66. package/dist/styles/anchor.css.map +1 -1
  67. package/dist/styles/avatar.css.map +1 -1
  68. package/dist/styles/button.css.map +1 -1
  69. package/dist/styles/card.css.map +1 -1
  70. package/dist/styles/checkbox.css.map +1 -1
  71. package/dist/styles/crud.css.map +1 -1
  72. package/dist/styles/dragAndDrop.css.map +1 -1
  73. package/dist/styles/error.css.map +1 -1
  74. package/dist/styles/file-input.css +165 -0
  75. package/dist/styles/file-input.css.map +1 -0
  76. package/dist/styles/file-input.scss +69 -0
  77. package/dist/styles/footer.css.map +1 -1
  78. package/dist/styles/form.css.map +1 -1
  79. package/dist/styles/header.css.map +1 -1
  80. package/dist/styles/heading.css.map +1 -1
  81. package/dist/styles/hero.css.map +1 -1
  82. package/dist/styles/htmlElements.css.map +1 -1
  83. package/dist/styles/label.css.map +1 -1
  84. package/dist/styles/loader.css.map +1 -1
  85. package/dist/styles/main.css +344 -11
  86. package/dist/styles/main.css.map +1 -1
  87. package/dist/styles/menu-hover.css.map +1 -1
  88. package/dist/styles/paragraph.css.map +1 -1
  89. package/dist/styles/prompt.css.map +1 -1
  90. package/dist/styles/radio.css.map +1 -1
  91. package/dist/styles/search.css +302 -0
  92. package/dist/styles/search.css.map +1 -0
  93. package/dist/styles/search.scss +260 -0
  94. package/dist/styles/select.css.map +1 -1
  95. package/dist/styles/side-menu.css.map +1 -1
  96. package/dist/styles/tabs.css.map +1 -1
  97. package/dist/styles/text-area.css.map +1 -1
  98. package/dist/styles/text-input.css +0 -11
  99. package/dist/styles/text-input.css.map +1 -1
  100. package/dist/styles/text-input.scss +0 -14
  101. package/dist/utils/utils/validation.js +2 -2
  102. package/dist/utils/validation.js +2 -2
  103. package/package.json +1 -1
  104. package/dist/icons/__tests__/CheckCircle.test.js +0 -9
  105. package/dist/icons/__tests__/ChevronDown.test.js +0 -9
  106. package/dist/icons/__tests__/Close.test.js +0 -9
  107. package/dist/icons/__tests__/EditSquare.test.js +0 -9
  108. package/dist/icons/__tests__/PlusCircle.test.js +0 -9
  109. package/dist/icons/__tests__/TrashX.test.js +0 -9
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+
3
+ var _validation = require("../../utils/validation.js");
4
+ /**
5
+ * @jest-environment jsdom
6
+ */
7
+
8
+ describe('Validation Utilities - Core Functions', () => {
9
+ describe('validateEmail', () => {
10
+ it('validates basic email addresses', () => {
11
+ expect((0, _validation.validateEmail)('user@example.com')).toBe(true);
12
+ expect((0, _validation.validateEmail)('test@domain.org')).toBe(true);
13
+ });
14
+ it('rejects invalid email addresses', () => {
15
+ expect((0, _validation.validateEmail)('invalid-email')).toBe(false);
16
+ expect((0, _validation.validateEmail)('@example.com')).toBe(false);
17
+ expect((0, _validation.validateEmail)('')).toBe(false);
18
+ expect((0, _validation.validateEmail)(null)).toBe(false);
19
+ });
20
+ });
21
+ describe('validatePhone', () => {
22
+ it('validates basic phone numbers', () => {
23
+ expect((0, _validation.validatePhone)('1234567890')).toBe(true);
24
+ expect((0, _validation.validatePhone)('+1234567890')).toBe(true);
25
+ });
26
+ it('rejects invalid phone numbers', () => {
27
+ expect((0, _validation.validatePhone)('abc')).toBe(false);
28
+ expect((0, _validation.validatePhone)('')).toBe(false);
29
+ expect((0, _validation.validatePhone)(null)).toBe(false);
30
+ expect((0, _validation.validatePhone)('0123456789')).toBe(false); // starts with 0
31
+ });
32
+ });
33
+ describe('validateUrl', () => {
34
+ it('validates basic URLs', () => {
35
+ expect((0, _validation.validateUrl)('https://example.com')).toBe(true);
36
+ expect((0, _validation.validateUrl)('http://example.com')).toBe(true);
37
+ });
38
+ it('rejects invalid URLs', () => {
39
+ expect((0, _validation.validateUrl)('example.com')).toBe(false);
40
+ expect((0, _validation.validateUrl)('invalid-url')).toBe(false);
41
+ expect((0, _validation.validateUrl)('')).toBe(false);
42
+ expect((0, _validation.validateUrl)(null)).toBe(false);
43
+ });
44
+ });
45
+ describe('validatePasswordStrength', () => {
46
+ it('validates strong passwords', () => {
47
+ // Test with known good pattern
48
+ expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
49
+ });
50
+ it('rejects weak passwords', () => {
51
+ expect((0, _validation.validatePasswordStrength)('password')).toBe(false);
52
+ expect((0, _validation.validatePasswordStrength)('123456')).toBe(false);
53
+ expect((0, _validation.validatePasswordStrength)('')).toBe(false);
54
+ expect((0, _validation.validatePasswordStrength)(null)).toBe(false);
55
+ });
56
+ });
57
+ describe('validateField', () => {
58
+ it('validates required fields', () => {
59
+ const rules = {
60
+ required: true
61
+ };
62
+ expect((0, _validation.validateField)('test', rules, 'username')).toBeNull();
63
+ expect((0, _validation.validateField)('', rules, 'username')).toBe('username is required');
64
+ });
65
+ it('validates email fields', () => {
66
+ const rules = {
67
+ email: true
68
+ };
69
+ expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
70
+ expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
71
+ });
72
+ it('validates minimum length', () => {
73
+ const rules = {
74
+ minLength: 5
75
+ };
76
+ expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
77
+ expect((0, _validation.validateField)('hi', rules, 'username')).toBe('username must be at least 5 characters');
78
+ });
79
+ it('validates maximum length', () => {
80
+ const rules = {
81
+ maxLength: 10
82
+ };
83
+ expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
84
+ expect((0, _validation.validateField)('this is too long', rules, 'username')).toBe('username must not exceed 10 characters');
85
+ });
86
+ it('handles multiple validation rules', () => {
87
+ const rules = {
88
+ required: true,
89
+ minLength: 3
90
+ };
91
+ expect((0, _validation.validateField)('', rules, 'field')).toBe('field is required');
92
+ expect((0, _validation.validateField)('ab', rules, 'field')).toBe('field must be at least 3 characters');
93
+ expect((0, _validation.validateField)('abc', rules, 'field')).toBeNull();
94
+ });
95
+ });
96
+ describe('VALIDATION_PATTERNS', () => {
97
+ it('contains expected patterns', () => {
98
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('email');
99
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('phone');
100
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('url');
101
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('strongPassword');
102
+ });
103
+ it('patterns are RegExp objects', () => {
104
+ Object.values(_validation.VALIDATION_PATTERNS).forEach(pattern => {
105
+ expect(pattern).toBeInstanceOf(RegExp);
106
+ });
107
+ });
108
+ });
109
+ describe('Error Handling', () => {
110
+ it('handles null and undefined inputs', () => {
111
+ expect((0, _validation.validateEmail)(null)).toBe(false);
112
+ expect((0, _validation.validatePhone)(undefined)).toBe(false);
113
+ expect((0, _validation.validateUrl)(null)).toBe(false);
114
+ expect((0, _validation.validatePasswordStrength)(undefined)).toBe(false);
115
+ });
116
+ it('handles non-string inputs', () => {
117
+ expect((0, _validation.validateEmail)(123)).toBe(false);
118
+ expect((0, _validation.validatePhone)({})).toBe(false);
119
+ expect((0, _validation.validateUrl)([])).toBe(false);
120
+ expect((0, _validation.validatePasswordStrength)(123)).toBe(false);
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+
3
+ var _validation = require("../../utils/validation.js");
4
+ /**
5
+ * @jest-environment jsdom
6
+ */
7
+
8
+ describe('Validation Utilities', () => {
9
+ describe('Email Validation', () => {
10
+ describe('validateEmail', () => {
11
+ it('validates basic email addresses', () => {
12
+ const validEmails = ['user@example.com', 'test.email@domain.co.uk', 'user+tag@subdomain.example.org', 'user_name@example-domain.com', '123@numbers.com'];
13
+ validEmails.forEach(email => {
14
+ expect((0, _validation.validateEmail)(email)).toBe(true);
15
+ });
16
+ });
17
+ it('rejects invalid email addresses', () => {
18
+ const invalidEmails = ['invalid-email', '@example.com', 'user@', 'user@.com', 'user@com', 'user@example.', 'user.example.com', 'user@ex ample.com', '', null, undefined, 123];
19
+ invalidEmails.forEach(email => {
20
+ expect((0, _validation.validateEmail)(email)).toBe(false);
21
+ });
22
+ });
23
+ it('handles strict mode validation', () => {
24
+ // Basic mode should pass these
25
+ const basicValidEmails = ['user@example.com', 'test@domain.org'];
26
+ basicValidEmails.forEach(email => {
27
+ expect((0, _validation.validateEmail)(email, false)).toBe(true);
28
+ expect((0, _validation.validateEmail)(email, true)).toBe(true);
29
+ });
30
+
31
+ // Edge cases that might differ between strict and basic
32
+ const edgeCaseEmails = ['user@sub.domain.com', 'test.email@example.co.uk'];
33
+ edgeCaseEmails.forEach(email => {
34
+ expect((0, _validation.validateEmail)(email, false)).toBe(true);
35
+ expect((0, _validation.validateEmail)(email, true)).toBe(true);
36
+ });
37
+ });
38
+ it('trims whitespace before validation', () => {
39
+ expect((0, _validation.validateEmail)(' user@example.com ')).toBe(true);
40
+ expect((0, _validation.validateEmail)('\tuser@example.com\n')).toBe(true);
41
+ });
42
+ it('handles non-string inputs', () => {
43
+ expect((0, _validation.validateEmail)(null)).toBe(false);
44
+ expect((0, _validation.validateEmail)(undefined)).toBe(false);
45
+ expect((0, _validation.validateEmail)(123)).toBe(false);
46
+ expect((0, _validation.validateEmail)({})).toBe(false);
47
+ expect((0, _validation.validateEmail)([])).toBe(false);
48
+ });
49
+ });
50
+ describe('EMAIL_REGEX patterns', () => {
51
+ it('EMAIL_REGEX matches basic email format', () => {
52
+ expect(_validation.EMAIL_REGEX.test('user@example.com')).toBe(true);
53
+ expect(_validation.EMAIL_REGEX.test('invalid-email')).toBe(false);
54
+ });
55
+ it('EMAIL_REGEX_STRICT is more comprehensive', () => {
56
+ expect(_validation.EMAIL_REGEX_STRICT.test('user@example.com')).toBe(true);
57
+ expect(_validation.EMAIL_REGEX_STRICT.test('invalid-email')).toBe(false);
58
+ });
59
+ });
60
+ });
61
+ describe('Phone Validation', () => {
62
+ describe('validatePhone', () => {
63
+ it('validates basic phone numbers', () => {
64
+ const validPhones = ['+1234567890', '1234567890', '+12345678901234', '9876543210'];
65
+ validPhones.forEach(phone => {
66
+ expect((0, _validation.validatePhone)(phone)).toBe(true);
67
+ });
68
+ });
69
+ it('rejects invalid phone numbers', () => {
70
+ const invalidPhones = ['123',
71
+ // too short
72
+ 'abc123', '++123456', '+',
73
+ // just plus
74
+ '0123456789',
75
+ // starts with 0
76
+ null, undefined, 123];
77
+ invalidPhones.forEach(phone => {
78
+ expect((0, _validation.validatePhone)(phone)).toBe(false);
79
+ });
80
+ });
81
+ it('strips formatting characters before validation', () => {
82
+ const formattedPhones = ['(123) 456-7890', '123-456-7890', '123 456 7890'];
83
+ formattedPhones.forEach(phone => {
84
+ // These will be stripped to '1234567890' which is valid
85
+ expect((0, _validation.validatePhone)(phone)).toBe(true);
86
+ });
87
+ });
88
+ it('handles non-string inputs', () => {
89
+ expect((0, _validation.validatePhone)(null)).toBe(false);
90
+ expect((0, _validation.validatePhone)(undefined)).toBe(false);
91
+ expect((0, _validation.validatePhone)(123456789)).toBe(false);
92
+ expect((0, _validation.validatePhone)({})).toBe(false);
93
+ });
94
+ });
95
+ });
96
+ describe('URL Validation', () => {
97
+ describe('validateUrl', () => {
98
+ it('validates valid URLs', () => {
99
+ const validUrls = ['https://example.com', 'http://example.com', 'https://www.example.com', 'http://subdomain.example.org', 'https://example.com/path', 'https://example.com/path?query=value', 'https://example.com/path#anchor', 'https://example.com:8080'];
100
+ validUrls.forEach(url => {
101
+ expect((0, _validation.validateUrl)(url)).toBe(true);
102
+ });
103
+ });
104
+ it('rejects invalid URLs', () => {
105
+ const invalidUrls = ['example.com', 'ftp://example.com', 'invalid-url', 'https://', 'http://', '', null, undefined, 123, 'javascript:alert(1)'];
106
+ invalidUrls.forEach(url => {
107
+ expect((0, _validation.validateUrl)(url)).toBe(false);
108
+ });
109
+ });
110
+ it('trims whitespace before validation', () => {
111
+ expect((0, _validation.validateUrl)(' https://example.com ')).toBe(true);
112
+ expect((0, _validation.validateUrl)('\thttps://example.com\n')).toBe(true);
113
+ });
114
+ it('handles non-string inputs', () => {
115
+ expect((0, _validation.validateUrl)(null)).toBe(false);
116
+ expect((0, _validation.validateUrl)(undefined)).toBe(false);
117
+ expect((0, _validation.validateUrl)(123)).toBe(false);
118
+ expect((0, _validation.validateUrl)({})).toBe(false);
119
+ });
120
+ });
121
+ });
122
+ describe('Password Strength Validation', () => {
123
+ describe('validatePasswordStrength', () => {
124
+ it('validates strong passwords', () => {
125
+ const strongPasswords = ['Password123!', 'MyStr0ng@Pass', 'C0mplex#Password', 'Secure123$', 'Strong&Pass1'];
126
+ strongPasswords.forEach(password => {
127
+ expect((0, _validation.validatePasswordStrength)(password)).toBe(true);
128
+ });
129
+ });
130
+ it('rejects weak passwords', () => {
131
+ const weakPasswords = ['password',
132
+ // no uppercase, number, special char
133
+ 'PASSWORD',
134
+ // no lowercase, number, special char
135
+ '12345678',
136
+ // no letters, special char
137
+ 'Password',
138
+ // no number, special char
139
+ 'Password123',
140
+ // no special char
141
+ 'Pass1!',
142
+ // too short (6 chars)
143
+ '',
144
+ // empty
145
+ 'Pass!' // too short, missing requirements
146
+ ];
147
+ weakPasswords.forEach(password => {
148
+ expect((0, _validation.validatePasswordStrength)(password)).toBe(false);
149
+ });
150
+ });
151
+ it('requires minimum 8 characters', () => {
152
+ expect((0, _validation.validatePasswordStrength)('Pass1!')).toBe(false); // 6 chars
153
+ expect((0, _validation.validatePasswordStrength)('Pass12!')).toBe(false); // 7 chars
154
+ expect((0, _validation.validatePasswordStrength)('Pass123!')).toBe(true); // 8 chars
155
+ });
156
+ it('requires uppercase letter', () => {
157
+ expect((0, _validation.validatePasswordStrength)('password123!')).toBe(false);
158
+ expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
159
+ });
160
+ it('requires lowercase letter', () => {
161
+ expect((0, _validation.validatePasswordStrength)('PASSWORD123!')).toBe(false);
162
+ expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
163
+ });
164
+ it('requires number', () => {
165
+ expect((0, _validation.validatePasswordStrength)('Password!')).toBe(false);
166
+ expect((0, _validation.validatePasswordStrength)('Password1!')).toBe(true);
167
+ });
168
+ it('requires special character', () => {
169
+ expect((0, _validation.validatePasswordStrength)('Password123')).toBe(false);
170
+ expect((0, _validation.validatePasswordStrength)('Password123!')).toBe(true);
171
+ });
172
+ it('handles non-string inputs', () => {
173
+ expect((0, _validation.validatePasswordStrength)(null)).toBe(false);
174
+ expect((0, _validation.validatePasswordStrength)(undefined)).toBe(false);
175
+ expect((0, _validation.validatePasswordStrength)(123)).toBe(false);
176
+ expect((0, _validation.validatePasswordStrength)({})).toBe(false);
177
+ });
178
+ });
179
+ });
180
+ describe('Generic Field Validation', () => {
181
+ describe('validateField', () => {
182
+ it('validates required fields', () => {
183
+ const rules = {
184
+ required: true
185
+ };
186
+ expect((0, _validation.validateField)('test', rules, 'username')).toBeNull();
187
+ expect((0, _validation.validateField)('', rules, 'username')).toBe('username is required');
188
+ expect((0, _validation.validateField)(' ', rules, 'username')).toBe('username is required');
189
+ expect((0, _validation.validateField)(null, rules, 'username')).toBe('username is required');
190
+ expect((0, _validation.validateField)(undefined, rules, 'username')).toBe('username is required');
191
+ });
192
+ it('skips validation for empty non-required fields', () => {
193
+ const rules = {
194
+ email: true,
195
+ minLength: 5
196
+ };
197
+ expect((0, _validation.validateField)('', rules, 'email')).toBeNull();
198
+ expect((0, _validation.validateField)(' ', rules, 'email')).toBeNull();
199
+ });
200
+ it('validates email fields', () => {
201
+ const rules = {
202
+ email: true
203
+ };
204
+ expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
205
+ expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
206
+ });
207
+ it('validates email fields with strict mode', () => {
208
+ const rules = {
209
+ email: true,
210
+ strict: true
211
+ };
212
+ expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
213
+ expect((0, _validation.validateField)('invalid-email', rules, 'email')).toBe('Please enter a valid email address');
214
+ });
215
+ it('validates phone fields', () => {
216
+ const rules = {
217
+ phone: true
218
+ };
219
+ expect((0, _validation.validateField)('1234567890', rules, 'phone')).toBeNull();
220
+ expect((0, _validation.validateField)('invalid-phone', rules, 'phone')).toBe('Please enter a valid phone number');
221
+ });
222
+ it('validates URL fields', () => {
223
+ const rules = {
224
+ url: true
225
+ };
226
+ expect((0, _validation.validateField)('https://example.com', rules, 'website')).toBeNull();
227
+ expect((0, _validation.validateField)('invalid-url', rules, 'website')).toBe('Please enter a valid URL');
228
+ });
229
+ it('validates password strength', () => {
230
+ const rules = {
231
+ strongPassword: true
232
+ };
233
+ expect((0, _validation.validateField)('Password123!', rules, 'password')).toBeNull();
234
+ expect((0, _validation.validateField)('weak', rules, 'password')).toBe('Password must contain at least 8 characters with uppercase, lowercase, number, and special character');
235
+ });
236
+ it('validates minimum length', () => {
237
+ const rules = {
238
+ minLength: 5
239
+ };
240
+ expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
241
+ expect((0, _validation.validateField)('hi', rules, 'username')).toBe('username must be at least 5 characters');
242
+ });
243
+ it('validates maximum length', () => {
244
+ const rules = {
245
+ maxLength: 10
246
+ };
247
+ expect((0, _validation.validateField)('hello', rules, 'username')).toBeNull();
248
+ expect((0, _validation.validateField)('this is too long', rules, 'username')).toBe('username must not exceed 10 characters');
249
+ });
250
+ it('validates pattern matching', () => {
251
+ const rules = {
252
+ pattern: /^[a-zA-Z]+$/
253
+ };
254
+ expect((0, _validation.validateField)('hello', rules, 'name')).toBeNull();
255
+ expect((0, _validation.validateField)('hello123', rules, 'name')).toBe('name format is invalid');
256
+ });
257
+ it('validates pattern with custom message', () => {
258
+ const rules = {
259
+ pattern: /^[a-zA-Z]+$/,
260
+ patternMessage: 'Only letters allowed'
261
+ };
262
+ expect((0, _validation.validateField)('hello123', rules, 'name')).toBe('Only letters allowed');
263
+ });
264
+ it('validates multiple rules in correct order', () => {
265
+ const rules = {
266
+ required: true,
267
+ email: true,
268
+ minLength: 10
269
+ };
270
+
271
+ // Required takes precedence
272
+ expect((0, _validation.validateField)('', rules, 'email')).toBe('email is required');
273
+
274
+ // Email validation before length
275
+ expect((0, _validation.validateField)('invalid', rules, 'email')).toBe('Please enter a valid email address');
276
+
277
+ // Length validation after email
278
+ expect((0, _validation.validateField)('a@b.co', rules, 'email')).toBe('email must be at least 10 characters');
279
+
280
+ // Valid email with sufficient length
281
+ expect((0, _validation.validateField)('user@example.com', rules, 'email')).toBeNull();
282
+ });
283
+ it('returns null for no rules', () => {
284
+ expect((0, _validation.validateField)('anything', null, 'field')).toBeNull();
285
+ expect((0, _validation.validateField)('anything', undefined, 'field')).toBeNull();
286
+ expect((0, _validation.validateField)('anything', {}, 'field')).toBeNull();
287
+ });
288
+ it('combines required with other validations correctly', () => {
289
+ const rules = {
290
+ required: true,
291
+ minLength: 3
292
+ };
293
+ expect((0, _validation.validateField)('', rules, 'field')).toBe('field is required');
294
+ expect((0, _validation.validateField)('ab', rules, 'field')).toBe('field must be at least 3 characters');
295
+ expect((0, _validation.validateField)('abc', rules, 'field')).toBeNull();
296
+ });
297
+ });
298
+ });
299
+ describe('VALIDATION_PATTERNS', () => {
300
+ it('contains expected patterns', () => {
301
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('email');
302
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('phone');
303
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('url');
304
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('postalCode');
305
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('creditCard');
306
+ expect(_validation.VALIDATION_PATTERNS).toHaveProperty('strongPassword');
307
+ });
308
+ it('patterns are RegExp objects', () => {
309
+ Object.values(_validation.VALIDATION_PATTERNS).forEach(pattern => {
310
+ expect(pattern).toBeInstanceOf(RegExp);
311
+ });
312
+ });
313
+ it('postal code pattern works correctly', () => {
314
+ const validPostalCodes = ['12345', 'AB1 2CD', 'M5V 3L9', '90210-1234'];
315
+ const invalidPostalCodes = ['1', '12', '1234567890123', ''];
316
+ validPostalCodes.forEach(code => {
317
+ expect(_validation.VALIDATION_PATTERNS.postalCode.test(code)).toBe(true);
318
+ });
319
+ invalidPostalCodes.forEach(code => {
320
+ expect(_validation.VALIDATION_PATTERNS.postalCode.test(code)).toBe(false);
321
+ });
322
+ });
323
+ it('credit card pattern works correctly', () => {
324
+ const validCards = ['1234567890123456', '4111111111111111', '5555555555554444'];
325
+ const invalidCards = ['123456789012', '12345678901234567890', 'abcd1234567890123', ''];
326
+ validCards.forEach(card => {
327
+ expect(_validation.VALIDATION_PATTERNS.creditCard.test(card)).toBe(true);
328
+ });
329
+ invalidCards.forEach(card => {
330
+ expect(_validation.VALIDATION_PATTERNS.creditCard.test(card)).toBe(false);
331
+ });
332
+ });
333
+ });
334
+ describe('Edge Cases and Error Handling', () => {
335
+ it('handles empty string inputs consistently', () => {
336
+ expect((0, _validation.validateEmail)('')).toBe(false);
337
+ expect((0, _validation.validatePhone)('')).toBe(false);
338
+ expect((0, _validation.validateUrl)('')).toBe(false);
339
+ expect((0, _validation.validatePasswordStrength)('')).toBe(false);
340
+ });
341
+ it('handles whitespace-only inputs', () => {
342
+ expect((0, _validation.validateEmail)(' ')).toBe(false);
343
+ expect((0, _validation.validatePhone)(' ')).toBe(false);
344
+ expect((0, _validation.validateUrl)(' ')).toBe(false);
345
+ expect((0, _validation.validatePasswordStrength)(' ')).toBe(false);
346
+ });
347
+ it('handles very long inputs gracefully', () => {
348
+ const veryLongString = 'a'.repeat(10000);
349
+ expect(() => (0, _validation.validateEmail)(veryLongString)).not.toThrow();
350
+ expect(() => (0, _validation.validatePhone)(veryLongString)).not.toThrow();
351
+ expect(() => (0, _validation.validateUrl)(veryLongString)).not.toThrow();
352
+ expect(() => (0, _validation.validatePasswordStrength)(veryLongString)).not.toThrow();
353
+ });
354
+ it('handles special characters in inputs', () => {
355
+ const specialChars = '!@#$%^&*()_+-=[]{}|;:,.<>?';
356
+ expect(() => (0, _validation.validateEmail)(specialChars)).not.toThrow();
357
+ expect(() => (0, _validation.validatePhone)(specialChars)).not.toThrow();
358
+ expect(() => (0, _validation.validateUrl)(specialChars)).not.toThrow();
359
+ expect(() => (0, _validation.validatePasswordStrength)(specialChars)).not.toThrow();
360
+ });
361
+ });
362
+ });
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Alert = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _propTypes = require("prop-types");
9
+ var _icons = require("../icons");
10
+ var _ssrSafeId = require("../utils/ssrSafeId");
11
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
13
+ /**
14
+ * Alert - A stateless, accessible alert component for displaying messages
15
+ *
16
+ * Features:
17
+ * - Multiple types (success, error, warning, info)
18
+ * - Dismissible with close button
19
+ * - Accessible with proper ARIA attributes
20
+ * - Icon support
21
+ * - Custom message content
22
+ * - Visibility control
23
+ *
24
+ * @component
25
+ * @example
26
+ * <Alert
27
+ * type="success"
28
+ * message="Operation completed successfully!"
29
+ * isVisible={true}
30
+ * onClose={() => setAlertVisible(false)}
31
+ * isDismissible={true}
32
+ * />
33
+ */
34
+ const Alert = exports.Alert = /*#__PURE__*/(0, _react.forwardRef)(({
35
+ // Core props
36
+ id,
37
+ className = '',
38
+ // Content props
39
+ type = 'info',
40
+ message,
41
+ children,
42
+ icon,
43
+ // Behavior props
44
+ isVisible = true,
45
+ isDismissible = true,
46
+ onClose,
47
+ // Accessibility props
48
+ ariaLabel,
49
+ role = 'alert',
50
+ // Additional props
51
+ ...rest
52
+ }, ref) => {
53
+ const stableId = (0, _ssrSafeId.useStableId)(id);
54
+ if (!isVisible) {
55
+ return null;
56
+ }
57
+ const handleClose = () => {
58
+ if (onClose && typeof onClose === 'function') {
59
+ onClose();
60
+ }
61
+ };
62
+ const combinedClassName = ['ui-alert', `ui-alert--${type}`, isDismissible && 'ui-alert--dismissible', className].filter(Boolean).join(' ');
63
+ return /*#__PURE__*/_react.default.createElement("div", _extends({
64
+ ref: ref,
65
+ id: stableId,
66
+ className: combinedClassName,
67
+ role: role,
68
+ "aria-label": ariaLabel
69
+ }, rest), icon && /*#__PURE__*/_react.default.createElement("div", {
70
+ className: "ui-alert__icon"
71
+ }, icon), /*#__PURE__*/_react.default.createElement("div", {
72
+ className: "ui-alert__content"
73
+ }, message && /*#__PURE__*/_react.default.createElement("div", {
74
+ className: "ui-alert__message"
75
+ }, message), children && /*#__PURE__*/_react.default.createElement("div", {
76
+ className: "ui-alert__children"
77
+ }, children)), isDismissible && onClose && /*#__PURE__*/_react.default.createElement("button", {
78
+ className: "ui-alert__close",
79
+ onClick: handleClose,
80
+ "aria-label": "Close alert",
81
+ type: "button"
82
+ }, /*#__PURE__*/_react.default.createElement(_icons.Close, {
83
+ dimensions: 16
84
+ })));
85
+ });
86
+ Alert.displayName = 'Alert';
87
+ Alert.propTypes = {
88
+ // Core props
89
+ id: _propTypes.string,
90
+ className: _propTypes.string,
91
+ // Content props
92
+ type: (0, _propTypes.oneOf)(['success', 'error', 'warning', 'info']),
93
+ message: _propTypes.node,
94
+ children: _propTypes.node,
95
+ icon: _propTypes.node,
96
+ // Behavior props
97
+ isVisible: _propTypes.bool,
98
+ isDismissible: _propTypes.bool,
99
+ onClose: _propTypes.func,
100
+ // Accessibility props
101
+ ariaLabel: _propTypes.string,
102
+ role: _propTypes.string
103
+ };
104
+ var _default = exports.default = Alert;