@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
@@ -306,11 +306,420 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
306
306
  }
307
307
  };
308
308
 
309
+ let DateFormatter$1 = class DateFormatter {
310
+
311
+ constructor() {
312
+
313
+ /**
314
+ * @description Parses a date string into its components.
315
+ * @param {string} dateStr - Date string to parse.
316
+ * @param {string} format - Date format to parse.
317
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
318
+ */
319
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
320
+
321
+ // Guard Clause: Date string is defined
322
+ if (!dateStr) {
323
+ return undefined;
324
+ }
325
+
326
+ // Assume the separator is a "/" a defined in our code base
327
+ const separator = '/';
328
+
329
+ // Get the parts of the date and format
330
+ const valueParts = dateStr.split(separator);
331
+ const formatParts = format.split(separator);
332
+
333
+ // Check if the value and format have the correct number of parts
334
+ if (valueParts.length !== formatParts.length) {
335
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
336
+ }
337
+
338
+ // Holds the result to be returned
339
+ const result = formatParts.reduce((acc, part, index) => {
340
+ const value = valueParts[index];
341
+
342
+ if ((/m/iu).test(part)) {
343
+ acc.month = value;
344
+ } else if ((/d/iu).test(part)) {
345
+ acc.day = value;
346
+ } else if ((/y/iu).test(part)) {
347
+ acc.year = value;
348
+ }
349
+
350
+ return acc;
351
+ }, {});
352
+
353
+ // If we found all the parts, return the result
354
+ if (result.month && result.year) {
355
+ return result;
356
+ }
357
+
358
+ // Throw an error to let the dev know we were unable to parse the date string
359
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
360
+ };
361
+
362
+ /**
363
+ * Convert a date object to string format.
364
+ * @param {Object} date - Date to convert to string.
365
+ * @returns {Object} Returns the date as a string.
366
+ */
367
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
368
+ year: "numeric",
369
+ month: "2-digit",
370
+ day: "2-digit",
371
+ });
372
+
373
+ /**
374
+ * Converts a date string to a North American date format.
375
+ * @param {String} dateStr - Date to validate.
376
+ * @param {String} format - Date format to validate against.
377
+ * @returns {Boolean}
378
+ */
379
+ this.toNorthAmericanFormat = (dateStr, format) => {
380
+
381
+ if (format === 'mm/dd/yyyy') {
382
+ return dateStr;
383
+ }
384
+
385
+ const parsedDate = this.parseDate(dateStr, format);
386
+
387
+ if (!parsedDate) {
388
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
389
+ }
390
+
391
+ const { month, day, year } = parsedDate;
392
+
393
+ const dateParts = [];
394
+ if (month) {
395
+ dateParts.push(month);
396
+ }
397
+
398
+ if (day) {
399
+ dateParts.push(day);
400
+ }
401
+
402
+ if (year) {
403
+ dateParts.push(year);
404
+ }
405
+
406
+ return dateParts.join('/');
407
+ };
408
+ }
409
+ };
410
+ const dateFormatter$1 = new DateFormatter$1();
411
+
412
+ // filepath: dateConstraints.mjs
413
+ const DATE_UTIL_CONSTRAINTS$1 = {
414
+ maxDay: 31,
415
+ maxMonth: 12,
416
+ maxYear: 2400,
417
+ minDay: 1,
418
+ minMonth: 1,
419
+ minYear: 1900,
420
+ };
421
+
422
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
423
+
424
+ /**
425
+ * @description The maximum day value allowed by the various utilities in this class.
426
+ * @readonly
427
+ * @type {Number}
428
+ */
429
+ get maxDay() {
430
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
431
+ }
432
+
433
+ /**
434
+ * @description The maximum month value allowed by the various utilities in this class.
435
+ * @readonly
436
+ * @type {Number}
437
+ */
438
+ get maxMonth() {
439
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
440
+ }
441
+
442
+ /**
443
+ * @description The maximum year value allowed by the various utilities in this class.
444
+ * @readonly
445
+ * @type {Number}
446
+ */
447
+ get maxYear() {
448
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
449
+ }
450
+
451
+ /**
452
+ * @description The minimum day value allowed by the various utilities in this class.
453
+ * @readonly
454
+ * @type {Number}
455
+ */
456
+ get minDay() {
457
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
458
+ }
459
+
460
+ /**
461
+ * @description The minimum month value allowed by the various utilities in this class.
462
+ * @readonly
463
+ * @type {Number}
464
+ */
465
+ get minMonth() {
466
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
467
+ }
468
+
469
+ /**
470
+ * @description The minimum year value allowed by the various utilities in this class.
471
+ * @readonly
472
+ * @type {Number}
473
+ */
474
+ get minYear() {
475
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
476
+ }
477
+ };
478
+
479
+ /* eslint-disable no-magic-numbers */
480
+
481
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
482
+
483
+ /**
484
+ * Returns the current century.
485
+ * @returns {String} The current century.
486
+ */
487
+ getCentury () {
488
+ return String(new Date().getFullYear()).slice(0, 2);
489
+ }
490
+
491
+ /**
492
+ * Returns a four digit year.
493
+ * @param {String} year - The year to convert to four digits.
494
+ * @returns {String} The four digit year.
495
+ */
496
+ getFourDigitYear (year) {
497
+
498
+ const strYear = String(year).trim();
499
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
500
+ }
501
+
502
+ constructor() {
503
+
504
+ super();
505
+
506
+ /**
507
+ * Compares two dates to see if they match.
508
+ * @param {Object} date1 - First date to compare.
509
+ * @param {Object} date2 - Second date to compare.
510
+ * @returns {Boolean} Returns true if the dates match.
511
+ */
512
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
513
+
514
+ /**
515
+ * Returns true if value passed in is a valid date.
516
+ * @param {String} date - Date to validate.
517
+ * @param {String} format - Date format to validate against.
518
+ * @returns {Boolean}
519
+ */
520
+ this.validDateStr = (date, format) => {
521
+
522
+ // The length we expect the date string to be
523
+ const dateStrLength = format.length;
524
+
525
+ // Guard Clause: Date and format are defined
526
+ if (typeof date === "undefined" || typeof format === "undefined") {
527
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
528
+ }
529
+
530
+ // Guard Clause: Date should be of type string
531
+ if (typeof date !== "string") {
532
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
533
+ }
534
+
535
+ // Guard Clause: Format should be of type string
536
+ if (typeof format !== "string") {
537
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
538
+ }
539
+
540
+ // Guard Clause: Length is what we expect it to be
541
+ if (date.length !== dateStrLength) {
542
+ return false;
543
+ }
544
+ // Get a formatted date string and parse it
545
+ const dateParts = dateFormatter$1.parseDate(date, format);
546
+
547
+ // Guard Clause: Date parse succeeded
548
+ if (!dateParts) {
549
+ return false;
550
+ }
551
+
552
+ // Create the expected date string based on the date parts
553
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
554
+
555
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
556
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
557
+
558
+ // Get the date string of the date object we created from the string date
559
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
560
+
561
+ // Guard Clause: Generated date matches date string input
562
+ if (expectedDateStr !== actualDateStr) {
563
+ return false;
564
+ }
565
+
566
+ // If we passed all other checks, we can assume the date is valid
567
+ return true;
568
+ };
569
+
570
+ /**
571
+ * Determines if a string date value matches the format provided.
572
+ * @param {string} value = The date string value.
573
+ * @param { string} format = The date format to match against.
574
+ * @returns {boolean}
575
+ */
576
+ this.dateAndFormatMatch = (value, format) => {
577
+
578
+ // Ensure we have both values we need to do the comparison
579
+ if (!value || !format) {
580
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
581
+ }
582
+
583
+ // If the lengths are different, they cannot match
584
+ if (value.length !== format.length) {
585
+ return false;
586
+ }
587
+
588
+ // Get the parts of the date
589
+ const dateParts = dateFormatter$1.parseDate(value, format);
590
+
591
+ // Validator for day
592
+ const dayValueIsValid = (day) => {
593
+
594
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
595
+ if (!dateParts.day) {
596
+ return true;
597
+ }
598
+
599
+ // Guard clause: ensure day exists.
600
+ if (!day) {
601
+ return false;
602
+ }
603
+
604
+ // Convert day to number
605
+ const numDay = Number.parseInt(day, 10);
606
+
607
+ // Guard clause: ensure day is a valid integer
608
+ if (Number.isNaN(numDay)) {
609
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
610
+ }
611
+
612
+ // Guard clause: ensure day is within the valid range
613
+ if (numDay < this.minDay || numDay > this.maxDay) {
614
+ return false;
615
+ }
616
+
617
+ // Default return
618
+ return true;
619
+ };
620
+
621
+ // Validator for month
622
+ const monthValueIsValid = (month) => {
623
+
624
+ // Guard clause: ensure month exists.
625
+ if (!month) {
626
+ return false;
627
+ }
628
+
629
+ // Convert month to number
630
+ const numMonth = Number.parseInt(month, 10);
631
+
632
+ // Guard clause: ensure month is a valid integer
633
+ if (Number.isNaN(numMonth)) {
634
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
635
+ }
636
+
637
+ // Guard clause: ensure month is within the valid range
638
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
639
+ return false;
640
+ }
641
+
642
+ // Default return
643
+ return true;
644
+ };
645
+
646
+ // Validator for year
647
+ const yearIsValid = (_year) => {
648
+
649
+ // Guard clause: ensure year exists.
650
+ if (!_year) {
651
+ return false;
652
+ }
653
+
654
+ // Get the full year
655
+ const year = this.getFourDigitYear(_year);
656
+
657
+ // Convert year to number
658
+ const numYear = Number.parseInt(year, 10);
659
+
660
+ // Guard clause: ensure year is a valid integer
661
+ if (Number.isNaN(numYear)) {
662
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
663
+ }
664
+
665
+ // Guard clause: ensure year is within the valid range
666
+ if (numYear < this.minYear || numYear > this.maxYear) {
667
+ return false;
668
+ }
669
+
670
+ // Default return
671
+ return true;
672
+ };
673
+
674
+ // Self-contained checks for month, day, and year
675
+ const checks = [
676
+ monthValueIsValid(dateParts.month),
677
+ dayValueIsValid(dateParts.day),
678
+ yearIsValid(dateParts.year)
679
+ ];
680
+
681
+ // If any of the checks failed, the date format does not match and the result is invalid
682
+ const isValid = checks.every((check) => check === true);
683
+
684
+ // If the check is invalid, return false
685
+ if (!isValid) {
686
+ return false;
687
+ }
688
+
689
+ // Default case
690
+ return true;
691
+ };
692
+ }
693
+ };
694
+
695
+ // Export a class instance
696
+ const dateUtilities$1 = new AuroDateUtilities$1();
697
+
698
+ // Export the class instance methods individually
699
+ const {
700
+ datesMatch: datesMatch$1,
701
+ validDateStr: validDateStr$1,
702
+ dateAndFormatMatch: dateAndFormatMatch$1,
703
+ minDay: minDay$1,
704
+ minMonth: minMonth$1,
705
+ minYear: minYear$1,
706
+ maxDay: maxDay$1,
707
+ maxMonth: maxMonth$1,
708
+ maxYear: maxYear$1
709
+ } = dateUtilities$1;
710
+
711
+ const {
712
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
713
+ parseDate: parseDate$1,
714
+ getDateAsString: getDateAsString$1
715
+ } = dateFormatter$1;
716
+
309
717
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
310
718
  // See LICENSE in the project root for license information.
