@aurodesignsystem/auro-formkit 3.0.1 → 3.1.0

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 (59) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/components/checkbox/README.md +1 -1
  3. package/components/checkbox/demo/api.min.js +468 -25
  4. package/components/checkbox/demo/index.min.js +468 -25
  5. package/components/checkbox/demo/readme.md +1 -1
  6. package/components/checkbox/dist/index.js +468 -25
  7. package/components/checkbox/dist/registered.js +468 -25
  8. package/components/combobox/README.md +1 -1
  9. package/components/combobox/demo/api.min.js +1125 -74
  10. package/components/combobox/demo/index.min.js +1125 -74
  11. package/components/combobox/demo/readme.md +1 -1
  12. package/components/combobox/dist/auro-combobox.d.ts +30 -0
  13. package/components/combobox/dist/index.js +1125 -74
  14. package/components/combobox/dist/registered.js +1125 -74
  15. package/components/counter/README.md +1 -1
  16. package/components/counter/demo/api.min.js +570 -45
  17. package/components/counter/demo/index.min.js +570 -45
  18. package/components/counter/demo/readme.md +1 -1
  19. package/components/counter/dist/index.js +570 -45
  20. package/components/counter/dist/registered.js +570 -45
  21. package/components/datepicker/README.md +1 -1
  22. package/components/datepicker/demo/api.min.js +1073 -70
  23. package/components/datepicker/demo/index.min.js +1073 -70
  24. package/components/datepicker/demo/readme.md +1 -1
  25. package/components/datepicker/dist/index.js +1073 -70
  26. package/components/datepicker/dist/registered.js +1073 -70
  27. package/components/dropdown/README.md +1 -1
  28. package/components/dropdown/demo/api.md +8 -5
  29. package/components/dropdown/demo/api.min.js +104 -22
  30. package/components/dropdown/demo/index.min.js +104 -22
  31. package/components/dropdown/demo/readme.md +1 -1
  32. package/components/dropdown/dist/auro-dropdown.d.ts +29 -0
  33. package/components/dropdown/dist/index.js +104 -22
  34. package/components/dropdown/dist/registered.js +104 -22
  35. package/components/form/README.md +1 -1
  36. package/components/form/demo/readme.md +1 -1
  37. package/components/input/README.md +1 -1
  38. package/components/input/demo/api.md +4 -1
  39. package/components/input/demo/api.min.js +503 -25
  40. package/components/input/demo/index.min.js +503 -25
  41. package/components/input/demo/readme.md +1 -1
  42. package/components/input/dist/base-input.d.ts +24 -0
  43. package/components/input/dist/index.js +503 -25
  44. package/components/input/dist/registered.js +503 -25
  45. package/components/menu/README.md +1 -1
  46. package/components/menu/demo/readme.md +1 -1
  47. package/components/radio/README.md +1 -1
  48. package/components/radio/demo/api.min.js +468 -25
  49. package/components/radio/demo/index.min.js +468 -25
  50. package/components/radio/demo/readme.md +1 -1
  51. package/components/radio/dist/index.js +468 -25
  52. package/components/radio/dist/registered.js +468 -25
  53. package/components/select/README.md +1 -1
  54. package/components/select/demo/api.min.js +570 -45
  55. package/components/select/demo/index.min.js +570 -45
  56. package/components/select/demo/readme.md +1 -1
  57. package/components/select/dist/index.js +570 -45
  58. package/components/select/dist/registered.js +570 -45
  59. package/package.json +2 -2
@@ -114,11 +114,420 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
114
114
  }
115
115
  };
116
116
 
