@aurodesignsystem/auro-formkit 3.1.0-beta.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 (49) hide show
  1. package/CHANGELOG.md +9 -3
  2. package/components/checkbox/demo/api.min.js +468 -25
  3. package/components/checkbox/demo/index.min.js +468 -25
  4. package/components/checkbox/dist/index.js +468 -25
  5. package/components/checkbox/dist/registered.js +468 -25
  6. package/components/combobox/demo/api.md +1 -1
  7. package/components/combobox/demo/api.min.js +1265 -235
  8. package/components/combobox/demo/index.min.js +1265 -235
  9. package/components/combobox/dist/auro-combobox.d.ts +32 -5
  10. package/components/combobox/dist/index.js +1130 -100
  11. package/components/combobox/dist/registered.js +1130 -100
  12. package/components/counter/demo/api.md +1 -1
  13. package/components/counter/demo/api.min.js +575 -71
  14. package/components/counter/demo/index.min.js +575 -71
  15. package/components/counter/dist/auro-counter-group.d.ts +2 -5
  16. package/components/counter/dist/index.js +575 -71
  17. package/components/counter/dist/registered.js +575 -71
  18. package/components/datepicker/demo/api.md +0 -1
  19. package/components/datepicker/demo/api.min.js +1077 -106
  20. package/components/datepicker/demo/index.min.js +1077 -106
  21. package/components/datepicker/dist/auro-datepicker.d.ts +0 -13
  22. package/components/datepicker/dist/index.js +1077 -106
  23. package/components/datepicker/dist/registered.js +1077 -106
  24. package/components/dropdown/demo/api.md +9 -6
  25. package/components/dropdown/demo/api.min.js +107 -43
  26. package/components/dropdown/demo/index.md +0 -83
  27. package/components/dropdown/demo/index.min.js +107 -43
  28. package/components/dropdown/dist/auro-dropdown.d.ts +30 -12
  29. package/components/dropdown/dist/index.js +107 -43
  30. package/components/dropdown/dist/registered.js +107 -43
  31. package/components/input/demo/api.md +4 -1
  32. package/components/input/demo/api.min.js +503 -25
  33. package/components/input/demo/index.min.js +503 -25
  34. package/components/input/dist/base-input.d.ts +24 -0
  35. package/components/input/dist/index.js +503 -25
  36. package/components/input/dist/registered.js +503 -25
  37. package/components/radio/demo/api.min.js +468 -25
  38. package/components/radio/demo/index.min.js +468 -25
  39. package/components/radio/dist/index.js +468 -25
  40. package/components/radio/dist/registered.js +468 -25
  41. package/components/select/demo/api.md +1 -1
  42. package/components/select/demo/api.min.js +575 -71
  43. package/components/select/demo/index.md +1 -46
  44. package/components/select/demo/index.min.js +575 -71
  45. package/components/select/dist/auro-select.d.ts +2 -5
  46. package/components/select/dist/index.js +575 -71
  47. package/components/select/dist/registered.js +575 -71
  48. package/package.json +2 -2
  49. package/components/form/demo/autocomplete.html +0 -15