311
719
 
312
720
 
313
721
  let AuroFormValidation$1 = class AuroFormValidation {
722
+
314
723
  constructor() {
315
724
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
316
725
  }
@@ -402,17 +811,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
402
811
  ]
403
812
  }
404
813
  };
405
-
814
+
406
815
  let elementType;
407
816
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
408
817
  elementType = 'input';
409
818
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
410
819
  elementType = 'counter';
411
820
  }
412
-
821
+
413
822
  if (elementType) {
414
823
  const rules = validationRules[elementType];
415
-
824
+
416
825
  if (rules) {
417
826
  Object.values(rules).flat().forEach(rule => {
418
827
  if (rule.check(elem)) {
@@ -438,48 +847,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
438
847
  if (!elem.value.match(emailRegex)) {
439
848
  elem.validity = 'patternMismatch';
440
849
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
850
+ return;
441
851
  }
442
852
  } else if (elem.type === 'credit-card') {
443
853
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
444
854
  elem.validity = 'tooShort';
445
855
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
856
+ return;
446
857
  }
447
858
  } else if (elem.type === 'number') {
448
859
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
449
860
  elem.validity = 'rangeOverflow';
450
861
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
862
+ return;
451
863
  }
452
864
 
453
865
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
454
866
  elem.validity = 'rangeUnderflow';
455
867
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
868
+ return;
456
869
  }
457
- } else if (elem.type === 'date') {
458
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
870
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
871
+
872
+ // Guard Clause: if the value is too short
873
+ if (elem.value.length < elem.lengthForType) {
874
+
459
875
  elem.validity = 'tooShort';
460
876
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
461
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
462
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
463
- const valueDate = new Date(formattedValue.dateForComparison);
877
+ return;
878
+ }
879
+
880
+ // Guard Clause: If the value is too long for the type
881
+ if (elem.value?.length > elem.lengthForType) {
882
+
883
+ elem.validity = 'tooLong';
884
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
885
+ return;
886
+ }
887
+
888
+ // Validate that the date passed was the correct format
889
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
890
+ elem.validity = 'patternMismatch';
891
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
892
+ return;
893
+ }
894
+
895
+ // Validate that the date passed was a valid date
896
+ if (!validDateStr$1(elem.value, elem.format)) {
897
+ elem.validity = 'invalidDate';
898
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
899
+ return;
900
+ }
464
901
 
465
- // validate max
466
- if (elem.max?.length === elem.lengthForType) {
467
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
902
+ // Perform the rest of the validation
903
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
904
+ const valueDate = new Date(formattedValue);
468
905
 
469
- if (valueDate > maxDate) {
470
- elem.validity = 'rangeOverflow';
471
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
472
- }
906
+ // // Validate max date
907
+ if (elem.max?.length === elem.lengthForType) {
908
+
909
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
910
+
911
+ if (valueDate > maxDate) {
912
+ elem.validity = 'rangeOverflow';
913
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
914
+ return;
473
915
  }
916
+ }
474
917
 
475
- // validate min
476
- if (elem.min?.length === elem.lengthForType) {
477
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
918
+ // Validate min date
919
+ if (elem.min?.length === elem.lengthForType) {
920
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
478
921
 
479
- if (valueDate < minDate) {
480
- elem.validity = 'rangeUnderflow';
481
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
482
- }
922
+ if (valueDate < minDate) {
923
+ elem.validity = 'rangeUnderflow';
924
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
925
+ return;
483
926
  }
484
927
  }
485
928
  }
@@ -598,7 +1041,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
598
1041
  if (input.validationMessage.length > 0) {
599
1042
  elem.errorMessage = input.validationMessage;
600
1043
  }
601
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
1044
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
602
1045
  const firstInput = this.inputElements[0];
603
1046
 
604
1047
  if (firstInput.validationMessage.length > 0) {
@@ -2479,7 +2922,7 @@ class AuroFloatingUI {
2479
2922
  /**
2480
2923
  * @private
2481
2924
  * getting called on 'blur' in trigger or `focusin` in document
2482
- *
2925
+ *
2483
2926
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2484
2927
  * This method checks if the currently active element is still within the trigger or bib.
2485
2928
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -2595,7 +3038,7 @@ class AuroFloatingUI {
2595
3038
  // Close any other dropdown that is already open
2596
3039
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2597
3040
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2598
- existedVisibleFloatingUI.isPopoverVisible &&
3041
+ existedVisibleFloatingUI.element.isPopoverVisible &&
2599
3042
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2600
3043
  document.expandedAuroFloater.hideBib();
2601
3044
  }
@@ -2771,7 +3214,7 @@ class AuroFloatingUI {
2771
3214
  this.id = window.crypto.randomUUID();
2772
3215
  this.element.setAttribute('id', this.id);
2773
3216
  }
2774
-
3217
+
2775
3218
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2776
3219
  }
2777
3220
 
@@ -2824,7 +3267,7 @@ class AuroFloatingUI {
2824
3267
  if (this.element.bib) {
2825
3268
  this.element.shadowRoot.append(this.element.bib);
2826
3269
  }
2827
-
3270
+
2828
3271
  // Remove event & keyboard listeners
2829
3272
  if (this.element?.trigger) {
2830
3273
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -3553,6 +3996,7 @@ var helpTextVersion$1 = '1.0.0';
3553
3996
  * @csspart helpText - The helpText content container.
3554
3997
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3555
3998
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3999
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3556
4000
  */
3557
4001
  class AuroDropdown extends r$1 {
3558
4002
  constructor() {
@@ -3598,7 +4042,9 @@ class AuroDropdown extends r$1 {
3598
4042
  this.rounded = false;
3599
4043
  this.tabIndex = 0;
3600
4044
  this.noToggle = false;
4045
+ this.a11yAutocomplete = 'none';
3601
4046
  this.labeled = true;
4047
+ this.a11yRole = 'combobox';
3602
4048
  this.onDark = false;
3603
4049
 
3604
4050
  // floaterConfig
@@ -3734,6 +4180,16 @@ class AuroDropdown extends r$1 {
3734
4180
  type: Number
3735
4181
  },
3736
4182
 
4183
+ /**
4184
+ * The unique ID for the dropdown bib element.
4185
+ * @private
4186
+ */
4187
+ dropdownId: {
4188
+ type: String,
4189
+ reflect: false,
4190
+ attribute: false
4191
+ },
4192
+
3737
4193
  /**
3738
4194
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3739
4195
  */
@@ -3896,6 +4352,23 @@ class AuroDropdown extends r$1 {
3896
4352
  */
3897
4353
  tabIndex: {
3898
4354
  type: Number
4355
+ },
4356
+
4357
+ /**
4358
+ * The value for the role attribute of the trigger element.
4359
+ */
4360
+ a11yRole: {
4361
+ type: String || undefined,
4362
+ attribute: false,
4363
+ reflect: false
4364
+ },
4365
+
4366
+ /**
4367
+ * The value for the aria-autocomplete attribute of the trigger element.
4368
+ */
4369
+ a11yAutocomplete: {
4370
+ type: String,
4371
+ attribute: false,
3899
4372
  }
3900
4373
  };
3901
4374
  }
@@ -3946,7 +4419,22 @@ class AuroDropdown extends r$1 {
3946
4419
  }
3947
4420
 
3948
4421
  firstUpdated() {
4422
+
4423
+ // Configure the floater to, this will generate the ID for the bib
3949
4424
  this.floater.configure(this, 'auroDropdown');
4425
+
4426
+ /**
4427
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4428
+ * @event auroDropdown-idAdded
4429
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4430
+ */
4431
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4432
+
4433
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4434
+ if (!this.triggerContentFocusable) {
4435
+ this.dropdownId = this.floater.element.id;
4436
+ }
4437
+
3950
4438
  this.bibContent = this.floater.element.bib;
3951
4439
 
3952
4440
  // Add the tag name as an attribute if it is different than the component name
@@ -4098,6 +4586,30 @@ class AuroDropdown extends r$1 {
4098
4586
  });
4099
4587
  }
4100
4588
 
4589
+ /*
4590
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4591
+ * @private
4592
+ * @method setTriggerAriaAttributes
4593
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4594
+ */
4595
+ clearTriggerA11yAttributes(triggerElement) {
4596
+
4597
+ if (!triggerElement || !triggerElement.removeAttribute) {
4598
+ return;
4599
+ }
4600
+
4601
+ // Reset appropriate attributes for a11y
4602
+ triggerElement.removeAttribute('aria-labelledby');
4603
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4604
+ triggerElement.removeAttribute('id');
4605
+ }
4606
+ triggerElement.removeAttribute('role');
4607
+ triggerElement.removeAttribute('aria-expanded');
4608
+
4609
+ triggerElement.removeAttribute('aria-controls');
4610
+ triggerElement.removeAttribute('aria-autocomplete');
4611
+ }
4612
+
4101
4613
  /**
4102
4614
  * Handles changes to the trigger content slot and updates related properties.
4103
4615
  *
@@ -4111,32 +4623,41 @@ class AuroDropdown extends r$1 {
4111
4623
  * @returns {void}
4112
4624
  */
4113
4625
  handleTriggerContentSlotChange(event) {
4626
+
4114
4627
  this.floater.handleTriggerTabIndex();
4115
4628
 
4629
+ // Get the trigger
4630
+ const trigger = this.shadowRoot.querySelector('#trigger');
4631
+
4632
+ // Get the trigger slot
4116
4633
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
4117
4634
 
4635
+ // If there's a trigger slot
4118
4636
  if (triggerSlot) {
4119
4637
 
4638
+ // Get the content nodes to see if there are any children
4120
4639
  const triggerContentNodes = triggerSlot.assignedNodes();
4121
4640
 
4641
+ // If there are children
4122
4642
  if (triggerContentNodes) {
4123
4643
 
4124
- triggerContentNodes.forEach((node) => {
4125
- if (!this.triggerContentFocusable) {
4126
- this.triggerContentFocusable = this.containsFocusableElement(node);
4127
- }
4128
- });
4129
- }
4130
- }
4644
+ // See if any of them are focusable elemeents
4645
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
4131
4646
 
4132
- const trigger = this.shadowRoot.querySelector('#trigger');
4647
+ // If any of them are focusable elements
4648
+ if (this.triggerContentFocusable) {
4133
4649
 
4134
- if (!this.triggerContentFocusable) {
4135
- trigger.setAttribute('tabindex', '0');
4136
- trigger.setAttribute('role', 'button');
4137
- } else {
4138
- trigger.removeAttribute('tabindex');
4139
- trigger.removeAttribute('role');
4650
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4651
+ this.clearTriggerA11yAttributes(trigger);
4652
+
4653
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4654
+ trigger.removeAttribute('tabindex');
4655
+ } else {
4656
+
4657
+ // Add the tabindex to the trigger so that it's in the focus flow
4658
+ trigger.setAttribute('tabindex', '0');
4659
+ }
4660
+ }
4140
4661
  }
4141
4662
 
4142
4663
  if (event) {
@@ -4146,6 +4667,7 @@ class AuroDropdown extends r$1 {
4146
4667
 
4147
4668
  if (this.triggerContentSlot) {
4148
4669
  this.setupTriggerFocusEventBinding();
4670
+
4149
4671
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
4150
4672
  if (slot.textContent.trim()) {
4151
4673
  return true;
@@ -4213,10 +4735,13 @@ class AuroDropdown extends r$1 {
4213
4735
  id="trigger"
4214
4736
  class="trigger"
4215
4737
  part="trigger"
4216
- aria-labelledby="triggerLabel"
4217
4738
  tabindex="${this.tabIndex}"
4218
4739
  ?showBorder="${this.showTriggerBorders}"
4219
- >
4740
+ role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4741
+ aria-expanded="${o(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4742
+ aria-controls="${o(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4743
+ aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4744
+ >
4220
4745
  <div class="triggerContentWrapper">
4221
4746
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4222
4747
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4250,12 +4775,12 @@ class AuroDropdown extends r$1 {
4250
4775
  <div id="bibSizer" part="size"></div>
4251
4776
  <${this.dropdownBibTag}
4252
4777
  id="bib"
4253
- role="tooltip"
4254
4778
  ?data-show="${this.isPopoverVisible}"
4255
4779
  ?isfullscreen="${this.isBibFullscreen}"
4256
4780
  ?common="${this.common}"
4257
4781
  ?rounded="${this.common || this.rounded}"
4258
- ?inset="${this.common || this.inset}">
4782
+ ?inset="${this.common || this.inset}"
4783
+ >
4259
4784
  </${this.dropdownBibTag}>
4260
4785
  </div>
4261
4786
  `;
@@ -8246,6 +8771,414 @@ class AuroInputUtilities {
8246
8771
  }
8247
8772
  }
8248
8773
 
8774
+ class DateFormatter {
8775
+
8776
+ constructor() {
8777
+
8778
+ /**
8779
+ * @description Parses a date string into its components.
8780
+ * @param {string} dateStr - Date string to parse.
8781
+ * @param {string} format - Date format to parse.
8782
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
8783
+ */
8784
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
8785
+
8786
+ // Guard Clause: Date string is defined
8787
+ if (!dateStr) {
8788
+ return undefined;
8789
+ }
8790
+
8791
+ // Assume the separator is a "/" a defined in our code base
8792
+ const separator = '/';
8793
+
8794
+ // Get the parts of the date and format
8795
+ const valueParts = dateStr.split(separator);
8796
+ const formatParts = format.split(separator);
8797
+
8798
+ // Check if the value and format have the correct number of parts
8799
+ if (valueParts.length !== formatParts.length) {
8800
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
8801
+ }
8802
+
8803
+ // Holds the result to be returned
8804
+ const result = formatParts.reduce((acc, part, index) => {
8805
+ const value = valueParts[index];
8806
+
8807
+ if ((/m/iu).test(part)) {
8808
+ acc.month = value;
8809
+ } else if ((/d/iu).test(part)) {
8810
+ acc.day = value;
8811
+ } else if ((/y/iu).test(part)) {
8812
+ acc.year = value;
8813
+ }
8814
+
8815
+ return acc;
8816
+ }, {});
8817
+
8818
+ // If we found all the parts, return the result
8819
+ if (result.month && result.year) {
8820
+ return result;
8821
+ }
8822
+
8823
+ // Throw an error to let the dev know we were unable to parse the date string
8824
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
8825
+ };
8826
+
8827
+ /**
8828
+ * Convert a date object to string format.
8829
+ * @param {Object} date - Date to convert to string.
8830
+ * @returns {Object} Returns the date as a string.
8831
+ */
8832
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
8833
+ year: "numeric",
8834
+ month: "2-digit",
8835
+ day: "2-digit",
8836
+ });
8837
+
8838
+ /**
8839
+ * Converts a date string to a North American date format.
8840
+ * @param {String} dateStr - Date to validate.
8841
+ * @param {String} format - Date format to validate against.
8842
+ * @returns {Boolean}
8843
+ */
8844
+ this.toNorthAmericanFormat = (dateStr, format) => {
8845
+
8846
+ if (format === 'mm/dd/yyyy') {
8847
+ return dateStr;
8848
+ }
8849
+
8850
+ const parsedDate = this.parseDate(dateStr, format);
8851
+
8852
+ if (!parsedDate) {
8853
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
8854
+ }
8855
+
8856
+ const { month, day, year } = parsedDate;
8857
+
8858
+ const dateParts = [];
8859
+ if (month) {
8860
+ dateParts.push(month);
8861
+ }
8862
+
8863
+ if (day) {
8864
+ dateParts.push(day);
8865
+ }
8866
+
8867
+ if (year) {
8868
+ dateParts.push(year);
8869
+ }
8870
+
8871
+ return dateParts.join('/');
8872
+ };
8873
+ }
8874
+ }
8875
+ const dateFormatter = new DateFormatter();
8876
+
8877
+ // filepath: dateConstraints.mjs
8878
+ const DATE_UTIL_CONSTRAINTS = {
8879
+ maxDay: 31,
8880
+ maxMonth: 12,
8881
+ maxYear: 2400,
8882
+ minDay: 1,
8883
+ minMonth: 1,
8884
+ minYear: 1900,
8885
+ };
8886
+
8887
+ class AuroDateUtilitiesBase {
8888
+
8889
+ /**
8890
+ * @description The maximum day value allowed by the various utilities in this class.
8891
+ * @readonly
8892
+ * @type {Number}
8893
+ */
8894
+ get maxDay() {
8895
+ return DATE_UTIL_CONSTRAINTS.maxDay;
8896
+ }
8897
+
8898
+ /**
8899
+ * @description The maximum month value allowed by the various utilities in this class.
8900
+ * @readonly
8901
+ * @type {Number}
8902
+ */
8903
+ get maxMonth() {
8904
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
8905
+ }
8906
+
8907
+ /**
8908
+ * @description The maximum year value allowed by the various utilities in this class.
8909
+ * @readonly
8910
+ * @type {Number}
8911
+ */
8912
+ get maxYear() {
8913
+ return DATE_UTIL_CONSTRAINTS.maxYear;
8914
+ }
8915
+
8916
+ /**
8917
+ * @description The minimum day value allowed by the various utilities in this class.
8918
+ * @readonly
8919
+ * @type {Number}
8920
+ */
8921
+ get minDay() {
8922
+ return DATE_UTIL_CONSTRAINTS.minDay;
8923
+ }
8924
+
8925
+ /**
8926
+ * @description The minimum month value allowed by the various utilities in this class.
8927
+ * @readonly
8928
+ * @type {Number}
8929
+ */
8930
+ get minMonth() {
8931
+ return DATE_UTIL_CONSTRAINTS.minMonth;
8932
+ }
8933
+
8934
+ /**
8935
+ * @description The minimum year value allowed by the various utilities in this class.
8936
+ * @readonly
8937
+ * @type {Number}
8938
+ */
8939
+ get minYear() {
8940
+ return DATE_UTIL_CONSTRAINTS.minYear;
8941
+ }
8942
+ }
8943
+
8944
+ /* eslint-disable no-magic-numbers */
8945
+
8946
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
8947
+
8948
+ /**
8949
+ * Returns the current century.
8950
+ * @returns {String} The current century.
8951
+ */
8952
+ getCentury () {
8953
+ return String(new Date().getFullYear()).slice(0, 2);
8954
+ }
8955
+
8956
+ /**
8957
+ * Returns a four digit year.
8958
+ * @param {String} year - The year to convert to four digits.
8959
+ * @returns {String} The four digit year.
8960
+ */
8961
+ getFourDigitYear (year) {
8962
+
8963
+ const strYear = String(year).trim();
8964
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
8965
+ }
8966
+
8967
+ constructor() {
8968
+
8969
+ super();
8970
+
8971
+ /**
8972
+ * Compares two dates to see if they match.
8973
+ * @param {Object} date1 - First date to compare.
8974
+ * @param {Object} date2 - Second date to compare.
8975
+ * @returns {Boolean} Returns true if the dates match.
8976
+ */
8977
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
8978
+
8979
+ /**
8980
+ * Returns true if value passed in is a valid date.
8981
+ * @param {String} date - Date to validate.
8982
+ * @param {String} format - Date format to validate against.
8983
+ * @returns {Boolean}
8984
+ */
8985
+ this.validDateStr = (date, format) => {
8986
+
8987
+ // The length we expect the date string to be
8988
+ const dateStrLength = format.length;
8989
+
8990
+ // Guard Clause: Date and format are defined
8991
+ if (typeof date === "undefined" || typeof format === "undefined") {
8992
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
8993
+ }
8994
+
8995
+ // Guard Clause: Date should be of type string
8996
+ if (typeof date !== "string") {
8997
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
8998
+ }
8999
+
9000
+ // Guard Clause: Format should be of type string
9001
+ if (typeof format !== "string") {
9002
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
9003
+ }
9004
+
9005
+ // Guard Clause: Length is what we expect it to be
9006
+ if (date.length !== dateStrLength) {
9007
+ return false;
9008
+ }
9009
+ // Get a formatted date string and parse it
9010
+ const dateParts = dateFormatter.parseDate(date, format);
9011
+
9012
+ // Guard Clause: Date parse succeeded
9013
+ if (!dateParts) {
9014
+ return false;
9015
+ }
9016
+
9017
+ // Create the expected date string based on the date parts
9018
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
9019
+
9020
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
9021
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
9022
+
9023
+ // Get the date string of the date object we created from the string date
9024
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
9025
+
9026
+ // Guard Clause: Generated date matches date string input
9027
+ if (expectedDateStr !== actualDateStr) {
9028
+ return false;
9029
+ }
9030
+
9031
+ // If we passed all other checks, we can assume the date is valid
9032
+ return true;
9033
+ };
9034
+
9035
+ /**
9036
+ * Determines if a string date value matches the format provided.
9037
+ * @param {string} value = The date string value.
9038
+ * @param { string} format = The date format to match against.
9039
+ * @returns {boolean}
9040
+ */
9041
+ this.dateAndFormatMatch = (value, format) => {
9042
+
9043
+ // Ensure we have both values we need to do the comparison
9044
+ if (!value || !format) {
9045
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
9046
+ }
9047
+
9048
+ // If the lengths are different, they cannot match
9049
+ if (value.length !== format.length) {
9050
+ return false;
9051
+ }
9052
+
9053
+ // Get the parts of the date
9054
+ const dateParts = dateFormatter.parseDate(value, format);
9055
+
9056
+ // Validator for day
9057
+ const dayValueIsValid = (day) => {
9058
+
9059
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
9060
+ if (!dateParts.day) {
9061
+ return true;
9062
+ }
9063
+
9064
+ // Guard clause: ensure day exists.
9065
+ if (!day) {
9066
+ return false;
9067
+ }
9068
+
9069
+ // Convert day to number
9070
+ const numDay = Number.parseInt(day, 10);
9071
+
9072
+ // Guard clause: ensure day is a valid integer
9073
+ if (Number.isNaN(numDay)) {
9074
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
9075
+ }
9076
+
9077
+ // Guard clause: ensure day is within the valid range
9078
+ if (numDay < this.minDay || numDay > this.maxDay) {
9079
+ return false;
9080
+ }
9081
+
9082
+ // Default return
9083
+ return true;
9084
+ };
9085
+
9086
+ // Validator for month
9087
+ const monthValueIsValid = (month) => {
9088
+
9089
+ // Guard clause: ensure month exists.
9090
+ if (!month) {
9091
+ return false;
9092
+ }
9093
+
9094
+ // Convert month to number
9095
+ const numMonth = Number.parseInt(month, 10);
9096
+
9097
+ // Guard clause: ensure month is a valid integer
9098
+ if (Number.isNaN(numMonth)) {
9099
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
9100
+ }
9101
+
9102
+ // Guard clause: ensure month is within the valid range
9103
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
9104
+ return false;
9105
+ }
9106
+
9107
+ // Default return
9108
+ return true;
9109
+ };
9110
+
9111
+ // Validator for year
9112
+ const yearIsValid = (_year) => {
9113
+
9114
+ // Guard clause: ensure year exists.
9115
+ if (!_year) {
9116
+ return false;
9117
+ }
9118
+
9119
+ // Get the full year
9120
+ const year = this.getFourDigitYear(_year);
9121
+
9122
+ // Convert year to number
9123
+ const numYear = Number.parseInt(year, 10);
9124
+
9125
+ // Guard clause: ensure year is a valid integer
9126
+ if (Number.isNaN(numYear)) {
9127
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
9128
+ }
9129
+
9130
+ // Guard clause: ensure year is within the valid range
9131
+ if (numYear < this.minYear || numYear > this.maxYear) {
9132
+ return false;
9133
+ }
9134
+
9135
+ // Default return
9136
+ return true;
9137
+ };
9138
+
9139
+ // Self-contained checks for month, day, and year
9140
+ const checks = [
9141
+ monthValueIsValid(dateParts.month),
9142
+ dayValueIsValid(dateParts.day),
9143
+ yearIsValid(dateParts.year)
9144
+ ];
9145
+
9146
+ // If any of the checks failed, the date format does not match and the result is invalid
9147
+ const isValid = checks.every((check) => check === true);
9148
+
9149
+ // If the check is invalid, return false
9150
+ if (!isValid) {
9151
+ return false;
9152
+ }
9153
+
9154
+ // Default case
9155
+ return true;
9156
+ };
9157
+ }
9158
+ }
9159
+
9160
+ // Export a class instance
9161
+ const dateUtilities = new AuroDateUtilities();
9162
+
9163
+ // Export the class instance methods individually
9164
+ const {
9165
+ datesMatch,
9166
+ validDateStr,
9167
+ dateAndFormatMatch,
9168
+ minDay,
9169
+ minMonth,
9170
+ minYear,
9171
+ maxDay,
9172
+ maxMonth,
9173
+ maxYear
9174
+ } = dateUtilities;
9175
+
9176
+ const {
9177
+ toNorthAmericanFormat,
9178
+ parseDate,
9179
+ getDateAsString
9180
+ } = dateFormatter;
9181
+
8249
9182
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
8250
9183
  // See LICENSE in the project root for license information.
8251
9184
 
@@ -8321,6 +9254,7 @@ let AuroLibraryRuntimeUtils$1$1 = class AuroLibraryRuntimeUtils {
8321
9254
 
8322
9255
 
8323
9256
  class AuroFormValidation {
9257
+
8324
9258
  constructor() {
8325
9259
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1$1();
8326
9260
  }
@@ -8412,17 +9346,17 @@ class AuroFormValidation {
8412
9346
  ]
8413
9347
  }
8414
9348
  };
8415
-
9349
+
8416
9350
  let elementType;
8417
9351
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
8418
9352
  elementType = 'input';
8419
9353
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
8420
9354
  elementType = 'counter';
8421
9355
  }
8422
-
9356
+
8423
9357
  if (elementType) {
8424
9358
  const rules = validationRules[elementType];
8425
-
9359
+
8426
9360
  if (rules) {
8427
9361
  Object.values(rules).flat().forEach(rule => {
8428
9362
  if (rule.check(elem)) {
@@ -8448,48 +9382,82 @@ class AuroFormValidation {
8448
9382
  if (!elem.value.match(emailRegex)) {
8449
9383
  elem.validity = 'patternMismatch';
8450
9384
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9385
+ return;
8451
9386
  }
8452
9387
  } else if (elem.type === 'credit-card') {
8453
9388
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
8454
9389
  elem.validity = 'tooShort';
8455
9390
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9391
+ return;
8456
9392
  }
8457
9393
  } else if (elem.type === 'number') {
8458
9394
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
8459
9395
  elem.validity = 'rangeOverflow';
8460
9396
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9397
+ return;
8461
9398
  }
8462
9399
 
8463
9400
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
8464
9401
  elem.validity = 'rangeUnderflow';
8465
9402
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9403
+ return;
8466
9404
  }
8467
- } else if (elem.type === 'date') {
8468
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
9405
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
9406
+
9407
+ // Guard Clause: if the value is too short
9408
+ if (elem.value.length < elem.lengthForType) {
9409
+
8469
9410
  elem.validity = 'tooShort';
8470
9411
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
8471
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
8472
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
8473
- const valueDate = new Date(formattedValue.dateForComparison);
9412
+ return;
9413
+ }
9414
+
9415
+ // Guard Clause: If the value is too long for the type
9416
+ if (elem.value?.length > elem.lengthForType) {
9417
+
9418
+ elem.validity = 'tooLong';
9419
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9420
+ return;
9421
+ }
9422
+
9423
+ // Validate that the date passed was the correct format
9424
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
9425
+ elem.validity = 'patternMismatch';
9426
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
9427
+ return;
9428
+ }
9429
+
9430
+ // Validate that the date passed was a valid date
9431
+ if (!validDateStr(elem.value, elem.format)) {
9432
+ elem.validity = 'invalidDate';
9433
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
9434
+ return;
9435
+ }
8474
9436
 
8475
- // validate max
8476
- if (elem.max?.length === elem.lengthForType) {
8477
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
9437
+ // Perform the rest of the validation
9438
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
9439
+ const valueDate = new Date(formattedValue);
8478
9440
 
8479
- if (valueDate > maxDate) {
8480
- elem.validity = 'rangeOverflow';
8481
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
8482
- }
9441
+ // // Validate max date
9442
+ if (elem.max?.length === elem.lengthForType) {
9443
+
9444
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
9445
+
9446
+ if (valueDate > maxDate) {
9447
+ elem.validity = 'rangeOverflow';
9448
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9449
+ return;
8483
9450
  }
9451
+ }
8484
9452
 
8485
- // validate min
8486
- if (elem.min?.length === elem.lengthForType) {
8487
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
9453
+ // Validate min date
9454
+ if (elem.min?.length === elem.lengthForType) {
9455
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
8488
9456
 
8489
- if (valueDate < minDate) {
8490
- elem.validity = 'rangeUnderflow';
8491
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
8492
- }
9457
+ if (valueDate < minDate) {
9458
+ elem.validity = 'rangeUnderflow';
9459
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9460
+ return;
8493
9461
  }
8494
9462
  }
8495
9463
  }
@@ -8608,7 +9576,7 @@ class AuroFormValidation {
8608
9576
  if (input.validationMessage.length > 0) {
8609
9577
  elem.errorMessage = input.validationMessage;
8610
9578
  }
8611
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
9579
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
8612
9580
  const firstInput = this.inputElements[0];
8613
9581
 
8614
9582
  if (firstInput.validationMessage.length > 0) {
@@ -8730,6 +9698,33 @@ class BaseInput extends r$1 {
8730
9698
  static get properties() {
8731
9699
  return {
8732
9700
 
9701
+ /**
9702
+ * The value for the role attribute.
9703
+ */
9704
+ a11yRole: {
9705
+ type: String,
9706
+ attribute: true,
9707
+ reflect: true
9708
+ },
9709
+
9710
+ /**
9711
+ * The value for the aria-expanded attribute.
9712
+ */
9713
+ a11yExpanded: {
9714
+ type: Boolean,
9715
+ attribute: true,
9716
+ reflect: true
9717
+ },
9718
+
9719
+ /**
9720
+ * The value for the aria-controls attribute.
9721
+ */
9722
+ a11yControls: {
9723
+ type: String,
9724
+ attribute: true,
9725
+ reflect: true
9726
+ },
9727
+
8733
9728
  /**
8734
9729
  * If set, the label will remain fixed in the active position.
8735
9730
  */
@@ -9371,6 +10366,10 @@ class BaseInput extends r$1 {
9371
10366
  } else if (this.type === 'number') {
9372
10367
  this.inputMode = 'numeric';
9373
10368
  }
10369
+
10370
+ if (this.type === "date" && !this.format) {
10371
+ this.format = 'mm/dd/yyyy';
10372
+ }
9374
10373
  }
9375
10374
 
9376
10375
  /**
@@ -10614,6 +11613,7 @@ var helpTextVersion = '1.0.0';
10614
11613
 
10615
11614
  // build the component class
10616
11615
  class AuroInput extends BaseInput {
11616
+
10617
11617
  constructor() {
10618
11618
  super();
10619
11619
 
@@ -10726,7 +11726,7 @@ class AuroInput extends BaseInput {
10726
11726
  ?required="${this.required}"
10727
11727
  ?disabled="${this.disabled}"
10728
11728
  aria-describedby="${this.uniqueId}"
10729
- aria-invalid="${this.validity !== 'valid'}"
11729
+ ?aria-invalid="${this.validity !== 'valid'}"
10730
11730
  placeholder=${this.getPlaceholder()}
10731
11731
  lang="${o(this.lang)}"
10732
11732
  ?activeLabel="${this.activeLabel}"
@@ -10735,7 +11735,10 @@ class AuroInput extends BaseInput {
10735
11735
  autocapitalize="${o(this.autocapitalize ? this.autocapitalize : undefined)}"
10736
11736
  autocomplete="${o(this.autocomplete ? this.autocomplete : undefined)}"
10737
11737
  part="input"
10738
- />
11738
+ role="${o(this.a11yRole)}"
11739
+ aria-expanded="${o(this.a11yExpanded)}"
11740
+ aria-controls="${o(this.a11yControls)}"
11741
+ />
10739
11742
  </div>
10740
11743
  <div
10741
11744
  class="notificationIcons"
@@ -11727,6 +12730,7 @@ var styleCss$3 = i$5`.util_displayInline{display:inline}.util_displayInlineBlock
11727
12730
 
11728
12731
  // build the component class
11729
12732
  class AuroCombobox extends r$1 {
12733
+
11730
12734
  constructor() {
11731
12735
  super();
11732
12736
 
@@ -11738,6 +12742,8 @@ class AuroCombobox extends r$1 {
11738
12742
  * @returns {void} Internal defaults.
11739
12743
  */
11740
12744
  privateDefaults() {
12745
+ this.dropdownOpen = false;
12746
+ this.dropdownId = undefined;
11741
12747
  this.onDark = false;
11742
12748
 
11743
12749
  this.noFilter = false;
@@ -11817,6 +12823,26 @@ class AuroCombobox extends r$1 {
11817
12823
  reflect: true
11818
12824
  },
11819
12825
 
12826
+ /**
12827
+ * ID for the dropdown
12828
+ * @private
12829
+ */
12830
+ dropdownId: {
12831
+ type: String,
12832
+ reflect: false,
12833
+ attribute: false
12834
+ },
12835
+
12836
+ /**
12837
+ * Whether or not the dropdown is open
12838
+ * @private
12839
+ */
12840
+ dropdownOpen: {
12841
+ type: Boolean,
12842
+ reflect: false,
12843
+ attribute: false
12844
+ },
12845
+
11820
12846
  /**
11821
12847
  * When defined, sets persistent validity to `customError` and sets the validation message to the attribute value.
11822
12848
  */
@@ -11975,8 +13001,19 @@ class AuroCombobox extends r$1 {
11975
13001
  * @private
11976
13002
  */
11977
13003
  isDropdownFullscreen: {
11978
- type: Boolean
11979
- }
13004
+ type: Boolean,
13005
+ reflect: false
13006
+ },
13007
+
13008
+ /**
13009
+ * @private
13010
+ * specifies the currently active option
13011
+ */
13012
+ optionActive: {
13013
+ type: Object,
13014
+ reflect: false,
13015
+ attribute: false
13016
+ },
11980
13017
  };
11981
13018
  }
11982
13019
 
@@ -12128,6 +13165,18 @@ class AuroCombobox extends r$1 {
12128
13165
  * @returns {void}
12129
13166
  */
12130
13167
  configureDropdown() {
13168
+
13169
+ // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
13170
+ this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
13171
+ this.dropdownId = event.detail.id;
13172
+ });
13173
+
13174
+ // Listen for the dropdown to be shown or hidden
13175
+ this.dropdown.addEventListener("auroDropdown-toggled", (ev) => {
13176
+ this.dropdownOpen = ev.detail.expanded;
13177
+ });
13178
+
13179
+ // this.dropdown.addEventListener('auroDropdown-show', () => {
12131
13180
  this.menuWrapper = this.dropdown.querySelector('.menuWrapper');
12132
13181
  this.menuWrapper.append(this.menu);
12133
13182
 
@@ -12141,7 +13190,6 @@ class AuroCombobox extends r$1 {
12141
13190
  this.hideBib = this.hideBib.bind(this);
12142
13191
  this.bibtemplate.addEventListener('close-click', this.hideBib);
12143
13192
 
12144
- this.dropdown.setAttribute('role', 'combobox');
12145
13193
  this.dropdown.addEventListener('auroDropdown-triggerClick', () => {
12146
13194
  this.showBib();
12147
13195
  });
@@ -12157,7 +13205,6 @@ class AuroCombobox extends r$1 {
12157
13205
  this.isDropdownFullscreen = event.detail.strategy === 'fullscreen';
12158
13206
  setTimeout(this.transportInput);
12159
13207
  });
12160
-
12161
13208
  }
12162
13209
 
12163
13210
  /**
@@ -12651,6 +13698,10 @@ class AuroCombobox extends r$1 {
12651
13698
  ?noFlip="${this.noFlip}"
12652
13699
  disableEventShow>
12653
13700
  <${this.inputTag}
13701
+ .a11yRole="${"combobox"}"
13702
+ .a11yExpanded="${this.dropdownOpen}"
13703
+ .a11yControls="${this.dropdownId}"
13704
+ id="${this.id || 'auro-combobox-input'}"
12654
13705
  slot="trigger"
12655
13706
  bordered
12656
13707
  ?onDark="${this.onDark}"