117
+ let DateFormatter$1 = class DateFormatter {
118
+
119
+ constructor() {
120
+
121
+ /**
122
+ * @description Parses a date string into its components.
123
+ * @param {string} dateStr - Date string to parse.
124
+ * @param {string} format - Date format to parse.
125
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
126
+ */
127
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
128
+
129
+ // Guard Clause: Date string is defined
130
+ if (!dateStr) {
131
+ return undefined;
132
+ }
133
+
134
+ // Assume the separator is a "/" a defined in our code base
135
+ const separator = '/';
136
+
137
+ // Get the parts of the date and format
138
+ const valueParts = dateStr.split(separator);
139
+ const formatParts = format.split(separator);
140
+
141
+ // Check if the value and format have the correct number of parts
142
+ if (valueParts.length !== formatParts.length) {
143
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
144
+ }
145
+
146
+ // Holds the result to be returned
147
+ const result = formatParts.reduce((acc, part, index) => {
148
+ const value = valueParts[index];
149
+
150
+ if ((/m/iu).test(part)) {
151
+ acc.month = value;
152
+ } else if ((/d/iu).test(part)) {
153
+ acc.day = value;
154
+ } else if ((/y/iu).test(part)) {
155
+ acc.year = value;
156
+ }
157
+
158
+ return acc;
159
+ }, {});
160
+
161
+ // If we found all the parts, return the result
162
+ if (result.month && result.year) {
163
+ return result;
164
+ }
165
+
166
+ // Throw an error to let the dev know we were unable to parse the date string
167
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
168
+ };
169
+
170
+ /**
171
+ * Convert a date object to string format.
172
+ * @param {Object} date - Date to convert to string.
173
+ * @returns {Object} Returns the date as a string.
174
+ */
175
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
176
+ year: "numeric",
177
+ month: "2-digit",
178
+ day: "2-digit",
179
+ });
180
+
181
+ /**
182
+ * Converts a date string to a North American date format.
183
+ * @param {String} dateStr - Date to validate.
184
+ * @param {String} format - Date format to validate against.
185
+ * @returns {Boolean}
186
+ */
187
+ this.toNorthAmericanFormat = (dateStr, format) => {
188
+
189
+ if (format === 'mm/dd/yyyy') {
190
+ return dateStr;
191
+ }
192
+
193
+ const parsedDate = this.parseDate(dateStr, format);
194
+
195
+ if (!parsedDate) {
196
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
197
+ }
198
+
199
+ const { month, day, year } = parsedDate;
200
+
201
+ const dateParts = [];
202
+ if (month) {
203
+ dateParts.push(month);
204
+ }
205
+
206
+ if (day) {
207
+ dateParts.push(day);
208
+ }
209
+
210
+ if (year) {
211
+ dateParts.push(year);
212
+ }
213
+
214
+ return dateParts.join('/');
215
+ };
216
+ }
217
+ };
218
+ const dateFormatter$1 = new DateFormatter$1();
219
+
220
+ // filepath: dateConstraints.mjs
221
+ const DATE_UTIL_CONSTRAINTS$1 = {
222
+ maxDay: 31,
223
+ maxMonth: 12,
224
+ maxYear: 2400,
225
+ minDay: 1,
226
+ minMonth: 1,
227
+ minYear: 1900,
228
+ };
229
+
230
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
231
+
232
+ /**
233
+ * @description The maximum day value allowed by the various utilities in this class.
234
+ * @readonly
235
+ * @type {Number}
236
+ */
237
+ get maxDay() {
238
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
239
+ }
240
+
241
+ /**
242
+ * @description The maximum month value allowed by the various utilities in this class.
243
+ * @readonly
244
+ * @type {Number}
245
+ */
246
+ get maxMonth() {
247
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
248
+ }
249
+
250
+ /**
251
+ * @description The maximum year value allowed by the various utilities in this class.
252
+ * @readonly
253
+ * @type {Number}
254
+ */
255
+ get maxYear() {
256
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
257
+ }
258
+
259
+ /**
260
+ * @description The minimum day value allowed by the various utilities in this class.
261
+ * @readonly
262
+ * @type {Number}
263
+ */
264
+ get minDay() {
265
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
266
+ }
267
+
268
+ /**
269
+ * @description The minimum month value allowed by the various utilities in this class.
270
+ * @readonly
271
+ * @type {Number}
272
+ */
273
+ get minMonth() {
274
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
275
+ }
276
+
277
+ /**
278
+ * @description The minimum year value allowed by the various utilities in this class.
279
+ * @readonly
280
+ * @type {Number}
281
+ */
282
+ get minYear() {
283
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
284
+ }
285
+ };
286
+
287
+ /* eslint-disable no-magic-numbers */
288
+
289
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
290
+
291
+ /**
292
+ * Returns the current century.
293
+ * @returns {String} The current century.
294
+ */
295
+ getCentury () {
296
+ return String(new Date().getFullYear()).slice(0, 2);
297
+ }
298
+
299
+ /**
300
+ * Returns a four digit year.
301
+ * @param {String} year - The year to convert to four digits.
302
+ * @returns {String} The four digit year.
303
+ */
304
+ getFourDigitYear (year) {
305
+
306
+ const strYear = String(year).trim();
307
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
308
+ }
309
+
310
+ constructor() {
311
+
312
+ super();
313
+
314
+ /**
315
+ * Compares two dates to see if they match.
316
+ * @param {Object} date1 - First date to compare.
317
+ * @param {Object} date2 - Second date to compare.
318
+ * @returns {Boolean} Returns true if the dates match.
319
+ */
320
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
321
+
322
+ /**
323
+ * Returns true if value passed in is a valid date.
324
+ * @param {String} date - Date to validate.
325
+ * @param {String} format - Date format to validate against.
326
+ * @returns {Boolean}
327
+ */
328
+ this.validDateStr = (date, format) => {
329
+
330
+ // The length we expect the date string to be
331
+ const dateStrLength = format.length;
332
+
333
+ // Guard Clause: Date and format are defined
334
+ if (typeof date === "undefined" || typeof format === "undefined") {
335
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
336
+ }
337
+
338
+ // Guard Clause: Date should be of type string
339
+ if (typeof date !== "string") {
340
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
341
+ }
342
+
343
+ // Guard Clause: Format should be of type string
344
+ if (typeof format !== "string") {
345
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
346
+ }
347
+
348
+ // Guard Clause: Length is what we expect it to be
349
+ if (date.length !== dateStrLength) {
350
+ return false;
351
+ }
352
+ // Get a formatted date string and parse it
353
+ const dateParts = dateFormatter$1.parseDate(date, format);
354
+
355
+ // Guard Clause: Date parse succeeded
356
+ if (!dateParts) {
357
+ return false;
358
+ }
359
+
360
+ // Create the expected date string based on the date parts
361
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
362
+
363
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
364
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
365
+
366
+ // Get the date string of the date object we created from the string date
367
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
368
+
369
+ // Guard Clause: Generated date matches date string input
370
+ if (expectedDateStr !== actualDateStr) {
371
+ return false;
372
+ }
373
+
374
+ // If we passed all other checks, we can assume the date is valid
375
+ return true;
376
+ };
377
+
378
+ /**
379
+ * Determines if a string date value matches the format provided.
380
+ * @param {string} value = The date string value.
381
+ * @param { string} format = The date format to match against.
382
+ * @returns {boolean}
383
+ */
384
+ this.dateAndFormatMatch = (value, format) => {
385
+
386
+ // Ensure we have both values we need to do the comparison
387
+ if (!value || !format) {
388
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
389
+ }
390
+
391
+ // If the lengths are different, they cannot match
392
+ if (value.length !== format.length) {
393
+ return false;
394
+ }
395
+
396
+ // Get the parts of the date
397
+ const dateParts = dateFormatter$1.parseDate(value, format);
398
+
399
+ // Validator for day
400
+ const dayValueIsValid = (day) => {
401
+
402
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
403
+ if (!dateParts.day) {
404
+ return true;
405
+ }
406
+
407
+ // Guard clause: ensure day exists.
408
+ if (!day) {
409
+ return false;
410
+ }
411
+
412
+ // Convert day to number
413
+ const numDay = Number.parseInt(day, 10);
414
+
415
+ // Guard clause: ensure day is a valid integer
416
+ if (Number.isNaN(numDay)) {
417
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
418
+ }
419
+
420
+ // Guard clause: ensure day is within the valid range
421
+ if (numDay < this.minDay || numDay > this.maxDay) {
422
+ return false;
423
+ }
424
+
425
+ // Default return
426
+ return true;
427
+ };
428
+
429
+ // Validator for month
430
+ const monthValueIsValid = (month) => {
431
+
432
+ // Guard clause: ensure month exists.
433
+ if (!month) {
434
+ return false;
435
+ }
436
+
437
+ // Convert month to number
438
+ const numMonth = Number.parseInt(month, 10);
439
+
440
+ // Guard clause: ensure month is a valid integer
441
+ if (Number.isNaN(numMonth)) {
442
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
443
+ }
444
+
445
+ // Guard clause: ensure month is within the valid range
446
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
447
+ return false;
448
+ }
449
+
450
+ // Default return
451
+ return true;
452
+ };
453
+
454
+ // Validator for year
455
+ const yearIsValid = (_year) => {
456
+
457
+ // Guard clause: ensure year exists.
458
+ if (!_year) {
459
+ return false;
460
+ }
461
+
462
+ // Get the full year
463
+ const year = this.getFourDigitYear(_year);
464
+
465
+ // Convert year to number
466
+ const numYear = Number.parseInt(year, 10);
467
+
468
+ // Guard clause: ensure year is a valid integer
469
+ if (Number.isNaN(numYear)) {
470
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
471
+ }
472
+
473
+ // Guard clause: ensure year is within the valid range
474
+ if (numYear < this.minYear || numYear > this.maxYear) {
475
+ return false;
476
+ }
477
+
478
+ // Default return
479
+ return true;
480
+ };
481
+
482
+ // Self-contained checks for month, day, and year
483
+ const checks = [
484
+ monthValueIsValid(dateParts.month),
485
+ dayValueIsValid(dateParts.day),
486
+ yearIsValid(dateParts.year)
487
+ ];
488
+
489
+ // If any of the checks failed, the date format does not match and the result is invalid
490
+ const isValid = checks.every((check) => check === true);
491
+
492
+ // If the check is invalid, return false
493
+ if (!isValid) {
494
+ return false;
495
+ }
496
+
497
+ // Default case
498
+ return true;
499
+ };
500
+ }
501
+ };
502
+
503
+ // Export a class instance
504
+ const dateUtilities$1 = new AuroDateUtilities$1();
505
+
506
+ // Export the class instance methods individually
507
+ const {
508
+ datesMatch: datesMatch$1,
509
+ validDateStr: validDateStr$1,
510
+ dateAndFormatMatch: dateAndFormatMatch$1,
511
+ minDay: minDay$1,
512
+ minMonth: minMonth$1,
513
+ minYear: minYear$1,
514
+ maxDay: maxDay$1,
515
+ maxMonth: maxMonth$1,
516
+ maxYear: maxYear$1
517
+ } = dateUtilities$1;
518
+
519
+ const {
520
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
521
+ parseDate: parseDate$1,
522
+ getDateAsString: getDateAsString$1
523
+ } = dateFormatter$1;
524
+
117
525
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
118
526
  // See LICENSE in the project root for license information.