@@ -290,6 +290,414 @@ const t$2=globalThis,i$3=t$2.trustedTypes,s$2=i$3?i$3.createPolicy("lit-html",{c
290
290
  */
291
291
  const a=Symbol.for(""),o$2=t=>{if(t?.r===a)return t?._$litStatic$},s$1=t=>({_$litStatic$:t,r:a}),i$1=(t,...r)=>({_$litStatic$:r.reduce(((r,e,a)=>r+(t=>{if(void 0!==t._$litStatic$)return t._$litStatic$;throw Error(`Value passed to 'literal' function must be a 'literal' result: ${t}. Use 'unsafeStatic' to pass non-literal values, but\n take care to ensure page security.`)})(e)+t[a+1]),t[0]),r:a}),l=new Map,n$1=t=>(r,...e)=>{const a=e.length;let s,i;const n=[],u=[];let c,$=0,f=false;for(;$<a;){for(c=r[$];$<a&&void 0!==(i=e[$],s=o$2(i));)c+=s+r[++$],f=true;$!==a&&u.push(i),n.push(c),$++;}if($===a&&n.push(r[a]),f){const t=n.join("$$lit$$");void 0===(r=l.get(t))&&(n.raw=n,l.set(t,r=n)),e=u;}return t(r,...e)},u$2=n$1(x);
292
292
 
293
+ let DateFormatter$1 = class DateFormatter {
294
+
295
+ constructor() {
296
+
297
+ /**
298
+ * @description Parses a date string into its components.
299
+ * @param {string} dateStr - Date string to parse.
300
+ * @param {string} format - Date format to parse.
301
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
302
+ */
303
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
304
+
305
+ // Guard Clause: Date string is defined
306
+ if (!dateStr) {
307
+ return undefined;
308
+ }
309
+
310
+ // Assume the separator is a "/" a defined in our code base
311
+ const separator = '/';
312
+
313
+ // Get the parts of the date and format
314
+ const valueParts = dateStr.split(separator);
315
+ const formatParts = format.split(separator);
316
+
317
+ // Check if the value and format have the correct number of parts
318
+ if (valueParts.length !== formatParts.length) {
319
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
320
+ }
321
+
322
+ // Holds the result to be returned
323
+ const result = formatParts.reduce((acc, part, index) => {
324
+ const value = valueParts[index];
325
+
326
+ if ((/m/iu).test(part)) {
327
+ acc.month = value;
328
+ } else if ((/d/iu).test(part)) {
329
+ acc.day = value;
330
+ } else if ((/y/iu).test(part)) {
331
+ acc.year = value;
332
+ }
333
+
334
+ return acc;
335
+ }, {});
336
+
337
+ // If we found all the parts, return the result
338
+ if (result.month && result.year) {
339
+ return result;
340
+ }
341
+
342
+ // Throw an error to let the dev know we were unable to parse the date string
343
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
344
+ };
345
+
346
+ /**
347
+ * Convert a date object to string format.
348
+ * @param {Object} date - Date to convert to string.
349
+ * @returns {Object} Returns the date as a string.
350
+ */
351
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
352
+ year: "numeric",
353
+ month: "2-digit",
354
+ day: "2-digit",
355
+ });
356
+
357
+ /**
358
+ * Converts a date string to a North American date format.
359
+ * @param {String} dateStr - Date to validate.
360
+ * @param {String} format - Date format to validate against.
361
+ * @returns {Boolean}
362
+ */
363
+ this.toNorthAmericanFormat = (dateStr, format) => {
364
+
365
+ if (format === 'mm/dd/yyyy') {
366
+ return dateStr;
367
+ }
368
+
369
+ const parsedDate = this.parseDate(dateStr, format);
370
+
371
+ if (!parsedDate) {
372
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
373
+ }
374
+
375
+ const { month, day, year } = parsedDate;
376
+
377
+ const dateParts = [];
378
+ if (month) {
379
+ dateParts.push(month);
380
+ }
381
+
382
+ if (day) {
383
+ dateParts.push(day);
384
+ }
385
+
386
+ if (year) {
387
+ dateParts.push(year);
388
+ }
389
+
390
+ return dateParts.join('/');
391
+ };
392
+ }
393
+ };
394
+ const dateFormatter$1 = new DateFormatter$1();
395
+
396
+ // filepath: dateConstraints.mjs
397
+ const DATE_UTIL_CONSTRAINTS$1 = {
398
+ maxDay: 31,
399
+ maxMonth: 12,
400
+ maxYear: 2400,
401
+ minDay: 1,
402
+ minMonth: 1,
403
+ minYear: 1900,
404
+ };
405
+
406
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
407
+
408
+ /**
409
+ * @description The maximum day value allowed by the various utilities in this class.
410
+ * @readonly
411
+ * @type {Number}
412
+ */
413
+ get maxDay() {
414
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
415
+ }
416
+
417
+ /**
418
+ * @description The maximum month value allowed by the various utilities in this class.
419
+ * @readonly
420
+ * @type {Number}
421
+ */
422
+ get maxMonth() {
423
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
424
+ }
425
+
426
+ /**
427
+ * @description The maximum year value allowed by the various utilities in this class.
428
+ * @readonly
429
+ * @type {Number}
430
+ */
431
+ get maxYear() {
432
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
433
+ }
434
+
435
+ /**
436
+ * @description The minimum day value allowed by the various utilities in this class.
437
+ * @readonly
438
+ * @type {Number}
439
+ */
440
+ get minDay() {
441
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
442
+ }
443
+
444
+ /**
445
+ * @description The minimum month value allowed by the various utilities in this class.
446
+ * @readonly
447
+ * @type {Number}
448
+ */
449
+ get minMonth() {
450
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
451
+ }
452
+
453
+ /**
454
+ * @description The minimum year value allowed by the various utilities in this class.
455
+ * @readonly
456
+ * @type {Number}
457
+ */
458
+ get minYear() {
459
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
460
+ }
461
+ };
462
+
463
+ /* eslint-disable no-magic-numbers */
464
+
465
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
466
+
467
+ /**
468
+ * Returns the current century.
469
+ * @returns {String} The current century.
470
+ */
471
+ getCentury () {
472
+ return String(new Date().getFullYear()).slice(0, 2);
473
+ }
474
+
475
+ /**
476
+ * Returns a four digit year.
477
+ * @param {String} year - The year to convert to four digits.
478
+ * @returns {String} The four digit year.
479
+ */
480
+ getFourDigitYear (year) {
481
+
482
+ const strYear = String(year).trim();
483
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
484
+ }
485
+
486
+ constructor() {
487
+
488
+ super();
489
+
490
+ /**
491
+ * Compares two dates to see if they match.
492
+ * @param {Object} date1 - First date to compare.
493
+ * @param {Object} date2 - Second date to compare.
494
+ * @returns {Boolean} Returns true if the dates match.
495
+ */
496
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
497
+
498
+ /**
499
+ * Returns true if value passed in is a valid date.
500
+ * @param {String} date - Date to validate.
501
+ * @param {String} format - Date format to validate against.
502
+ * @returns {Boolean}
503
+ */
504
+ this.validDateStr = (date, format) => {
505
+
506
+ // The length we expect the date string to be
507
+ const dateStrLength = format.length;
508
+
509
+ // Guard Clause: Date and format are defined
510
+ if (typeof date === "undefined" || typeof format === "undefined") {
511
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
512
+ }
513
+
514
+ // Guard Clause: Date should be of type string
515
+ if (typeof date !== "string") {
516
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
517
+ }
518
+
519
+ // Guard Clause: Format should be of type string
520
+ if (typeof format !== "string") {
521
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
522
+ }
523
+
524
+ // Guard Clause: Length is what we expect it to be
525
+ if (date.length !== dateStrLength) {
526
+ return false;
527
+ }
528
+ // Get a formatted date string and parse it
529
+ const dateParts = dateFormatter$1.parseDate(date, format);
530
+
531
+ // Guard Clause: Date parse succeeded
532
+ if (!dateParts) {
533
+ return false;
534
+ }
535
+
536
+ // Create the expected date string based on the date parts
537
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
538
+
539
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
540
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
541
+
542
+ // Get the date string of the date object we created from the string date
543
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
544
+
545
+ // Guard Clause: Generated date matches date string input
546
+ if (expectedDateStr !== actualDateStr) {
547
+ return false;
548
+ }
549
+
550
+ // If we passed all other checks, we can assume the date is valid
551
+ return true;
552
+ };
553
+
554
+ /**
555
+ * Determines if a string date value matches the format provided.
556
+ * @param {string} value = The date string value.
557
+ * @param { string} format = The date format to match against.
558
+ * @returns {boolean}
559
+ */
560
+ this.dateAndFormatMatch = (value, format) => {
561
+
562
+ // Ensure we have both values we need to do the comparison
563
+ if (!value || !format) {
564
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
565
+ }
566
+
567
+ // If the lengths are different, they cannot match
568
+ if (value.length !== format.length) {
569
+ return false;
570
+ }
571
+
572
+ // Get the parts of the date
573
+ const dateParts = dateFormatter$1.parseDate(value, format);
574
+
575
+ // Validator for day
576
+ const dayValueIsValid = (day) => {
577
+
578
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
579
+ if (!dateParts.day) {
580
+ return true;
581
+ }
582
+
583
+ // Guard clause: ensure day exists.
584
+ if (!day) {
585
+ return false;
586
+ }
587
+
588
+ // Convert day to number
589
+ const numDay = Number.parseInt(day, 10);
590
+
591
+ // Guard clause: ensure day is a valid integer
592
+ if (Number.isNaN(numDay)) {
593
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
594
+ }
595
+
596
+ // Guard clause: ensure day is within the valid range
597
+ if (numDay < this.minDay || numDay > this.maxDay) {
598
+ return false;
599
+ }
600
+
601
+ // Default return
602
+ return true;
603
+ };
604
+
605
+ // Validator for month
606
+ const monthValueIsValid = (month) => {
607
+
608
+ // Guard clause: ensure month exists.
609
+ if (!month) {
610
+ return false;
611
+ }
612
+
613
+ // Convert month to number
614
+ const numMonth = Number.parseInt(month, 10);
615
+
616
+ // Guard clause: ensure month is a valid integer
617
+ if (Number.isNaN(numMonth)) {
618
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
619
+ }
620
+
621
+ // Guard clause: ensure month is within the valid range
622
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
623
+ return false;
624
+ }
625
+
626
+ // Default return
627
+ return true;
628
+ };
629
+
630
+ // Validator for year
631
+ const yearIsValid = (_year) => {
632
+
633
+ // Guard clause: ensure year exists.
634
+ if (!_year) {
635
+ return false;
636
+ }
637
+
638
+ // Get the full year
639
+ const year = this.getFourDigitYear(_year);
640
+
641
+ // Convert year to number
642
+ const numYear = Number.parseInt(year, 10);
643
+
644
+ // Guard clause: ensure year is a valid integer
645
+ if (Number.isNaN(numYear)) {
646
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
647
+ }
648
+
649
+ // Guard clause: ensure year is within the valid range
650
+ if (numYear < this.minYear || numYear > this.maxYear) {
651
+ return false;
652
+ }
653
+
654
+ // Default return
655
+ return true;
656
+ };
657
+
658
+ // Self-contained checks for month, day, and year
659
+ const checks = [
660
+ monthValueIsValid(dateParts.month),
661
+ dayValueIsValid(dateParts.day),
662
+ yearIsValid(dateParts.year)
663
+ ];
664
+
665
+ // If any of the checks failed, the date format does not match and the result is invalid
666
+ const isValid = checks.every((check) => check === true);
667
+
668
+ // If the check is invalid, return false
669
+ if (!isValid) {
670
+ return false;
671
+ }
672
+
673
+ // Default case
674
+ return true;
675
+ };
676
+ }
677
+ };
678
+
679
+ // Export a class instance
680
+ const dateUtilities$1 = new AuroDateUtilities$1();
681
+
682
+ // Export the class instance methods individually
683
+ const {
684
+ datesMatch: datesMatch$1,
685
+ validDateStr: validDateStr$1,
686
+ dateAndFormatMatch: dateAndFormatMatch$1,
687
+ minDay: minDay$1,
688
+ minMonth: minMonth$1,
689
+ minYear: minYear$1,
690
+ maxDay: maxDay$1,
691
+ maxMonth: maxMonth$1,
692
+ maxYear: maxYear$1
693
+ } = dateUtilities$1;
694
+
695
+ const {
696
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
697
+ parseDate: parseDate$1,
698
+ getDateAsString: getDateAsString$1
699
+ } = dateFormatter$1;
700
+
293
701
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
294
702
  // See LICENSE in the project root for license information.
295
703
 
@@ -365,6 +773,7 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
365
773
 
366
774
 
367
775
  let AuroFormValidation$1 = class AuroFormValidation {
776
+
368
777
  constructor() {
369
778
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
370
779
  }
@@ -456,17 +865,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
456
865
  ]
457
866
  }
458
867
  };
459
-
868
+
460
869
  let elementType;
461
870
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
462
871
  elementType = 'input';
463
872
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
464
873
  elementType = 'counter';
465
874
  }
466
-
875
+
467
876
  if (elementType) {
468
877
  const rules = validationRules[elementType];
469
-
878
+
470
879
  if (rules) {
471
880
  Object.values(rules).flat().forEach(rule => {
472
881
  if (rule.check(elem)) {
@@ -492,48 +901,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
492
901
  if (!elem.value.match(emailRegex)) {
493
902
  elem.validity = 'patternMismatch';
494
903
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
904
+ return;
495
905
  }
496
906
  } else if (elem.type === 'credit-card') {
497
907
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
498
908
  elem.validity = 'tooShort';
499
909
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
910
+ return;
500
911
  }
501
912
  } else if (elem.type === 'number') {
502
913
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
503
914
  elem.validity = 'rangeOverflow';
504
915
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
916
+ return;
505
917
  }
506
918
 
507
919
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
508
920
  elem.validity = 'rangeUnderflow';
509
921
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
922
+ return;
510
923
  }
511
- } else if (elem.type === 'date') {
512
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
924
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
925
+
926
+ // Guard Clause: if the value is too short
927
+ if (elem.value.length < elem.lengthForType) {
928
+
513
929
  elem.validity = 'tooShort';
514
930
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
515
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
516
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
517
- const valueDate = new Date(formattedValue.dateForComparison);
931
+ return;
932
+ }
933
+
934
+ // Guard Clause: If the value is too long for the type
935
+ if (elem.value?.length > elem.lengthForType) {
518
936
 
519
- // validate max
520
- if (elem.max?.length === elem.lengthForType) {
521
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
937
+ elem.validity = 'tooLong';
938
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
939
+ return;
940
+ }
941
+
942
+ // Validate that the date passed was the correct format
943
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
944
+ elem.validity = 'patternMismatch';
945
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
946
+ return;
947
+ }
948
+
949
+ // Validate that the date passed was a valid date
950
+ if (!validDateStr$1(elem.value, elem.format)) {
951
+ elem.validity = 'invalidDate';
952
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
953
+ return;
954
+ }
522
955
 
523
- if (valueDate > maxDate) {
524
- elem.validity = 'rangeOverflow';
525
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
526
- }
956
+ // Perform the rest of the validation
957
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
958
+ const valueDate = new Date(formattedValue);
959
+
960
+ // // Validate max date
961
+ if (elem.max?.length === elem.lengthForType) {
962
+
963
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
964
+
965
+ if (valueDate > maxDate) {
966
+ elem.validity = 'rangeOverflow';
967
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
968
+ return;
527
969
  }
970
+ }
528
971
 
529
- // validate min
530
- if (elem.min?.length === elem.lengthForType) {
531
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
972
+ // Validate min date
973
+ if (elem.min?.length === elem.lengthForType) {
974
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
532
975
 
533
- if (valueDate < minDate) {
534
- elem.validity = 'rangeUnderflow';
535
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
536
- }
976
+ if (valueDate < minDate) {
977
+ elem.validity = 'rangeUnderflow';
978
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
979
+ return;
537
980
  }
538
981
  }
539
982
  }
@@ -652,7 +1095,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
652
1095
  if (input.validationMessage.length > 0) {
653
1096
  elem.errorMessage = input.validationMessage;
654
1097
  }
655
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
1098
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
656
1099
  const firstInput = this.inputElements[0];
657
1100
 
658
1101
  if (firstInput.validationMessage.length > 0) {
@@ -14019,7 +14462,7 @@ class AuroFloatingUI {
14019
14462
  /**
14020
14463
  * @private
14021
14464
  * getting called on 'blur' in trigger or `focusin` in document
14022
- *
14465
+ *
14023
14466
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
14024
14467
  * This method checks if the currently active element is still within the trigger or bib.
14025
14468
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -14135,7 +14578,7 @@ class AuroFloatingUI {
14135
14578
  // Close any other dropdown that is already open
14136
14579
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
14137
14580
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
14138
- existedVisibleFloatingUI.isPopoverVisible &&
14581
+ existedVisibleFloatingUI.element.isPopoverVisible &&
14139
14582
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
14140
14583
  document.expandedAuroFloater.hideBib();
14141
14584
  }
@@ -14311,7 +14754,7 @@ class AuroFloatingUI {
14311
14754
  this.id = window.crypto.randomUUID();
14312
14755
  this.element.setAttribute('id', this.id);
14313
14756
  }
14314
-
14757
+
14315
14758
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
14316
14759
  }
14317
14760
 
@@ -14364,7 +14807,7 @@ class AuroFloatingUI {
14364
14807
  if (this.element.bib) {
14365
14808
  this.element.shadowRoot.append(this.element.bib);
14366
14809
  }
14367
-
14810
+
14368
14811
  // Remove event & keyboard listeners
14369
14812
  if (this.element?.trigger) {
14370
14813
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -14754,7 +15197,6 @@ var tokensCss$1$1 = i$5`:host{--ds-auro-dropdown-label-text-color: var(--ds-basi
14754
15197
 
14755
15198
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
14756
15199
  const DESIGN_TOKEN_BREAKPOINT_OPTIONS = [
14757
- 'xl',
14758
15200
  'lg',
14759
15201
  'md',
14760
15202
  'sm',
@@ -14826,7 +15268,6 @@ class AuroDropdownBib extends r$2 {
14826
15268
 
14827
15269
  set mobileFullscreenBreakpoint(value) {
14828
15270
  // verify the defined breakpoint is valid and exit out if not
14829
- // 'disabled' is a design token breakpoint so it acts as our "undefined" value
14830
15271
  const validatedValue = DESIGN_TOKEN_BREAKPOINT_OPTIONS.includes(value) ? value : undefined;
14831
15272
  if (!validatedValue) {
14832
15273
  this._mobileBreakpointValue = undefined;
@@ -15095,6 +15536,7 @@ var helpTextVersion$1 = '1.0.0';
15095
15536
  * @csspart helpText - The helpText content container.
15096
15537
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
15097
15538
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
15539
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
15098
15540
  */
15099
15541
  class AuroDropdown extends r$2 {
15100
15542
  constructor() {
@@ -15140,7 +15582,9 @@ class AuroDropdown extends r$2 {
15140
15582
  this.rounded = false;
15141
15583
  this.tabIndex = 0;
15142
15584
  this.noToggle = false;
15585
+ this.a11yAutocomplete = 'none';
15143
15586
  this.labeled = true;
15587
+ this.a11yRole = 'combobox';
15144
15588
  this.onDark = false;
15145
15589
 
15146
15590
  // floaterConfig
@@ -15276,6 +15720,16 @@ class AuroDropdown extends r$2 {
15276
15720
  type: Number
15277
15721
  },
15278
15722
 
15723
+ /**
15724
+ * The unique ID for the dropdown bib element.
15725
+ * @private
15726
+ */
15727
+ dropdownId: {
15728
+ type: String,
15729
+ reflect: false,
15730
+ attribute: false
15731
+ },
15732
+
15279
15733
  /**
15280
15734
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
15281
15735
  */
@@ -15339,12 +15793,7 @@ class AuroDropdown extends r$2 {
15339
15793
  },
15340
15794
 
15341
15795
  /**
15342
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
15343
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
15344
- *
15345
- * When expanded, the dropdown will automatically display in fullscreen mode
15346
- * if the screen size is equal to or smaller than the selected breakpoint.
15347
- * @default sm
15796
+ * Defines the screen size breakpoint (`lg`, `md`, `sm`, or `xs`) at which the dropdown switches to fullscreen mode on mobile. When expanded, the dropdown will automatically display in fullscreen mode if the screen size is equal to or smaller than the selected breakpoint.
15348
15797
  */
15349
15798
  fullscreenBreakpoint: {
15350
15799
  type: String,
@@ -15443,6 +15892,23 @@ class AuroDropdown extends r$2 {
15443
15892
  */
15444
15893
  tabIndex: {
15445
15894
  type: Number
15895
+ },
15896
+
15897
+ /**
15898
+ * The value for the role attribute of the trigger element.
15899
+ */
15900
+ a11yRole: {
15901
+ type: String || undefined,
15902
+ attribute: false,
15903
+ reflect: false
15904
+ },
15905
+
15906
+ /**
15907
+ * The value for the aria-autocomplete attribute of the trigger element.
15908
+ */
15909
+ a11yAutocomplete: {
15910
+ type: String,
15911
+ attribute: false,
15446
15912
  }
15447
15913
  };
15448
15914
  }
@@ -15467,15 +15933,6 @@ class AuroDropdown extends r$2 {
15467
15933
  AuroLibraryRuntimeUtils$1$1.prototype.registerComponent(name, AuroDropdown);
15468
15934
  }
15469
15935
 
15470
- /**
15471
- * Accessor for reusing the focusable entity query string.
15472
- * @private
15473
- * @returns {string}
15474
- */
15475
- get focusableEntityQuery () {
15476
- return 'auro-input, [auro-input], auro-button, [auro-button], button, input';
15477
- }
15478
-
15479
15936
  connectedCallback() {
15480
15937
  super.connectedCallback();
15481
15938
  }
@@ -15489,8 +15946,6 @@ class AuroDropdown extends r$2 {
15489
15946
  updated(changedProperties) {
15490
15947
  this.floater.handleUpdate(changedProperties);
15491
15948
 
15492
- // Note: `disabled` is not a breakpoint (it is not a screen size),
15493
- // so it looks like we never consume this - however, dropdownBib handles this in the setter as "undefined"
15494
15949
  if (changedProperties.has('fullscreenBreakpoint')) {
15495
15950
  this.bibContent.mobileFullscreenBreakpoint = this.fullscreenBreakpoint;
15496
15951
  }
@@ -15504,7 +15959,22 @@ class AuroDropdown extends r$2 {
15504
15959
  }
15505
15960
 
15506
15961
  firstUpdated() {
15962
+
15963
+ // Configure the floater to, this will generate the ID for the bib
15507
15964
  this.floater.configure(this, 'auroDropdown');
15965
+
15966
+ /**
15967
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
15968
+ * @event auroDropdown-idAdded
15969
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
15970
+ */
15971
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
15972
+
15973
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
15974
+ if (!this.triggerContentFocusable) {
15975
+ this.dropdownId = this.floater.element.id;
15976
+ }
15977
+
15508
15978
  this.bibContent = this.floater.element.bib;
15509
15979
 
15510
15980
  // Add the tag name as an attribute if it is different than the component name
@@ -15626,7 +16096,7 @@ class AuroDropdown extends r$2 {
15626
16096
 
15627
16097
  this.triggerContentSlot.forEach((node) => {
15628
16098
  if (node.querySelectorAll) {
15629
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
16099
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
15630
16100
  auroElements.forEach((auroEl) => {
15631
16101
  auroEl.addEventListener('focus', this.bindFocusEventToTrigger);
15632
16102
  auroEl.addEventListener('blur', this.bindFocusEventToTrigger);
@@ -15647,7 +16117,7 @@ class AuroDropdown extends r$2 {
15647
16117
 
15648
16118
  this.triggerContentSlot.forEach((node) => {
15649
16119
  if (node.querySelectorAll) {
15650
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
16120
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
15651
16121
  auroElements.forEach((auroEl) => {
15652
16122
  auroEl.removeEventListener('focus', this.bindFocusEventToTrigger);
15653
16123
  auroEl.removeEventListener('blur', this.bindFocusEventToTrigger);
@@ -15656,6 +16126,30 @@ class AuroDropdown extends r$2 {
15656
16126
  });
15657
16127
  }
15658
16128
 
16129
+ /*
16130
+ * Sets aria attributes for the trigger element if a custom one is passed in.
16131
+ * @private
16132
+ * @method setTriggerAriaAttributes
16133
+ * @param { HTMLElement } triggerElement - The custom trigger element.
16134
+ */
16135
+ clearTriggerA11yAttributes(triggerElement) {
16136
+
16137
+ if (!triggerElement || !triggerElement.removeAttribute) {
16138
+ return;
16139
+ }
16140
+
16141
+ // Reset appropriate attributes for a11y
16142
+ triggerElement.removeAttribute('aria-labelledby');
16143
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
16144
+ triggerElement.removeAttribute('id');
16145
+ }
16146
+ triggerElement.removeAttribute('role');
16147
+ triggerElement.removeAttribute('aria-expanded');
16148
+
16149
+ triggerElement.removeAttribute('aria-controls');
16150
+ triggerElement.removeAttribute('aria-autocomplete');
16151
+ }
16152
+
15659
16153
  /**
15660
16154
  * Handles changes to the trigger content slot and updates related properties.
15661
16155
  *
@@ -15669,32 +16163,41 @@ class AuroDropdown extends r$2 {
15669
16163
  * @returns {void}
15670
16164
  */
15671
16165
  handleTriggerContentSlotChange(event) {
16166
+
15672
16167
  this.floater.handleTriggerTabIndex();
15673
16168
 
16169
+ // Get the trigger
16170
+ const trigger = this.shadowRoot.querySelector('#trigger');
16171
+
16172
+ // Get the trigger slot
15674
16173
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
15675
16174
 
16175
+ // If there's a trigger slot
15676
16176
  if (triggerSlot) {
15677
16177
 
16178
+ // Get the content nodes to see if there are any children
15678
16179
  const triggerContentNodes = triggerSlot.assignedNodes();
15679
16180
 
16181
+ // If there are children
15680
16182
  if (triggerContentNodes) {
15681
16183
 
15682
- triggerContentNodes.forEach((node) => {
15683
- if (!this.triggerContentFocusable) {
15684
- this.triggerContentFocusable = this.containsFocusableElement(node);
15685
- }
15686
- });
15687
- }
15688
- }
16184
+ // See if any of them are focusable elemeents
16185
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
15689
16186
 
15690
- const trigger = this.shadowRoot.querySelector('#trigger');
16187
+ // If any of them are focusable elements
16188
+ if (this.triggerContentFocusable) {
15691
16189
 
15692
- if (!this.triggerContentFocusable) {
15693
- trigger.setAttribute('tabindex', '0');
15694
- trigger.setAttribute('role', 'button');
15695
- } else {
15696
- trigger.removeAttribute('tabindex');
15697
- trigger.removeAttribute('role');
16190
+ // Assume the consumer will be providing their own a11y in whatever they passed in
16191
+ this.clearTriggerA11yAttributes(trigger);
16192
+
16193
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
16194
+ trigger.removeAttribute('tabindex');
16195
+ } else {
16196
+
16197
+ // Add the tabindex to the trigger so that it's in the focus flow
16198
+ trigger.setAttribute('tabindex', '0');
16199
+ }
16200
+ }
15698
16201
  }
15699
16202
 
15700
16203
  if (event) {
@@ -15704,6 +16207,7 @@ class AuroDropdown extends r$2 {
15704
16207
 
15705
16208
  if (this.triggerContentSlot) {
15706
16209
  this.setupTriggerFocusEventBinding();
16210
+
15707
16211
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
15708
16212
  if (slot.textContent.trim()) {
15709
16213
  return true;
@@ -15771,10 +16275,13 @@ class AuroDropdown extends r$2 {
15771
16275
  id="trigger"
15772
16276
  class="trigger"
15773
16277
  part="trigger"
15774
- aria-labelledby="triggerLabel"
15775
16278
  tabindex="${this.tabIndex}"
15776
16279
  ?showBorder="${this.showTriggerBorders}"
15777
- >
16280
+ role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
16281
+ aria-expanded="${o(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
16282
+ aria-controls="${o(this.triggerContentFocusable ? undefined : this.dropdownId)}"
16283
+ aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
16284
+ >
15778
16285
  <div class="triggerContentWrapper">
15779
16286
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
15780
16287
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -15808,12 +16315,12 @@ class AuroDropdown extends r$2 {
15808
16315
  <div id="bibSizer" part="size"></div>
15809
16316
  <${this.dropdownBibTag}
15810
16317
  id="bib"
15811
- role="tooltip"
15812
16318
  ?data-show="${this.isPopoverVisible}"
15813
16319
  ?isfullscreen="${this.isBibFullscreen}"
15814
16320
  ?common="${this.common}"
15815
16321
  ?rounded="${this.common || this.rounded}"
15816
- ?inset="${this.common || this.inset}">
16322
+ ?inset="${this.common || this.inset}"
16323
+ >
15817
16324
  </${this.dropdownBibTag}>
15818
16325
  </div>
15819
16326
  `;
@@ -19804,6 +20311,414 @@ class AuroInputUtilities {
19804
20311
  }
19805
20312
  }
19806
20313
 
20314
+ class DateFormatter {
20315
+
20316
+ constructor() {
20317
+
20318
+ /**
20319
+ * @description Parses a date string into its components.
20320
+ * @param {string} dateStr - Date string to parse.
20321
+ * @param {string} format - Date format to parse.
20322
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
20323
+ */
20324
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
20325
+
20326
+ // Guard Clause: Date string is defined
20327
+ if (!dateStr) {
20328
+ return undefined;
20329
+ }
20330
+
20331
+ // Assume the separator is a "/" a defined in our code base
20332
+ const separator = '/';
20333
+
20334
+ // Get the parts of the date and format
20335
+ const valueParts = dateStr.split(separator);
20336
+ const formatParts = format.split(separator);
20337
+
20338
+ // Check if the value and format have the correct number of parts
20339
+ if (valueParts.length !== formatParts.length) {
20340
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
20341
+ }
20342
+
20343
+ // Holds the result to be returned
20344
+ const result = formatParts.reduce((acc, part, index) => {
20345
+ const value = valueParts[index];
20346
+
20347
+ if ((/m/iu).test(part)) {
20348
+ acc.month = value;
20349
+ } else if ((/d/iu).test(part)) {
20350
+ acc.day = value;
20351
+ } else if ((/y/iu).test(part)) {
20352
+ acc.year = value;
20353
+ }
20354
+
20355
+ return acc;
20356
+ }, {});
20357
+
20358
+ // If we found all the parts, return the result
20359
+ if (result.month && result.year) {
20360
+ return result;
20361
+ }
20362
+
20363
+ // Throw an error to let the dev know we were unable to parse the date string
20364
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
20365
+ };
20366
+
20367
+ /**
20368
+ * Convert a date object to string format.
20369
+ * @param {Object} date - Date to convert to string.
20370
+ * @returns {Object} Returns the date as a string.
20371
+ */
20372
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
20373
+ year: "numeric",
20374
+ month: "2-digit",
20375
+ day: "2-digit",
20376
+ });
20377
+
20378
+ /**
20379
+ * Converts a date string to a North American date format.
20380
+ * @param {String} dateStr - Date to validate.
20381
+ * @param {String} format - Date format to validate against.
20382
+ * @returns {Boolean}
20383
+ */
20384
+ this.toNorthAmericanFormat = (dateStr, format) => {
20385
+
20386
+ if (format === 'mm/dd/yyyy') {
20387
+ return dateStr;
20388
+ }
20389
+
20390
+ const parsedDate = this.parseDate(dateStr, format);
20391
+
20392
+ if (!parsedDate) {
20393
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
20394
+ }
20395
+
20396
+ const { month, day, year } = parsedDate;
20397
+
20398
+ const dateParts = [];
20399
+ if (month) {
20400
+ dateParts.push(month);
20401
+ }
20402
+
20403
+ if (day) {
20404
+ dateParts.push(day);
20405
+ }
20406
+
20407
+ if (year) {
20408
+ dateParts.push(year);
20409
+ }
20410
+
20411
+ return dateParts.join('/');
20412
+ };
20413
+ }
20414
+ }
20415
+ const dateFormatter = new DateFormatter();
20416
+
20417
+ // filepath: dateConstraints.mjs
20418
+ const DATE_UTIL_CONSTRAINTS = {
20419
+ maxDay: 31,
20420
+ maxMonth: 12,
20421
+ maxYear: 2400,
20422
+ minDay: 1,
20423
+ minMonth: 1,
20424
+ minYear: 1900,
20425
+ };
20426
+
20427
+ class AuroDateUtilitiesBase {
20428
+
20429
+ /**
20430
+ * @description The maximum day value allowed by the various utilities in this class.
20431
+ * @readonly
20432
+ * @type {Number}
20433
+ */
20434
+ get maxDay() {
20435
+ return DATE_UTIL_CONSTRAINTS.maxDay;
20436
+ }
20437
+
20438
+ /**
20439
+ * @description The maximum month value allowed by the various utilities in this class.
20440
+ * @readonly
20441
+ * @type {Number}
20442
+ */
20443
+ get maxMonth() {
20444
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
20445
+ }
20446
+
20447
+ /**
20448
+ * @description The maximum year value allowed by the various utilities in this class.
20449
+ * @readonly
20450
+ * @type {Number}
20451
+ */
20452
+ get maxYear() {
20453
+ return DATE_UTIL_CONSTRAINTS.maxYear;
20454
+ }
20455
+
20456
+ /**
20457
+ * @description The minimum day value allowed by the various utilities in this class.
20458
+ * @readonly
20459
+ * @type {Number}
20460
+ */
20461
+ get minDay() {
20462
+ return DATE_UTIL_CONSTRAINTS.minDay;
20463
+ }
20464
+
20465
+ /**
20466
+ * @description The minimum month value allowed by the various utilities in this class.
20467
+ * @readonly
20468
+ * @type {Number}
20469
+ */
20470
+ get minMonth() {
20471
+ return DATE_UTIL_CONSTRAINTS.minMonth;
20472
+ }
20473
+
20474
+ /**
20475
+ * @description The minimum year value allowed by the various utilities in this class.
20476
+ * @readonly
20477
+ * @type {Number}
20478
+ */
20479
+ get minYear() {
20480
+ return DATE_UTIL_CONSTRAINTS.minYear;
20481
+ }
20482
+ }
20483
+
20484
+ /* eslint-disable no-magic-numbers */
20485
+
20486
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
20487
+
20488
+ /**
20489
+ * Returns the current century.
20490
+ * @returns {String} The current century.
20491
+ */
20492
+ getCentury () {
20493
+ return String(new Date().getFullYear()).slice(0, 2);
20494
+ }
20495
+
20496
+ /**
20497
+ * Returns a four digit year.
20498
+ * @param {String} year - The year to convert to four digits.
20499
+ * @returns {String} The four digit year.
20500
+ */
20501
+ getFourDigitYear (year) {
20502
+
20503
+ const strYear = String(year).trim();
20504
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
20505
+ }
20506
+
20507
+ constructor() {
20508
+
20509
+ super();
20510
+
20511
+ /**
20512
+ * Compares two dates to see if they match.
20513
+ * @param {Object} date1 - First date to compare.
20514
+ * @param {Object} date2 - Second date to compare.
20515
+ * @returns {Boolean} Returns true if the dates match.
20516
+ */
20517
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
20518
+
20519
+ /**
20520
+ * Returns true if value passed in is a valid date.
20521
+ * @param {String} date - Date to validate.
20522
+ * @param {String} format - Date format to validate against.
20523
+ * @returns {Boolean}
20524
+ */
20525
+ this.validDateStr = (date, format) => {
20526
+
20527
+ // The length we expect the date string to be
20528
+ const dateStrLength = format.length;
20529
+
20530
+ // Guard Clause: Date and format are defined
20531
+ if (typeof date === "undefined" || typeof format === "undefined") {
20532
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
20533
+ }
20534
+
20535
+ // Guard Clause: Date should be of type string
20536
+ if (typeof date !== "string") {
20537
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
20538
+ }
20539
+
20540
+ // Guard Clause: Format should be of type string
20541
+ if (typeof format !== "string") {
20542
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
20543
+ }
20544
+
20545
+ // Guard Clause: Length is what we expect it to be
20546
+ if (date.length !== dateStrLength) {
20547
+ return false;
20548
+ }
20549
+ // Get a formatted date string and parse it
20550
+ const dateParts = dateFormatter.parseDate(date, format);
20551
+
20552
+ // Guard Clause: Date parse succeeded
20553
+ if (!dateParts) {
20554
+ return false;
20555
+ }
20556
+
20557
+ // Create the expected date string based on the date parts
20558
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
20559
+
20560
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
20561
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
20562
+
20563
+ // Get the date string of the date object we created from the string date
20564
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
20565
+
20566
+ // Guard Clause: Generated date matches date string input
20567
+ if (expectedDateStr !== actualDateStr) {
20568
+ return false;
20569
+ }
20570
+
20571
+ // If we passed all other checks, we can assume the date is valid
20572
+ return true;
20573
+ };
20574
+
20575
+ /**
20576
+ * Determines if a string date value matches the format provided.
20577
+ * @param {string} value = The date string value.
20578
+ * @param { string} format = The date format to match against.
20579
+ * @returns {boolean}
20580
+ */
20581
+ this.dateAndFormatMatch = (value, format) => {
20582
+
20583
+ // Ensure we have both values we need to do the comparison
20584
+ if (!value || !format) {
20585
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
20586
+ }
20587
+
20588
+ // If the lengths are different, they cannot match
20589
+ if (value.length !== format.length) {
20590
+ return false;
20591
+ }
20592
+
20593
+ // Get the parts of the date
20594
+ const dateParts = dateFormatter.parseDate(value, format);
20595
+
20596
+ // Validator for day
20597
+ const dayValueIsValid = (day) => {
20598
+
20599
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
20600
+ if (!dateParts.day) {
20601
+ return true;
20602
+ }
20603
+
20604
+ // Guard clause: ensure day exists.
20605
+ if (!day) {
20606
+ return false;
20607
+ }
20608
+
20609
+ // Convert day to number
20610
+ const numDay = Number.parseInt(day, 10);
20611
+
20612
+ // Guard clause: ensure day is a valid integer
20613
+ if (Number.isNaN(numDay)) {
20614
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
20615
+ }
20616
+
20617
+ // Guard clause: ensure day is within the valid range
20618
+ if (numDay < this.minDay || numDay > this.maxDay) {
20619
+ return false;
20620
+ }
20621
+
20622
+ // Default return
20623
+ return true;
20624
+ };
20625
+
20626
+ // Validator for month
20627
+ const monthValueIsValid = (month) => {
20628
+
20629
+ // Guard clause: ensure month exists.
20630
+ if (!month) {
20631
+ return false;
20632
+ }
20633
+
20634
+ // Convert month to number
20635
+ const numMonth = Number.parseInt(month, 10);
20636
+
20637
+ // Guard clause: ensure month is a valid integer
20638
+ if (Number.isNaN(numMonth)) {
20639
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
20640
+ }
20641
+
20642
+ // Guard clause: ensure month is within the valid range
20643
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
20644
+ return false;
20645
+ }
20646
+
20647
+ // Default return
20648
+ return true;
20649
+ };
20650
+
20651
+ // Validator for year
20652
+ const yearIsValid = (_year) => {
20653
+
20654
+ // Guard clause: ensure year exists.
20655
+ if (!_year) {
20656
+ return false;
20657
+ }
20658
+
20659
+ // Get the full year
20660
+ const year = this.getFourDigitYear(_year);
20661
+
20662
+ // Convert year to number
20663
+ const numYear = Number.parseInt(year, 10);
20664
+
20665
+ // Guard clause: ensure year is a valid integer
20666
+ if (Number.isNaN(numYear)) {
20667
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
20668
+ }
20669
+
20670
+ // Guard clause: ensure year is within the valid range
20671
+ if (numYear < this.minYear || numYear > this.maxYear) {
20672
+ return false;
20673
+ }
20674
+
20675
+ // Default return
20676
+ return true;
20677
+ };
20678
+
20679
+ // Self-contained checks for month, day, and year
20680
+ const checks = [
20681
+ monthValueIsValid(dateParts.month),
20682
+ dayValueIsValid(dateParts.day),
20683
+ yearIsValid(dateParts.year)
20684
+ ];
20685
+
20686
+ // If any of the checks failed, the date format does not match and the result is invalid
20687
+ const isValid = checks.every((check) => check === true);
20688
+
20689
+ // If the check is invalid, return false
20690
+ if (!isValid) {
20691
+ return false;
20692
+ }
20693
+
20694
+ // Default case
20695
+ return true;
20696
+ };
20697
+ }
20698
+ }
20699
+
20700
+ // Export a class instance
20701
+ const dateUtilities = new AuroDateUtilities();
20702
+
20703
+ // Export the class instance methods individually
20704
+ const {
20705
+ datesMatch,
20706
+ validDateStr,
20707
+ dateAndFormatMatch,
20708
+ minDay,
20709
+ minMonth,
20710
+ minYear,
20711
+ maxDay,
20712
+ maxMonth,
20713
+ maxYear
20714
+ } = dateUtilities;
20715
+
20716
+ const {
20717
+ toNorthAmericanFormat,
20718
+ parseDate,
20719
+ getDateAsString
20720
+ } = dateFormatter;
20721
+
19807
20722
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
19808
20723
  // See LICENSE in the project root for license information.
19809
20724
 
@@ -19879,6 +20794,7 @@ let AuroLibraryRuntimeUtils$1 = class AuroLibraryRuntimeUtils {
19879
20794
 
19880
20795
 
19881
20796
  class AuroFormValidation {
20797
+
19882
20798
  constructor() {
19883
20799
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1();
19884
20800
  }
@@ -19970,17 +20886,17 @@ class AuroFormValidation {
19970
20886
  ]
19971
20887
  }
19972
20888
  };
19973
-
20889
+
19974
20890
  let elementType;
19975
20891
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
19976
20892
  elementType = 'input';
19977
20893
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
19978
20894
  elementType = 'counter';
19979
20895
  }
19980
-
20896
+
19981
20897
  if (elementType) {
19982
20898
  const rules = validationRules[elementType];
19983
-
20899
+
19984
20900
  if (rules) {
19985
20901
  Object.values(rules).flat().forEach(rule => {
19986
20902
  if (rule.check(elem)) {
@@ -20006,48 +20922,82 @@ class AuroFormValidation {
20006
20922
  if (!elem.value.match(emailRegex)) {
20007
20923
  elem.validity = 'patternMismatch';
20008
20924
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20925
+ return;
20009
20926
  }
20010
20927
  } else if (elem.type === 'credit-card') {
20011
20928
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
20012
20929
  elem.validity = 'tooShort';
20013
20930
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20931
+ return;
20014
20932
  }
20015
20933
  } else if (elem.type === 'number') {
20016
20934
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
20017
20935
  elem.validity = 'rangeOverflow';
20018
20936
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
20937
+ return;
20019
20938
  }
20020
20939
 
20021
20940
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
20022
20941
  elem.validity = 'rangeUnderflow';
20023
20942
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
20943
+ return;
20024
20944
  }
20025
- } else if (elem.type === 'date') {
20026
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
20945
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
20946
+
20947
+ // Guard Clause: if the value is too short
20948
+ if (elem.value.length < elem.lengthForType) {
20949
+
20027
20950
  elem.validity = 'tooShort';
20028
20951
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20029
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
20030
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
20031
- const valueDate = new Date(formattedValue.dateForComparison);
20952
+ return;
20953
+ }
20954
+
20955
+ // Guard Clause: If the value is too long for the type
20956
+ if (elem.value?.length > elem.lengthForType) {
20032
20957
 
20033
- // validate max
20034
- if (elem.max?.length === elem.lengthForType) {
20035
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
20958
+ elem.validity = 'tooLong';
20959
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20960
+ return;
20961
+ }
20962
+
20963
+ // Validate that the date passed was the correct format
20964
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
20965
+ elem.validity = 'patternMismatch';
20966
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
20967
+ return;
20968
+ }
20969
+
20970
+ // Validate that the date passed was a valid date
20971
+ if (!validDateStr(elem.value, elem.format)) {
20972
+ elem.validity = 'invalidDate';
20973
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
20974
+ return;
20975
+ }
20036
20976
 
20037
- if (valueDate > maxDate) {
20038
- elem.validity = 'rangeOverflow';
20039
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
20040
- }
20977
+ // Perform the rest of the validation
20978
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
20979
+ const valueDate = new Date(formattedValue);
20980
+
20981
+ // // Validate max date
20982
+ if (elem.max?.length === elem.lengthForType) {
20983
+
20984
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
20985
+
20986
+ if (valueDate > maxDate) {
20987
+ elem.validity = 'rangeOverflow';
20988
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
20989
+ return;
20041
20990
  }
20991
+ }
20042
20992
 
20043
- // validate min
20044
- if (elem.min?.length === elem.lengthForType) {
20045
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
20993
+ // Validate min date
20994
+ if (elem.min?.length === elem.lengthForType) {
20995
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
20046
20996
 
20047
- if (valueDate < minDate) {
20048
- elem.validity = 'rangeUnderflow';
20049
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
20050
- }
20997
+ if (valueDate < minDate) {
20998
+ elem.validity = 'rangeUnderflow';
20999
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
21000
+ return;
20051
21001
  }
20052
21002
  }
20053
21003
  }
@@ -20166,7 +21116,7 @@ class AuroFormValidation {
20166
21116
  if (input.validationMessage.length > 0) {
20167
21117
  elem.errorMessage = input.validationMessage;
20168
21118
  }
20169
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
21119
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
20170
21120
  const firstInput = this.inputElements[0];
20171
21121
 
20172
21122
  if (firstInput.validationMessage.length > 0) {
@@ -20288,6 +21238,33 @@ class BaseInput extends r$2 {
20288
21238
  static get properties() {
20289
21239
  return {
20290
21240
 
21241
+ /**
21242
+ * The value for the role attribute.
21243
+ */
21244
+ a11yRole: {
21245
+ type: String,
21246
+ attribute: true,
21247
+ reflect: true
21248
+ },
21249
+
21250
+ /**
21251
+ * The value for the aria-expanded attribute.
21252
+ */
21253
+ a11yExpanded: {
21254
+ type: Boolean,
21255
+ attribute: true,
21256
+ reflect: true
21257
+ },
21258
+
21259
+ /**
21260
+ * The value for the aria-controls attribute.
21261
+ */
21262
+ a11yControls: {
21263
+ type: String,
21264
+ attribute: true,
21265
+ reflect: true
21266
+ },
21267
+
20291
21268
  /**
20292
21269
  * If set, the label will remain fixed in the active position.
20293
21270
  */
@@ -20929,6 +21906,10 @@ class BaseInput extends r$2 {
20929
21906
  } else if (this.type === 'number') {
20930
21907
  this.inputMode = 'numeric';
20931
21908
  }
21909
+
21910
+ if (this.type === "date" && !this.format) {
21911
+ this.format = 'mm/dd/yyyy';
21912
+ }
20932
21913
  }
20933
21914
 
20934
21915
  /**
@@ -22172,6 +23153,7 @@ var helpTextVersion = '1.0.0';
22172
23153
 
22173
23154
  // build the component class
22174
23155
  class AuroInput extends BaseInput {
23156
+
22175
23157
  constructor() {
22176
23158
  super();
22177
23159
 
@@ -22284,7 +23266,7 @@ class AuroInput extends BaseInput {
22284
23266
  ?required="${this.required}"
22285
23267
  ?disabled="${this.disabled}"
22286
23268
  aria-describedby="${this.uniqueId}"
22287
- aria-invalid="${this.validity !== 'valid'}"
23269
+ ?aria-invalid="${this.validity !== 'valid'}"
22288
23270
  placeholder=${this.getPlaceholder()}
22289
23271
  lang="${o(this.lang)}"
22290
23272
  ?activeLabel="${this.activeLabel}"
@@ -22293,7 +23275,10 @@ class AuroInput extends BaseInput {
22293
23275
  autocapitalize="${o(this.autocapitalize ? this.autocapitalize : undefined)}"
22294
23276
  autocomplete="${o(this.autocomplete ? this.autocomplete : undefined)}"
22295
23277
  part="input"
22296
- />
23278
+ role="${o(this.a11yRole)}"
23279
+ aria-expanded="${o(this.a11yExpanded)}"
23280
+ aria-controls="${o(this.a11yControls)}"
23281
+ />
22297
23282
  </div>
22298
23283
  <div
22299
23284
  class="notificationIcons"
@@ -22439,7 +23424,6 @@ class AuroDatePicker extends r$2 {
22439
23424
  this.calendarEndDate = undefined;
22440
23425
  this.calendarFocusDate = this.value;
22441
23426
  this.format = 'mm/dd/yyyy';
22442
- this.fullscreenBreakpoint = 'sm';
22443
23427
  this.monthNames = [
22444
23428
  'January',
22445
23429
  'February',
@@ -22577,19 +23561,6 @@ class AuroDatePicker extends r$2 {
22577
23561
  reflect: true
22578
23562
  },
22579
23563
 
22580
- /**
22581
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
22582
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
22583
- *
22584
- * When expanded, the dropdown will automatically display in fullscreen mode
22585
- * if the screen size is equal to or smaller than the selected breakpoint.
22586
- * @default sm
22587
- */
22588
- fullscreenBreakpoint: {
22589
- type: String,
22590
- reflect: true
22591
- },
22592
-
22593
23564
  /**
22594
23565
  * If declared, make bib.fullscreen.headline in HeadingDisplay.
22595
23566
  * Otherwise, Heading 600.
@@ -23416,7 +24387,7 @@ class AuroDatePicker extends r$2 {
23416
24387
  ?disabled="${this.disabled}"
23417
24388
  ?error="${this.validity !== undefined && this.validity !== 'valid'}"
23418
24389
  disableEventShow
23419
- .fullscreenBreakpoint="${this.fullscreenBreakpoint}"
24390
+ fullscreenBreakpoint="sm"
23420
24391
  .placement="${this.placement}"
23421
24392
  .offset="${this.offset}"
23422
24393
  ?autoPlacement="${this.autoPlacement}"