119
527
 
120
528
 
121
529
  let AuroFormValidation$1 = class AuroFormValidation {
530
+
122
531
  constructor() {
123
532
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
124
533
  }
@@ -210,17 +619,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
210
619
  ]
211
620
  }
212
621
  };
213
-
622
+
214
623
  let elementType;
215
624
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
216
625
  elementType = 'input';
217
626
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
218
627
  elementType = 'counter';
219
628
  }
220
-
629
+
221
630
  if (elementType) {
222
631
  const rules = validationRules[elementType];
223
-
632
+
224
633
  if (rules) {
225
634
  Object.values(rules).flat().forEach(rule => {
226
635
  if (rule.check(elem)) {
@@ -246,48 +655,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
246
655
  if (!elem.value.match(emailRegex)) {
247
656
  elem.validity = 'patternMismatch';
248
657
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
658
+ return;
249
659
  }
250
660
  } else if (elem.type === 'credit-card') {
251
661
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
252
662
  elem.validity = 'tooShort';
253
663
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
664
+ return;
254
665
  }
255
666
  } else if (elem.type === 'number') {
256
667
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
257
668
  elem.validity = 'rangeOverflow';
258
669
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
670
+ return;
259
671
  }
260
672
 
261
673
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
262
674
  elem.validity = 'rangeUnderflow';
263
675
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
676
+ return;
264
677
  }
265
- } else if (elem.type === 'date') {
266
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
678
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
679
+
680
+ // Guard Clause: if the value is too short
681
+ if (elem.value.length < elem.lengthForType) {
682
+
267
683
  elem.validity = 'tooShort';
268
684
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
269
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
270
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
271
- const valueDate = new Date(formattedValue.dateForComparison);
685
+ return;
686
+ }
687
+
688
+ // Guard Clause: If the value is too long for the type
689
+ if (elem.value?.length > elem.lengthForType) {
690
+
691
+ elem.validity = 'tooLong';
692
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
693
+ return;
694
+ }
695
+
696
+ // Validate that the date passed was the correct format
697
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
698
+ elem.validity = 'patternMismatch';
699
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
700
+ return;
701
+ }
702
+
703
+ // Validate that the date passed was a valid date
704
+ if (!validDateStr$1(elem.value, elem.format)) {
705
+ elem.validity = 'invalidDate';
706
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
707
+ return;
708
+ }
272
709
 
273
- // validate max
274
- if (elem.max?.length === elem.lengthForType) {
275
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
710
+ // Perform the rest of the validation
711
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
712
+ const valueDate = new Date(formattedValue);
276
713
 
277
- if (valueDate > maxDate) {
278
- elem.validity = 'rangeOverflow';
279
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
280
- }
714
+ // // Validate max date
715
+ if (elem.max?.length === elem.lengthForType) {
716
+
717
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
718
+
719
+ if (valueDate > maxDate) {
720
+ elem.validity = 'rangeOverflow';
721
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
722
+ return;
281
723
  }
724
+ }
282
725
 
283
- // validate min
284
- if (elem.min?.length === elem.lengthForType) {
285
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
726
+ // Validate min date
727
+ if (elem.min?.length === elem.lengthForType) {
728
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
286
729
 
287
- if (valueDate < minDate) {
288
- elem.validity = 'rangeUnderflow';
289
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
290
- }
730
+ if (valueDate < minDate) {
731
+ elem.validity = 'rangeUnderflow';
732
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
733
+ return;
291
734
  }
292
735
  }
293
736
  }
@@ -406,7 +849,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
406
849
  if (input.validationMessage.length > 0) {
407
850
  elem.errorMessage = input.validationMessage;
408
851
  }
409
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
852
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
410
853
  const firstInput = this.inputElements[0];
411
854
 
412
855
  if (firstInput.validationMessage.length > 0) {
@@ -2268,7 +2711,7 @@ class AuroFloatingUI {
2268
2711
  /**
2269
2712
  * @private
2270
2713
  * getting called on 'blur' in trigger or `focusin` in document
2271
- *
2714
+ *
2272
2715
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2273
2716
  * This method checks if the currently active element is still within the trigger or bib.
2274
2717
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -2384,7 +2827,7 @@ class AuroFloatingUI {
2384
2827
  // Close any other dropdown that is already open
2385
2828
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2386
2829
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2387
- existedVisibleFloatingUI.isPopoverVisible &&
2830
+ existedVisibleFloatingUI.element.isPopoverVisible &&
2388
2831
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2389
2832
  document.expandedAuroFloater.hideBib();
2390
2833
  }
@@ -2560,7 +3003,7 @@ class AuroFloatingUI {
2560
3003
  this.id = window.crypto.randomUUID();
2561
3004
  this.element.setAttribute('id', this.id);
2562
3005
  }
2563
-
3006
+
2564
3007
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2565
3008
  }
2566
3009
 
@@ -2613,7 +3056,7 @@ class AuroFloatingUI {
2613
3056
  if (this.element.bib) {
2614
3057
  this.element.shadowRoot.append(this.element.bib);
2615
3058
  }
2616
-
3059
+
2617
3060
  // Remove event & keyboard listeners
2618
3061
  if (this.element?.trigger) {
2619
3062
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -3342,6 +3785,7 @@ var helpTextVersion$1 = '1.0.0';
3342
3785
  * @csspart helpText - The helpText content container.
3343
3786
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3344
3787
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3788
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3345
3789
  */
3346
3790
  class AuroDropdown extends LitElement {
3347
3791
  constructor() {
@@ -3387,7 +3831,9 @@ class AuroDropdown extends LitElement {
3387
3831
  this.rounded = false;
3388
3832
  this.tabIndex = 0;
3389
3833
  this.noToggle = false;
3834
+ this.a11yAutocomplete = 'none';
3390
3835
  this.labeled = true;
3836
+ this.a11yRole = 'combobox';
3391
3837
  this.onDark = false;
3392
3838
 
3393
3839
  // floaterConfig
@@ -3523,6 +3969,16 @@ class AuroDropdown extends LitElement {
3523
3969
  type: Number
3524
3970
  },
3525
3971
 
3972
+ /**
3973
+ * The unique ID for the dropdown bib element.
3974
+ * @private
3975
+ */
3976
+ dropdownId: {
3977
+ type: String,
3978
+ reflect: false,
3979
+ attribute: false
3980
+ },
3981
+
3526
3982
  /**
3527
3983
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3528
3984
  */
@@ -3685,6 +4141,23 @@ class AuroDropdown extends LitElement {
3685
4141
  */
3686
4142
  tabIndex: {
3687
4143
  type: Number
4144
+ },
4145
+
4146
+ /**
4147
+ * The value for the role attribute of the trigger element.
4148
+ */
4149
+ a11yRole: {
4150
+ type: String || undefined,
4151
+ attribute: false,
4152
+ reflect: false
4153
+ },
4154
+
4155
+ /**
4156
+ * The value for the aria-autocomplete attribute of the trigger element.
4157
+ */
4158
+ a11yAutocomplete: {
4159
+ type: String,
4160
+ attribute: false,
3688
4161
  }
3689
4162
  };
3690
4163
  }
@@ -3735,7 +4208,22 @@ class AuroDropdown extends LitElement {
3735
4208
  }
3736
4209
 
3737
4210
  firstUpdated() {
4211
+
4212
+ // Configure the floater to, this will generate the ID for the bib
3738
4213
  this.floater.configure(this, 'auroDropdown');
4214
+
4215
+ /**
4216
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4217
+ * @event auroDropdown-idAdded
4218
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4219
+ */
4220
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4221
+
4222
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4223
+ if (!this.triggerContentFocusable) {
4224
+ this.dropdownId = this.floater.element.id;
4225
+ }
4226
+
3739
4227
  this.bibContent = this.floater.element.bib;
3740
4228
 
3741
4229
  // Add the tag name as an attribute if it is different than the component name
@@ -3887,6 +4375,30 @@ class AuroDropdown extends LitElement {
3887
4375
  });
3888
4376
  }
3889
4377
 
4378
+ /*
4379
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4380
+ * @private
4381
+ * @method setTriggerAriaAttributes
4382
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4383
+ */
4384
+ clearTriggerA11yAttributes(triggerElement) {
4385
+
4386
+ if (!triggerElement || !triggerElement.removeAttribute) {
4387
+ return;
4388
+ }
4389
+
4390
+ // Reset appropriate attributes for a11y
4391
+ triggerElement.removeAttribute('aria-labelledby');
4392
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4393
+ triggerElement.removeAttribute('id');
4394
+ }
4395
+ triggerElement.removeAttribute('role');
4396
+ triggerElement.removeAttribute('aria-expanded');
4397
+
4398
+ triggerElement.removeAttribute('aria-controls');
4399
+ triggerElement.removeAttribute('aria-autocomplete');
4400
+ }
4401
+
3890
4402
  /**
3891
4403
  * Handles changes to the trigger content slot and updates related properties.
3892
4404
  *
@@ -3900,32 +4412,41 @@ class AuroDropdown extends LitElement {
3900
4412
  * @returns {void}
3901
4413
  */
3902
4414
  handleTriggerContentSlotChange(event) {
4415
+
3903
4416
  this.floater.handleTriggerTabIndex();
3904
4417
 
4418
+ // Get the trigger
4419
+ const trigger = this.shadowRoot.querySelector('#trigger');
4420
+
4421
+ // Get the trigger slot
3905
4422
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
3906
4423
 
4424
+ // If there's a trigger slot
3907
4425
  if (triggerSlot) {
3908
4426
 
4427
+ // Get the content nodes to see if there are any children
3909
4428
  const triggerContentNodes = triggerSlot.assignedNodes();
3910
4429
 
4430
+ // If there are children
3911
4431
  if (triggerContentNodes) {
3912
4432
 
3913
- triggerContentNodes.forEach((node) => {
3914
- if (!this.triggerContentFocusable) {
3915
- this.triggerContentFocusable = this.containsFocusableElement(node);
3916
- }
3917
- });
3918
- }
3919
- }
4433
+ // See if any of them are focusable elemeents
4434
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
3920
4435
 
3921
- const trigger = this.shadowRoot.querySelector('#trigger');
4436
+ // If any of them are focusable elements
4437
+ if (this.triggerContentFocusable) {
3922
4438
 
3923
- if (!this.triggerContentFocusable) {
3924
- trigger.setAttribute('tabindex', '0');
3925
- trigger.setAttribute('role', 'button');
3926
- } else {
3927
- trigger.removeAttribute('tabindex');
3928
- trigger.removeAttribute('role');
4439
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4440
+ this.clearTriggerA11yAttributes(trigger);
4441
+
4442
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4443
+ trigger.removeAttribute('tabindex');
4444
+ } else {
4445
+
4446
+ // Add the tabindex to the trigger so that it's in the focus flow
4447
+ trigger.setAttribute('tabindex', '0');
4448
+ }
4449
+ }
3929
4450
  }
3930
4451
 
3931
4452
  if (event) {
@@ -3935,6 +4456,7 @@ class AuroDropdown extends LitElement {
3935
4456
 
3936
4457
  if (this.triggerContentSlot) {
3937
4458
  this.setupTriggerFocusEventBinding();
4459
+
3938
4460
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
3939
4461
  if (slot.textContent.trim()) {
3940
4462
  return true;
@@ -4002,10 +4524,13 @@ class AuroDropdown extends LitElement {
4002
4524
  id="trigger"
4003
4525
  class="trigger"
4004
4526
  part="trigger"
4005
- aria-labelledby="triggerLabel"
4006
4527
  tabindex="${this.tabIndex}"
4007
4528
  ?showBorder="${this.showTriggerBorders}"
4008
- >
4529
+ role="${ifDefined(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4530
+ aria-expanded="${ifDefined(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4531
+ aria-controls="${ifDefined(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4532
+ aria-labelledby="${ifDefined(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4533
+ >
4009
4534
  <div class="triggerContentWrapper">
4010
4535
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4011
4536
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4039,12 +4564,12 @@ class AuroDropdown extends LitElement {
4039
4564
  <div id="bibSizer" part="size"></div>
4040
4565
  <${this.dropdownBibTag}
4041
4566
  id="bib"
4042
- role="tooltip"
4043
4567
  ?data-show="${this.isPopoverVisible}"
4044
4568
  ?isfullscreen="${this.isBibFullscreen}"
4045
4569
  ?common="${this.common}"
4046
4570
  ?rounded="${this.common || this.rounded}"
4047
- ?inset="${this.common || this.inset}">
4571
+ ?inset="${this.common || this.inset}"
4572
+ >
4048
4573
  </${this.dropdownBibTag}>
4049
4574
  </div>
4050
4575
  `;
@@ -8022,6 +8547,414 @@ class AuroInputUtilities {
8022
8547
  }
8023
8548
  }
8024
8549
 
8550
+ class DateFormatter {
8551
+
8552
+ constructor() {
8553
+
8554
+ /**
8555
+ * @description Parses a date string into its components.
8556
+ * @param {string} dateStr - Date string to parse.
8557
+ * @param {string} format - Date format to parse.
8558
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
8559
+ */
8560
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
8561
+
8562
+ // Guard Clause: Date string is defined
8563
+ if (!dateStr) {
8564
+ return undefined;
8565
+ }
8566
+
8567
+ // Assume the separator is a "/" a defined in our code base
8568
+ const separator = '/';
8569
+
8570
+ // Get the parts of the date and format
8571
+ const valueParts = dateStr.split(separator);
8572
+ const formatParts = format.split(separator);
8573
+
8574
+ // Check if the value and format have the correct number of parts
8575
+ if (valueParts.length !== formatParts.length) {
8576
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
8577
+ }
8578
+
8579
+ // Holds the result to be returned
8580
+ const result = formatParts.reduce((acc, part, index) => {
8581
+ const value = valueParts[index];
8582
+
8583
+ if ((/m/iu).test(part)) {
8584
+ acc.month = value;
8585
+ } else if ((/d/iu).test(part)) {
8586
+ acc.day = value;
8587
+ } else if ((/y/iu).test(part)) {
8588
+ acc.year = value;
8589
+ }
8590
+
8591
+ return acc;
8592
+ }, {});
8593
+
8594
+ // If we found all the parts, return the result
8595
+ if (result.month && result.year) {
8596
+ return result;
8597
+ }
8598
+
8599
+ // Throw an error to let the dev know we were unable to parse the date string
8600
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
8601
+ };
8602
+
8603
+ /**
8604
+ * Convert a date object to string format.
8605
+ * @param {Object} date - Date to convert to string.
8606
+ * @returns {Object} Returns the date as a string.
8607
+ */
8608
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
8609
+ year: "numeric",
8610
+ month: "2-digit",
8611
+ day: "2-digit",
8612
+ });
8613
+
8614
+ /**
8615
+ * Converts a date string to a North American date format.
8616
+ * @param {String} dateStr - Date to validate.
8617
+ * @param {String} format - Date format to validate against.
8618
+ * @returns {Boolean}
8619
+ */
8620
+ this.toNorthAmericanFormat = (dateStr, format) => {
8621
+
8622
+ if (format === 'mm/dd/yyyy') {
8623
+ return dateStr;
8624
+ }
8625
+
8626
+ const parsedDate = this.parseDate(dateStr, format);
8627
+
8628
+ if (!parsedDate) {
8629
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
8630
+ }
8631
+
8632
+ const { month, day, year } = parsedDate;
8633
+
8634
+ const dateParts = [];
8635
+ if (month) {
8636
+ dateParts.push(month);
8637
+ }
8638
+
8639
+ if (day) {
8640
+ dateParts.push(day);
8641
+ }
8642
+
8643
+ if (year) {
8644
+ dateParts.push(year);
8645
+ }
8646
+
8647
+ return dateParts.join('/');
8648
+ };
8649
+ }
8650
+ }
8651
+ const dateFormatter = new DateFormatter();
8652
+
8653
+ // filepath: dateConstraints.mjs
8654
+ const DATE_UTIL_CONSTRAINTS = {
8655
+ maxDay: 31,
8656
+ maxMonth: 12,
8657
+ maxYear: 2400,
8658
+ minDay: 1,
8659
+ minMonth: 1,
8660
+ minYear: 1900,
8661
+ };
8662
+
8663
+ class AuroDateUtilitiesBase {
8664
+
8665
+ /**
8666
+ * @description The maximum day value allowed by the various utilities in this class.
8667
+ * @readonly
8668
+ * @type {Number}
8669
+ */
8670
+ get maxDay() {
8671
+ return DATE_UTIL_CONSTRAINTS.maxDay;
8672
+ }
8673
+
8674
+ /**
8675
+ * @description The maximum month value allowed by the various utilities in this class.
8676
+ * @readonly
8677
+ * @type {Number}
8678
+ */
8679
+ get maxMonth() {
8680
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
8681
+ }
8682
+
8683
+ /**
8684
+ * @description The maximum year value allowed by the various utilities in this class.
8685
+ * @readonly
8686
+ * @type {Number}
8687
+ */
8688
+ get maxYear() {
8689
+ return DATE_UTIL_CONSTRAINTS.maxYear;
8690
+ }
8691
+
8692
+ /**
8693
+ * @description The minimum day value allowed by the various utilities in this class.
8694
+ * @readonly
8695
+ * @type {Number}
8696
+ */
8697
+ get minDay() {
8698
+ return DATE_UTIL_CONSTRAINTS.minDay;
8699
+ }
8700
+
8701
+ /**
8702
+ * @description The minimum month value allowed by the various utilities in this class.
8703
+ * @readonly
8704
+ * @type {Number}
8705
+ */
8706
+ get minMonth() {
8707
+ return DATE_UTIL_CONSTRAINTS.minMonth;
8708
+ }
8709
+
8710
+ /**
8711
+ * @description The minimum year value allowed by the various utilities in this class.
8712
+ * @readonly
8713
+ * @type {Number}
8714
+ */
8715
+ get minYear() {
8716
+ return DATE_UTIL_CONSTRAINTS.minYear;
8717
+ }
8718
+ }
8719
+
8720
+ /* eslint-disable no-magic-numbers */
8721
+
8722
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
8723
+
8724
+ /**
8725
+ * Returns the current century.
8726
+ * @returns {String} The current century.
8727
+ */
8728
+ getCentury () {
8729
+ return String(new Date().getFullYear()).slice(0, 2);
8730
+ }
8731
+
8732
+ /**
8733
+ * Returns a four digit year.
8734
+ * @param {String} year - The year to convert to four digits.
8735
+ * @returns {String} The four digit year.
8736
+ */
8737
+ getFourDigitYear (year) {
8738
+
8739
+ const strYear = String(year).trim();
8740
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
8741
+ }
8742
+
8743
+ constructor() {
8744
+
8745
+ super();
8746
+
8747
+ /**
8748
+ * Compares two dates to see if they match.
8749
+ * @param {Object} date1 - First date to compare.
8750
+ * @param {Object} date2 - Second date to compare.
8751
+ * @returns {Boolean} Returns true if the dates match.
8752
+ */
8753
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
8754
+
8755
+ /**
8756
+ * Returns true if value passed in is a valid date.
8757
+ * @param {String} date - Date to validate.
8758
+ * @param {String} format - Date format to validate against.
8759
+ * @returns {Boolean}
8760
+ */
8761
+ this.validDateStr = (date, format) => {
8762
+
8763
+ // The length we expect the date string to be
8764
+ const dateStrLength = format.length;
8765
+
8766
+ // Guard Clause: Date and format are defined
8767
+ if (typeof date === "undefined" || typeof format === "undefined") {
8768
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
8769
+ }
8770
+
8771
+ // Guard Clause: Date should be of type string
8772
+ if (typeof date !== "string") {
8773
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
8774
+ }
8775
+
8776
+ // Guard Clause: Format should be of type string
8777
+ if (typeof format !== "string") {
8778
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
8779
+ }
8780
+
8781
+ // Guard Clause: Length is what we expect it to be
8782
+ if (date.length !== dateStrLength) {
8783
+ return false;
8784
+ }
8785
+ // Get a formatted date string and parse it
8786
+ const dateParts = dateFormatter.parseDate(date, format);
8787
+
8788
+ // Guard Clause: Date parse succeeded
8789
+ if (!dateParts) {
8790
+ return false;
8791
+ }
8792
+
8793
+ // Create the expected date string based on the date parts
8794
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
8795
+
8796
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
8797
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
8798
+
8799
+ // Get the date string of the date object we created from the string date
8800
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
8801
+
8802
+ // Guard Clause: Generated date matches date string input
8803
+ if (expectedDateStr !== actualDateStr) {
8804
+ return false;
8805
+ }
8806
+
8807
+ // If we passed all other checks, we can assume the date is valid
8808
+ return true;
8809
+ };
8810
+
8811
+ /**
8812
+ * Determines if a string date value matches the format provided.
8813
+ * @param {string} value = The date string value.
8814
+ * @param { string} format = The date format to match against.
8815
+ * @returns {boolean}
8816
+ */
8817
+ this.dateAndFormatMatch = (value, format) => {
8818
+
8819
+ // Ensure we have both values we need to do the comparison
8820
+ if (!value || !format) {
8821
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
8822
+ }
8823
+
8824
+ // If the lengths are different, they cannot match
8825
+ if (value.length !== format.length) {
8826
+ return false;
8827
+ }
8828
+
8829
+ // Get the parts of the date
8830
+ const dateParts = dateFormatter.parseDate(value, format);
8831
+
8832
+ // Validator for day
8833
+ const dayValueIsValid = (day) => {
8834
+
8835
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
8836
+ if (!dateParts.day) {
8837
+ return true;
8838
+ }
8839
+
8840
+ // Guard clause: ensure day exists.
8841
+ if (!day) {
8842
+ return false;
8843
+ }
8844
+
8845
+ // Convert day to number
8846
+ const numDay = Number.parseInt(day, 10);
8847
+
8848
+ // Guard clause: ensure day is a valid integer
8849
+ if (Number.isNaN(numDay)) {
8850
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
8851
+ }
8852
+
8853
+ // Guard clause: ensure day is within the valid range
8854
+ if (numDay < this.minDay || numDay > this.maxDay) {
8855
+ return false;
8856
+ }
8857
+
8858
+ // Default return
8859
+ return true;
8860
+ };
8861
+
8862
+ // Validator for month
8863
+ const monthValueIsValid = (month) => {
8864
+
8865
+ // Guard clause: ensure month exists.
8866
+ if (!month) {
8867
+ return false;
8868
+ }
8869
+
8870
+ // Convert month to number
8871
+ const numMonth = Number.parseInt(month, 10);
8872
+
8873
+ // Guard clause: ensure month is a valid integer
8874
+ if (Number.isNaN(numMonth)) {
8875
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
8876
+ }
8877
+
8878
+ // Guard clause: ensure month is within the valid range
8879
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
8880
+ return false;
8881
+ }
8882
+
8883
+ // Default return
8884
+ return true;
8885
+ };
8886
+
8887
+ // Validator for year
8888
+ const yearIsValid = (_year) => {
8889
+
8890
+ // Guard clause: ensure year exists.
8891
+ if (!_year) {
8892
+ return false;
8893
+ }
8894
+
8895
+ // Get the full year
8896
+ const year = this.getFourDigitYear(_year);
8897
+
8898
+ // Convert year to number
8899
+ const numYear = Number.parseInt(year, 10);
8900
+
8901
+ // Guard clause: ensure year is a valid integer
8902
+ if (Number.isNaN(numYear)) {
8903
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
8904
+ }
8905
+
8906
+ // Guard clause: ensure year is within the valid range
8907
+ if (numYear < this.minYear || numYear > this.maxYear) {
8908
+ return false;
8909
+ }
8910
+
8911
+ // Default return
8912
+ return true;
8913
+ };
8914
+
8915
+ // Self-contained checks for month, day, and year
8916
+ const checks = [
8917
+ monthValueIsValid(dateParts.month),
8918
+ dayValueIsValid(dateParts.day),
8919
+ yearIsValid(dateParts.year)
8920
+ ];
8921
+
8922
+ // If any of the checks failed, the date format does not match and the result is invalid
8923
+ const isValid = checks.every((check) => check === true);
8924
+
8925
+ // If the check is invalid, return false
8926
+ if (!isValid) {
8927
+ return false;
8928
+ }
8929
+
8930
+ // Default case
8931
+ return true;
8932
+ };
8933
+ }
8934
+ }
8935
+
8936
+ // Export a class instance
8937
+ const dateUtilities = new AuroDateUtilities();
8938
+
8939
+ // Export the class instance methods individually
8940
+ const {
8941
+ datesMatch,
8942
+ validDateStr,
8943
+ dateAndFormatMatch,
8944
+ minDay,
8945
+ minMonth,
8946
+ minYear,
8947
+ maxDay,
8948
+ maxMonth,
8949
+ maxYear
8950
+ } = dateUtilities;
8951
+
8952
+ const {
8953
+ toNorthAmericanFormat,
8954
+ parseDate,
8955
+ getDateAsString
8956
+ } = dateFormatter;
8957
+
8025
8958
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
8026
8959
  // See LICENSE in the project root for license information.
8027
8960
 
@@ -8097,6 +9030,7 @@ let AuroLibraryRuntimeUtils$1$1 = class AuroLibraryRuntimeUtils {
8097
9030
 
8098
9031
 
8099
9032
  class AuroFormValidation {
9033
+
8100
9034
  constructor() {
8101
9035
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1$1();
8102
9036
  }
@@ -8188,17 +9122,17 @@ class AuroFormValidation {
8188
9122
  ]
8189
9123
  }
8190
9124
  };
8191
-
9125
+
8192
9126
  let elementType;
8193
9127
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
8194
9128
  elementType = 'input';
8195
9129
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
8196
9130
  elementType = 'counter';
8197
9131
  }
8198
-
9132
+
8199
9133
  if (elementType) {
8200
9134
  const rules = validationRules[elementType];
8201
-
9135
+
8202
9136
  if (rules) {
8203
9137
  Object.values(rules).flat().forEach(rule => {
8204
9138
  if (rule.check(elem)) {
@@ -8224,48 +9158,82 @@ class AuroFormValidation {
8224
9158
  if (!elem.value.match(emailRegex)) {
8225
9159
  elem.validity = 'patternMismatch';
8226
9160
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9161
+ return;
8227
9162
  }
8228
9163
  } else if (elem.type === 'credit-card') {
8229
9164
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
8230
9165
  elem.validity = 'tooShort';
8231
9166
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9167
+ return;
8232
9168
  }
8233
9169
  } else if (elem.type === 'number') {
8234
9170
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
8235
9171
  elem.validity = 'rangeOverflow';
8236
9172
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9173
+ return;
8237
9174
  }
8238
9175
 
8239
9176
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
8240
9177
  elem.validity = 'rangeUnderflow';
8241
9178
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9179
+ return;
8242
9180
  }
8243
- } else if (elem.type === 'date') {
8244
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
9181
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
9182
+
9183
+ // Guard Clause: if the value is too short
9184
+ if (elem.value.length < elem.lengthForType) {
9185
+
8245
9186
  elem.validity = 'tooShort';
8246
9187
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
8247
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
8248
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
8249
- const valueDate = new Date(formattedValue.dateForComparison);
9188
+ return;
9189
+ }
9190
+
9191
+ // Guard Clause: If the value is too long for the type
9192
+ if (elem.value?.length > elem.lengthForType) {
8250
9193
 
8251
- // validate max
8252
- if (elem.max?.length === elem.lengthForType) {
8253
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
9194
+ elem.validity = 'tooLong';
9195
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9196
+ return;
9197
+ }
9198
+
9199
+ // Validate that the date passed was the correct format
9200
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
9201
+ elem.validity = 'patternMismatch';
9202
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
9203
+ return;
9204
+ }
9205
+
9206
+ // Validate that the date passed was a valid date
9207
+ if (!validDateStr(elem.value, elem.format)) {
9208
+ elem.validity = 'invalidDate';
9209
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
9210
+ return;
9211
+ }
8254
9212
 
8255
- if (valueDate > maxDate) {
8256
- elem.validity = 'rangeOverflow';
8257
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
8258
- }
9213
+ // Perform the rest of the validation
9214
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
9215
+ const valueDate = new Date(formattedValue);
9216
+
9217
+ // // Validate max date
9218
+ if (elem.max?.length === elem.lengthForType) {
9219
+
9220
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
9221
+
9222
+ if (valueDate > maxDate) {
9223
+ elem.validity = 'rangeOverflow';
9224
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9225
+ return;
8259
9226
  }
9227
+ }
8260
9228
 
8261
- // validate min
8262
- if (elem.min?.length === elem.lengthForType) {
8263
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
9229
+ // Validate min date
9230
+ if (elem.min?.length === elem.lengthForType) {
9231
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
8264
9232
 
8265
- if (valueDate < minDate) {
8266
- elem.validity = 'rangeUnderflow';
8267
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
8268
- }
9233
+ if (valueDate < minDate) {
9234
+ elem.validity = 'rangeUnderflow';
9235
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9236
+ return;
8269
9237
  }
8270
9238
  }
8271
9239
  }
@@ -8384,7 +9352,7 @@ class AuroFormValidation {
8384
9352
  if (input.validationMessage.length > 0) {
8385
9353
  elem.errorMessage = input.validationMessage;
8386
9354
  }
8387
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
9355
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
8388
9356
  const firstInput = this.inputElements[0];
8389
9357
 
8390
9358
  if (firstInput.validationMessage.length > 0) {
@@ -8506,6 +9474,33 @@ class BaseInput extends LitElement {
8506
9474
  static get properties() {
8507
9475
  return {
8508
9476
 
9477
+ /**
9478
+ * The value for the role attribute.
9479
+ */
9480
+ a11yRole: {
9481
+ type: String,
9482
+ attribute: true,
9483
+ reflect: true
9484
+ },
9485
+
9486
+ /**
9487
+ * The value for the aria-expanded attribute.
9488
+ */
9489
+ a11yExpanded: {
9490
+ type: Boolean,
9491
+ attribute: true,
9492
+ reflect: true
9493
+ },
9494
+
9495
+ /**
9496
+ * The value for the aria-controls attribute.
9497
+ */
9498
+ a11yControls: {
9499
+ type: String,
9500
+ attribute: true,
9501
+ reflect: true
9502
+ },
9503
+
8509
9504
  /**
8510
9505
  * If set, the label will remain fixed in the active position.
8511
9506
  */
@@ -9147,6 +10142,10 @@ class BaseInput extends LitElement {
9147
10142
  } else if (this.type === 'number') {
9148
10143
  this.inputMode = 'numeric';
9149
10144
  }
10145
+
10146
+ if (this.type === "date" && !this.format) {
10147
+ this.format = 'mm/dd/yyyy';
10148
+ }
9150
10149
  }
9151
10150
 
9152
10151
  /**
@@ -10390,6 +11389,7 @@ var helpTextVersion = '1.0.0';
10390
11389
 
10391
11390
  // build the component class
10392
11391
  class AuroInput extends BaseInput {
11392
+
10393
11393
  constructor() {
10394
11394
  super();
10395
11395
 
@@ -10502,7 +11502,7 @@ class AuroInput extends BaseInput {
10502
11502
  ?required="${this.required}"
10503
11503
  ?disabled="${this.disabled}"
10504
11504
  aria-describedby="${this.uniqueId}"
10505
- aria-invalid="${this.validity !== 'valid'}"
11505
+ ?aria-invalid="${this.validity !== 'valid'}"
10506
11506
  placeholder=${this.getPlaceholder()}
10507
11507
  lang="${ifDefined(this.lang)}"
10508
11508
  ?activeLabel="${this.activeLabel}"
@@ -10511,7 +11511,10 @@ class AuroInput extends BaseInput {
10511
11511
  autocapitalize="${ifDefined(this.autocapitalize ? this.autocapitalize : undefined)}"
10512
11512
  autocomplete="${ifDefined(this.autocomplete ? this.autocomplete : undefined)}"
10513
11513
  part="input"
10514
- />
11514
+ role="${ifDefined(this.a11yRole)}"
11515
+ aria-expanded="${ifDefined(this.a11yExpanded)}"
11516
+ aria-controls="${ifDefined(this.a11yControls)}"
11517
+ />
10515
11518
  </div>
10516
11519
  <div
10517
11520
  class="notificationIcons"
@@ -11503,6 +12506,7 @@ var styleCss = css`.util_displayInline{display:inline}.util_displayInlineBlock{d
11503
12506
 
11504
12507
  // build the component class
11505
12508
  class AuroCombobox extends LitElement {
12509
+
11506
12510
  constructor() {
11507
12511
  super();
11508
12512
 
@@ -11514,6 +12518,8 @@ class AuroCombobox extends LitElement {
11514
12518
  * @returns {void} Internal defaults.
11515
12519
  */
11516
12520
  privateDefaults() {
12521
+ this.dropdownOpen = false;
12522
+ this.dropdownId = undefined;
11517
12523
  this.onDark = false;
11518
12524
 
11519
12525
  this.noFilter = false;
@@ -11593,6 +12599,26 @@ class AuroCombobox extends LitElement {
11593
12599
  reflect: true
11594
12600
  },
11595
12601
 
12602
+ /**
12603
+ * ID for the dropdown
12604
+ * @private
12605
+ */
12606
+ dropdownId: {
12607
+ type: String,
12608
+ reflect: false,
12609
+ attribute: false
12610
+ },
12611
+
12612
+ /**
12613
+ * Whether or not the dropdown is open
12614
+ * @private
12615
+ */
12616
+ dropdownOpen: {
12617
+ type: Boolean,
12618
+ reflect: false,
12619
+ attribute: false
12620
+ },
12621
+
11596
12622
  /**
11597
12623
  * When defined, sets persistent validity to `customError` and sets the validation message to the attribute value.
11598
12624
  */
@@ -11751,8 +12777,19 @@ class AuroCombobox extends LitElement {
11751
12777
  * @private
11752
12778
  */
11753
12779
  isDropdownFullscreen: {
11754
- type: Boolean
11755
- }
12780
+ type: Boolean,
12781
+ reflect: false
12782
+ },
12783
+
12784
+ /**
12785
+ * @private
12786
+ * specifies the currently active option
12787
+ */
12788
+ optionActive: {
12789
+ type: Object,
12790
+ reflect: false,
12791
+ attribute: false
12792
+ },
11756
12793
  };
11757
12794
  }
11758
12795
 
@@ -11904,6 +12941,18 @@ class AuroCombobox extends LitElement {
11904
12941
  * @returns {void}
11905
12942
  */
11906
12943
  configureDropdown() {
12944
+
12945
+ // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
12946
+ this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
12947
+ this.dropdownId = event.detail.id;
12948
+ });
12949
+
12950
+ // Listen for the dropdown to be shown or hidden
12951
+ this.dropdown.addEventListener("auroDropdown-toggled", (ev) => {
12952
+ this.dropdownOpen = ev.detail.expanded;
12953
+ });
12954
+
12955
+ // this.dropdown.addEventListener('auroDropdown-show', () => {
11907
12956
  this.menuWrapper = this.dropdown.querySelector('.menuWrapper');
11908
12957
  this.menuWrapper.append(this.menu);
11909
12958
 
@@ -11917,7 +12966,6 @@ class AuroCombobox extends LitElement {
11917
12966
  this.hideBib = this.hideBib.bind(this);
11918
12967
  this.bibtemplate.addEventListener('close-click', this.hideBib);
11919
12968
 
11920
- this.dropdown.setAttribute('role', 'combobox');
11921
12969
  this.dropdown.addEventListener('auroDropdown-triggerClick', () => {
11922
12970
  this.showBib();
11923
12971
  });
@@ -11933,7 +12981,6 @@ class AuroCombobox extends LitElement {
11933
12981
  this.isDropdownFullscreen = event.detail.strategy === 'fullscreen';
11934
12982
  setTimeout(this.transportInput);
11935
12983
  });
11936
-
11937
12984
  }
11938
12985
 
11939
12986
  /**
@@ -12427,6 +13474,10 @@ class AuroCombobox extends LitElement {
12427
13474
  ?noFlip="${this.noFlip}"
12428
13475
  disableEventShow>
12429
13476
  <${this.inputTag}
13477
+ .a11yRole="${"combobox"}"
13478
+ .a11yExpanded="${this.dropdownOpen}"
13479
+ .a11yControls="${this.dropdownId}"
13480
+ id="${this.id || 'auro-combobox-input'}"
12430
13481
  slot="trigger"
12431
13482
  bordered
12432
13483
  ?onDark="${this.onDark}"