@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
@@ -164,11 +164,420 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
164
164
  }
165
165
  };
166
166
 
167
+ let DateFormatter$1 = class DateFormatter {
168
+
169
+ constructor() {
170
+
171
+ /**
172
+ * @description Parses a date string into its components.
173
+ * @param {string} dateStr - Date string to parse.
174
+ * @param {string} format - Date format to parse.
175
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
176
+ */
177
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
178
+
179
+ // Guard Clause: Date string is defined
180
+ if (!dateStr) {
181
+ return undefined;
182
+ }
183
+
184
+ // Assume the separator is a "/" a defined in our code base
185
+ const separator = '/';
186
+
187
+ // Get the parts of the date and format
188
+ const valueParts = dateStr.split(separator);
189
+ const formatParts = format.split(separator);
190
+
191
+ // Check if the value and format have the correct number of parts
192
+ if (valueParts.length !== formatParts.length) {
193
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
194
+ }
195
+
196
+ // Holds the result to be returned
197
+ const result = formatParts.reduce((acc, part, index) => {
198
+ const value = valueParts[index];
199
+
200
+ if ((/m/iu).test(part)) {
201
+ acc.month = value;
202
+ } else if ((/d/iu).test(part)) {
203
+ acc.day = value;
204
+ } else if ((/y/iu).test(part)) {
205
+ acc.year = value;
206
+ }
207
+
208
+ return acc;
209
+ }, {});
210
+
211
+ // If we found all the parts, return the result
212
+ if (result.month && result.year) {
213
+ return result;
214
+ }
215
+
216
+ // Throw an error to let the dev know we were unable to parse the date string
217
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
218
+ };
219
+
220
+ /**
221
+ * Convert a date object to string format.
222
+ * @param {Object} date - Date to convert to string.
223
+ * @returns {Object} Returns the date as a string.
224
+ */
225
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
226
+ year: "numeric",
227
+ month: "2-digit",
228
+ day: "2-digit",
229
+ });
230
+
231
+ /**
232
+ * Converts a date string to a North American date format.
233
+ * @param {String} dateStr - Date to validate.
234
+ * @param {String} format - Date format to validate against.
235
+ * @returns {Boolean}
236
+ */
237
+ this.toNorthAmericanFormat = (dateStr, format) => {
238
+
239
+ if (format === 'mm/dd/yyyy') {
240
+ return dateStr;
241
+ }
242
+
243
+ const parsedDate = this.parseDate(dateStr, format);
244
+
245
+ if (!parsedDate) {
246
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
247
+ }
248
+
249
+ const { month, day, year } = parsedDate;
250
+
251
+ const dateParts = [];
252
+ if (month) {
253
+ dateParts.push(month);
254
+ }
255
+
256
+ if (day) {
257
+ dateParts.push(day);
258
+ }
259
+
260
+ if (year) {
261
+ dateParts.push(year);
262
+ }
263
+
264
+ return dateParts.join('/');
265
+ };
266
+ }
267
+ };
268
+ const dateFormatter$1 = new DateFormatter$1();
269
+
270
+ // filepath: dateConstraints.mjs
271
+ const DATE_UTIL_CONSTRAINTS$1 = {
272
+ maxDay: 31,
273
+ maxMonth: 12,
274
+ maxYear: 2400,
275
+ minDay: 1,
276
+ minMonth: 1,
277
+ minYear: 1900,
278
+ };
279
+
280
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
281
+
282
+ /**
283
+ * @description The maximum day value allowed by the various utilities in this class.
284
+ * @readonly
285
+ * @type {Number}
286
+ */
287
+ get maxDay() {
288
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
289
+ }
290
+
291
+ /**
292
+ * @description The maximum month value allowed by the various utilities in this class.
293
+ * @readonly
294
+ * @type {Number}
295
+ */
296
+ get maxMonth() {
297
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
298
+ }
299
+
300
+ /**
301
+ * @description The maximum year value allowed by the various utilities in this class.
302
+ * @readonly
303
+ * @type {Number}
304
+ */
305
+ get maxYear() {
306
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
307
+ }
308
+
309
+ /**
310
+ * @description The minimum day value allowed by the various utilities in this class.
311
+ * @readonly
312
+ * @type {Number}
313
+ */
314
+ get minDay() {
315
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
316
+ }
317
+
318
+ /**
319
+ * @description The minimum month value allowed by the various utilities in this class.
320
+ * @readonly
321
+ * @type {Number}
322
+ */
323
+ get minMonth() {
324
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
325
+ }
326
+
327
+ /**
328
+ * @description The minimum year value allowed by the various utilities in this class.
329
+ * @readonly
330
+ * @type {Number}
331
+ */
332
+ get minYear() {
333
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
334
+ }
335
+ };
336
+
337
+ /* eslint-disable no-magic-numbers */
338
+
339
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
340
+
341
+ /**
342
+ * Returns the current century.
343
+ * @returns {String} The current century.
344
+ */
345
+ getCentury () {
346
+ return String(new Date().getFullYear()).slice(0, 2);
347
+ }
348
+
349
+ /**
350
+ * Returns a four digit year.
351
+ * @param {String} year - The year to convert to four digits.
352
+ * @returns {String} The four digit year.
353
+ */
354
+ getFourDigitYear (year) {
355
+
356
+ const strYear = String(year).trim();
357
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
358
+ }
359
+
360
+ constructor() {
361
+
362
+ super();
363
+
364
+ /**
365
+ * Compares two dates to see if they match.
366
+ * @param {Object} date1 - First date to compare.
367
+ * @param {Object} date2 - Second date to compare.
368
+ * @returns {Boolean} Returns true if the dates match.
369
+ */
370
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
371
+
372
+ /**
373
+ * Returns true if value passed in is a valid date.
374
+ * @param {String} date - Date to validate.
375
+ * @param {String} format - Date format to validate against.
376
+ * @returns {Boolean}
377
+ */
378
+ this.validDateStr = (date, format) => {
379
+
380
+ // The length we expect the date string to be
381
+ const dateStrLength = format.length;
382
+
383
+ // Guard Clause: Date and format are defined
384
+ if (typeof date === "undefined" || typeof format === "undefined") {
385
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
386
+ }
387
+
388
+ // Guard Clause: Date should be of type string
389
+ if (typeof date !== "string") {
390
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
391
+ }
392
+
393
+ // Guard Clause: Format should be of type string
394
+ if (typeof format !== "string") {
395
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
396
+ }
397
+
398
+ // Guard Clause: Length is what we expect it to be
399
+ if (date.length !== dateStrLength) {
400
+ return false;
401
+ }
402
+ // Get a formatted date string and parse it
403
+ const dateParts = dateFormatter$1.parseDate(date, format);
404
+
405
+ // Guard Clause: Date parse succeeded
406
+ if (!dateParts) {
407
+ return false;
408
+ }
409
+
410
+ // Create the expected date string based on the date parts
411
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
412
+
413
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
414
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
415
+
416
+ // Get the date string of the date object we created from the string date
417
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
418
+
419
+ // Guard Clause: Generated date matches date string input
420
+ if (expectedDateStr !== actualDateStr) {
421
+ return false;
422
+ }
423
+
424
+ // If we passed all other checks, we can assume the date is valid
425
+ return true;
426
+ };
427
+
428
+ /**
429
+ * Determines if a string date value matches the format provided.
430
+ * @param {string} value = The date string value.
431
+ * @param { string} format = The date format to match against.
432
+ * @returns {boolean}
433
+ */
434
+ this.dateAndFormatMatch = (value, format) => {
435
+
436
+ // Ensure we have both values we need to do the comparison
437
+ if (!value || !format) {
438
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
439
+ }
440
+
441
+ // If the lengths are different, they cannot match
442
+ if (value.length !== format.length) {
443
+ return false;
444
+ }
445
+
446
+ // Get the parts of the date
447
+ const dateParts = dateFormatter$1.parseDate(value, format);
448
+
449
+ // Validator for day
450
+ const dayValueIsValid = (day) => {
451
+
452
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
453
+ if (!dateParts.day) {
454
+ return true;
455
+ }
456
+
457
+ // Guard clause: ensure day exists.
458
+ if (!day) {
459
+ return false;
460
+ }
461
+
462
+ // Convert day to number
463
+ const numDay = Number.parseInt(day, 10);
464
+
465
+ // Guard clause: ensure day is a valid integer
466
+ if (Number.isNaN(numDay)) {
467
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
468
+ }
469
+
470
+ // Guard clause: ensure day is within the valid range
471
+ if (numDay < this.minDay || numDay > this.maxDay) {
472
+ return false;
473
+ }
474
+
475
+ // Default return
476
+ return true;
477
+ };
478
+
479
+ // Validator for month
480
+ const monthValueIsValid = (month) => {
481
+
482
+ // Guard clause: ensure month exists.
483
+ if (!month) {
484
+ return false;
485
+ }
486
+
487
+ // Convert month to number
488
+ const numMonth = Number.parseInt(month, 10);
489
+
490
+ // Guard clause: ensure month is a valid integer
491
+ if (Number.isNaN(numMonth)) {
492
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
493
+ }
494
+
495
+ // Guard clause: ensure month is within the valid range
496
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
497
+ return false;
498
+ }
499
+
500
+ // Default return
501
+ return true;
502
+ };
503
+
504
+ // Validator for year
505
+ const yearIsValid = (_year) => {
506
+
507
+ // Guard clause: ensure year exists.
508
+ if (!_year) {
509
+ return false;
510
+ }
511
+
512
+ // Get the full year
513
+ const year = this.getFourDigitYear(_year);
514
+
515
+ // Convert year to number
516
+ const numYear = Number.parseInt(year, 10);
517
+
518
+ // Guard clause: ensure year is a valid integer
519
+ if (Number.isNaN(numYear)) {
520
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
521
+ }
522
+
523
+ // Guard clause: ensure year is within the valid range
524
+ if (numYear < this.minYear || numYear > this.maxYear) {
525
+ return false;
526
+ }
527
+
528
+ // Default return
529
+ return true;
530
+ };
531
+
532
+ // Self-contained checks for month, day, and year
533
+ const checks = [
534
+ monthValueIsValid(dateParts.month),
535
+ dayValueIsValid(dateParts.day),
536
+ yearIsValid(dateParts.year)
537
+ ];
538
+
539
+ // If any of the checks failed, the date format does not match and the result is invalid
540
+ const isValid = checks.every((check) => check === true);
541
+
542
+ // If the check is invalid, return false
543
+ if (!isValid) {
544
+ return false;
545
+ }
546
+
547
+ // Default case
548
+ return true;
549
+ };
550
+ }
551
+ };
552
+
553
+ // Export a class instance
554
+ const dateUtilities$1 = new AuroDateUtilities$1();
555
+
556
+ // Export the class instance methods individually
557
+ const {
558
+ datesMatch: datesMatch$1,
559
+ validDateStr: validDateStr$1,
560
+ dateAndFormatMatch: dateAndFormatMatch$1,
561
+ minDay: minDay$1,
562
+ minMonth: minMonth$1,
563
+ minYear: minYear$1,
564
+ maxDay: maxDay$1,
565
+ maxMonth: maxMonth$1,
566
+ maxYear: maxYear$1
567
+ } = dateUtilities$1;
568
+
569
+ const {
570
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
571
+ parseDate: parseDate$1,
572
+ getDateAsString: getDateAsString$1
573
+ } = dateFormatter$1;
574
+
167
575
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
168
576
  // See LICENSE in the project root for license information.
169
577
 
170
578
 
171
579
  let AuroFormValidation$1 = class AuroFormValidation {
580
+
172
581
  constructor() {
173
582
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
174
583
  }
@@ -260,17 +669,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
260
669
  ]
261
670
  }
262
671
  };
263
-
672
+
264
673
  let elementType;
265
674
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
266
675
  elementType = 'input';
267
676
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
268
677
  elementType = 'counter';
269
678
  }
270
-
679
+
271
680
  if (elementType) {
272
681
  const rules = validationRules[elementType];
273
-
682
+
274
683
  if (rules) {
275
684
  Object.values(rules).flat().forEach(rule => {
276
685
  if (rule.check(elem)) {
@@ -296,48 +705,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
296
705
  if (!elem.value.match(emailRegex)) {
297
706
  elem.validity = 'patternMismatch';
298
707
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
708
+ return;
299
709
  }
300
710
  } else if (elem.type === 'credit-card') {
301
711
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
302
712
  elem.validity = 'tooShort';
303
713
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
714
+ return;
304
715
  }
305
716
  } else if (elem.type === 'number') {
306
717
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
307
718
  elem.validity = 'rangeOverflow';
308
719
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
720
+ return;
309
721
  }
310
722
 
311
723
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
312
724
  elem.validity = 'rangeUnderflow';
313
725
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
726
+ return;
314
727
  }
315
- } else if (elem.type === 'date') {
316
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
728
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
729
+
730
+ // Guard Clause: if the value is too short
731
+ if (elem.value.length < elem.lengthForType) {
732
+
317
733
  elem.validity = 'tooShort';
318
734
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
319
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
320
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
321
- const valueDate = new Date(formattedValue.dateForComparison);
735
+ return;
736
+ }
737
+
738
+ // Guard Clause: If the value is too long for the type
739
+ if (elem.value?.length > elem.lengthForType) {
740
+
741
+ elem.validity = 'tooLong';
742
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
743
+ return;
744
+ }
745
+
746
+ // Validate that the date passed was the correct format
747
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
748
+ elem.validity = 'patternMismatch';
749
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
750
+ return;
751
+ }
752
+
753
+ // Validate that the date passed was a valid date
754
+ if (!validDateStr$1(elem.value, elem.format)) {
755
+ elem.validity = 'invalidDate';
756
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
757
+ return;
758
+ }
322
759
 
323
- // validate max
324
- if (elem.max?.length === elem.lengthForType) {
325
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
760
+ // Perform the rest of the validation
761
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
762
+ const valueDate = new Date(formattedValue);
326
763
 
327
- if (valueDate > maxDate) {
328
- elem.validity = 'rangeOverflow';
329
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
330
- }
764
+ // // Validate max date
765
+ if (elem.max?.length === elem.lengthForType) {
766
+
767
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
768
+
769
+ if (valueDate > maxDate) {
770
+ elem.validity = 'rangeOverflow';
771
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
772
+ return;
331
773
  }
774
+ }
332
775
 
333
- // validate min
334
- if (elem.min?.length === elem.lengthForType) {
335
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
776
+ // Validate min date
777
+ if (elem.min?.length === elem.lengthForType) {
778
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
336
779
 
337
- if (valueDate < minDate) {
338
- elem.validity = 'rangeUnderflow';
339
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
340
- }
780
+ if (valueDate < minDate) {
781
+ elem.validity = 'rangeUnderflow';
782
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
783
+ return;
341
784
  }
342
785
  }
343
786
  }
@@ -456,7 +899,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
456
899
  if (input.validationMessage.length > 0) {
457
900
  elem.errorMessage = input.validationMessage;
458
901
  }
459
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
902
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
460
903
  const firstInput = this.inputElements[0];
461
904
 
462
905
  if (firstInput.validationMessage.length > 0) {
@@ -2337,7 +2780,7 @@ class AuroFloatingUI {
2337
2780
  /**
2338
2781
  * @private
2339
2782
  * getting called on 'blur' in trigger or `focusin` in document
2340
- *
2783
+ *
2341
2784
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2342
2785
  * This method checks if the currently active element is still within the trigger or bib.
2343
2786
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -2453,7 +2896,7 @@ class AuroFloatingUI {
2453
2896
  // Close any other dropdown that is already open
2454
2897
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2455
2898
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2456
- existedVisibleFloatingUI.isPopoverVisible &&
2899
+ existedVisibleFloatingUI.element.isPopoverVisible &&
2457
2900
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2458
2901
  document.expandedAuroFloater.hideBib();
2459
2902
  }
@@ -2629,7 +3072,7 @@ class AuroFloatingUI {
2629
3072
  this.id = window.crypto.randomUUID();
2630
3073
  this.element.setAttribute('id', this.id);
2631
3074
  }
2632
-
3075
+
2633
3076
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2634
3077
  }
2635
3078
 
@@ -2682,7 +3125,7 @@ class AuroFloatingUI {
2682
3125
  if (this.element.bib) {
2683
3126
  this.element.shadowRoot.append(this.element.bib);
2684
3127
  }
2685
-
3128
+
2686
3129
  // Remove event & keyboard listeners
2687
3130
  if (this.element?.trigger) {
2688
3131
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -3411,6 +3854,7 @@ var helpTextVersion$1 = '1.0.0';
3411
3854
  * @csspart helpText - The helpText content container.
3412
3855
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3413
3856
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3857
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3414
3858
  */
3415
3859
  class AuroDropdown extends r$1 {
3416
3860
  constructor() {
@@ -3456,7 +3900,9 @@ class AuroDropdown extends r$1 {
3456
3900
  this.rounded = false;
3457
3901
  this.tabIndex = 0;
3458
3902
  this.noToggle = false;
3903
+ this.a11yAutocomplete = 'none';
3459
3904
  this.labeled = true;
3905
+ this.a11yRole = 'combobox';
3460
3906
  this.onDark = false;
3461
3907
 
3462
3908
  // floaterConfig
@@ -3592,6 +4038,16 @@ class AuroDropdown extends r$1 {
3592
4038
  type: Number
3593
4039
  },
3594
4040
 
4041
+ /**
4042
+ * The unique ID for the dropdown bib element.
4043
+ * @private
4044
+ */
4045
+ dropdownId: {
4046
+ type: String,
4047
+ reflect: false,
4048
+ attribute: false
4049
+ },
4050
+
3595
4051
  /**
3596
4052
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3597
4053
  */
@@ -3754,6 +4210,23 @@ class AuroDropdown extends r$1 {
3754
4210
  */
3755
4211
  tabIndex: {
3756
4212
  type: Number
4213
+ },
4214
+
4215
+ /**
4216
+ * The value for the role attribute of the trigger element.
4217
+ */
4218
+ a11yRole: {
4219
+ type: String || undefined,
4220
+ attribute: false,
4221
+ reflect: false
4222
+ },
4223
+
4224
+ /**
4225
+ * The value for the aria-autocomplete attribute of the trigger element.
4226
+ */
4227
+ a11yAutocomplete: {
4228
+ type: String,
4229
+ attribute: false,
3757
4230
  }
3758
4231
  };
3759
4232
  }
@@ -3804,7 +4277,22 @@ class AuroDropdown extends r$1 {
3804
4277
  }
3805
4278
 
3806
4279
  firstUpdated() {
4280
+
4281
+ // Configure the floater to, this will generate the ID for the bib
3807
4282
  this.floater.configure(this, 'auroDropdown');
4283
+
4284
+ /**
4285
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4286
+ * @event auroDropdown-idAdded
4287
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4288
+ */
4289
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4290
+
4291
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4292
+ if (!this.triggerContentFocusable) {
4293
+ this.dropdownId = this.floater.element.id;
4294
+ }
4295
+
3808
4296
  this.bibContent = this.floater.element.bib;
3809
4297
 
3810
4298
  // Add the tag name as an attribute if it is different than the component name
@@ -3956,6 +4444,30 @@ class AuroDropdown extends r$1 {
3956
4444
  });
3957
4445
  }
3958
4446
 
4447
+ /*
4448
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4449
+ * @private
4450
+ * @method setTriggerAriaAttributes
4451
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4452
+ */
4453
+ clearTriggerA11yAttributes(triggerElement) {
4454
+
4455
+ if (!triggerElement || !triggerElement.removeAttribute) {
4456
+ return;
4457
+ }
4458
+
4459
+ // Reset appropriate attributes for a11y
4460
+ triggerElement.removeAttribute('aria-labelledby');
4461
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4462
+ triggerElement.removeAttribute('id');
4463
+ }
4464
+ triggerElement.removeAttribute('role');
4465
+ triggerElement.removeAttribute('aria-expanded');
4466
+
4467
+ triggerElement.removeAttribute('aria-controls');
4468
+ triggerElement.removeAttribute('aria-autocomplete');
4469
+ }
4470
+
3959
4471
  /**
3960
4472
  * Handles changes to the trigger content slot and updates related properties.
3961
4473
  *
@@ -3969,32 +4481,41 @@ class AuroDropdown extends r$1 {
3969
4481
  * @returns {void}
3970
4482
  */
3971
4483
  handleTriggerContentSlotChange(event) {
4484
+
3972
4485
  this.floater.handleTriggerTabIndex();
3973
4486
 
4487
+ // Get the trigger
4488
+ const trigger = this.shadowRoot.querySelector('#trigger');
4489
+
4490
+ // Get the trigger slot
3974
4491
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
3975
4492
 
4493
+ // If there's a trigger slot
3976
4494
  if (triggerSlot) {
3977
4495
 
4496
+ // Get the content nodes to see if there are any children
3978
4497
  const triggerContentNodes = triggerSlot.assignedNodes();
3979
4498
 
4499
+ // If there are children
3980
4500
  if (triggerContentNodes) {
3981
4501
 
3982
- triggerContentNodes.forEach((node) => {
3983
- if (!this.triggerContentFocusable) {
3984
- this.triggerContentFocusable = this.containsFocusableElement(node);
3985
- }
3986
- });
3987
- }
3988
- }
4502
+ // See if any of them are focusable elemeents
4503
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
3989
4504
 
3990
- const trigger = this.shadowRoot.querySelector('#trigger');
4505
+ // If any of them are focusable elements
4506
+ if (this.triggerContentFocusable) {
3991
4507
 
3992
- if (!this.triggerContentFocusable) {
3993
- trigger.setAttribute('tabindex', '0');
3994
- trigger.setAttribute('role', 'button');
3995
- } else {
3996
- trigger.removeAttribute('tabindex');
3997
- trigger.removeAttribute('role');
4508
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4509
+ this.clearTriggerA11yAttributes(trigger);
4510
+
4511
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4512
+ trigger.removeAttribute('tabindex');
4513
+ } else {
4514
+
4515
+ // Add the tabindex to the trigger so that it's in the focus flow
4516
+ trigger.setAttribute('tabindex', '0');
4517
+ }
4518
+ }
3998
4519
  }
3999
4520
 
4000
4521
  if (event) {
@@ -4004,6 +4525,7 @@ class AuroDropdown extends r$1 {
4004
4525
 
4005
4526
  if (this.triggerContentSlot) {
4006
4527
  this.setupTriggerFocusEventBinding();
4528
+
4007
4529
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
4008
4530
  if (slot.textContent.trim()) {
4009
4531
  return true;
@@ -4071,10 +4593,13 @@ class AuroDropdown extends r$1 {
4071
4593
  id="trigger"
4072
4594
  class="trigger"
4073
4595
  part="trigger"
4074
- aria-labelledby="triggerLabel"
4075
4596
  tabindex="${this.tabIndex}"
4076
4597
  ?showBorder="${this.showTriggerBorders}"
4077
- >
4598
+ role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4599
+ aria-expanded="${o(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4600
+ aria-controls="${o(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4601
+ aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4602
+ >
4078
4603
  <div class="triggerContentWrapper">
4079
4604
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4080
4605
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4108,12 +4633,12 @@ class AuroDropdown extends r$1 {
4108
4633
  <div id="bibSizer" part="size"></div>
4109
4634
  <${this.dropdownBibTag}
4110
4635
  id="bib"
4111
- role="tooltip"
4112
4636
  ?data-show="${this.isPopoverVisible}"
4113
4637
  ?isfullscreen="${this.isBibFullscreen}"
4114
4638
  ?common="${this.common}"
4115
4639
  ?rounded="${this.common || this.rounded}"
4116
- ?inset="${this.common || this.inset}">
4640
+ ?inset="${this.common || this.inset}"
4641
+ >
4117
4642
  </${this.dropdownBibTag}>
4118
4643
  </div>
4119
4644
  `;
@@ -8104,6 +8629,414 @@ class AuroInputUtilities {
8104
8629
  }
8105
8630
  }
8106
8631
 
8632
+ class DateFormatter {
8633
+
8634
+ constructor() {
8635
+
8636
+ /**
8637
+ * @description Parses a date string into its components.
8638
+ * @param {string} dateStr - Date string to parse.
8639
+ * @param {string} format - Date format to parse.
8640
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
8641
+ */
8642
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
8643
+
8644
+ // Guard Clause: Date string is defined
8645
+ if (!dateStr) {
8646
+ return undefined;
8647
+ }
8648
+
8649
+ // Assume the separator is a "/" a defined in our code base
8650
+ const separator = '/';
8651
+
8652
+ // Get the parts of the date and format
8653
+ const valueParts = dateStr.split(separator);
8654
+ const formatParts = format.split(separator);
8655
+
8656
+ // Check if the value and format have the correct number of parts
8657
+ if (valueParts.length !== formatParts.length) {
8658
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
8659
+ }
8660
+
8661
+ // Holds the result to be returned
8662
+ const result = formatParts.reduce((acc, part, index) => {
8663
+ const value = valueParts[index];
8664
+
8665
+ if ((/m/iu).test(part)) {
8666
+ acc.month = value;
8667
+ } else if ((/d/iu).test(part)) {
8668
+ acc.day = value;
8669
+ } else if ((/y/iu).test(part)) {
8670
+ acc.year = value;
8671
+ }
8672
+
8673
+ return acc;
8674
+ }, {});
8675
+
8676
+ // If we found all the parts, return the result
8677
+ if (result.month && result.year) {
8678
+ return result;
8679
+ }
8680
+
8681
+ // Throw an error to let the dev know we were unable to parse the date string
8682
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
8683
+ };
8684
+
8685
+ /**
8686
+ * Convert a date object to string format.
8687
+ * @param {Object} date - Date to convert to string.
8688
+ * @returns {Object} Returns the date as a string.
8689
+ */
8690
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
8691
+ year: "numeric",
8692
+ month: "2-digit",
8693
+ day: "2-digit",
8694
+ });
8695
+
8696
+ /**
8697
+ * Converts a date string to a North American date format.
8698
+ * @param {String} dateStr - Date to validate.
8699
+ * @param {String} format - Date format to validate against.
8700
+ * @returns {Boolean}
8701
+ */
8702
+ this.toNorthAmericanFormat = (dateStr, format) => {
8703
+
8704
+ if (format === 'mm/dd/yyyy') {
8705
+ return dateStr;
8706
+ }
8707
+
8708
+ const parsedDate = this.parseDate(dateStr, format);
8709
+
8710
+ if (!parsedDate) {
8711
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
8712
+ }
8713
+
8714
+ const { month, day, year } = parsedDate;
8715
+
8716
+ const dateParts = [];
8717
+ if (month) {
8718
+ dateParts.push(month);
8719
+ }
8720
+
8721
+ if (day) {
8722
+ dateParts.push(day);
8723
+ }
8724
+
8725
+ if (year) {
8726
+ dateParts.push(year);
8727
+ }
8728
+
8729
+ return dateParts.join('/');
8730
+ };
8731
+ }
8732
+ }
8733
+ const dateFormatter = new DateFormatter();
8734
+
8735
+ // filepath: dateConstraints.mjs
8736
+ const DATE_UTIL_CONSTRAINTS = {
8737
+ maxDay: 31,
8738
+ maxMonth: 12,
8739
+ maxYear: 2400,
8740
+ minDay: 1,
8741
+ minMonth: 1,
8742
+ minYear: 1900,
8743
+ };
8744
+
8745
+ class AuroDateUtilitiesBase {
8746
+
8747
+ /**
8748
+ * @description The maximum day value allowed by the various utilities in this class.
8749
+ * @readonly
8750
+ * @type {Number}
8751
+ */
8752
+ get maxDay() {
8753
+ return DATE_UTIL_CONSTRAINTS.maxDay;
8754
+ }
8755
+
8756
+ /**
8757
+ * @description The maximum month value allowed by the various utilities in this class.
8758
+ * @readonly
8759
+ * @type {Number}
8760
+ */
8761
+ get maxMonth() {
8762
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
8763
+ }
8764
+
8765
+ /**
8766
+ * @description The maximum year value allowed by the various utilities in this class.
8767
+ * @readonly
8768
+ * @type {Number}
8769
+ */
8770
+ get maxYear() {
8771
+ return DATE_UTIL_CONSTRAINTS.maxYear;
8772
+ }
8773
+
8774
+ /**
8775
+ * @description The minimum day value allowed by the various utilities in this class.
8776
+ * @readonly
8777
+ * @type {Number}
8778
+ */
8779
+ get minDay() {
8780
+ return DATE_UTIL_CONSTRAINTS.minDay;
8781
+ }
8782
+
8783
+ /**
8784
+ * @description The minimum month value allowed by the various utilities in this class.
8785
+ * @readonly
8786
+ * @type {Number}
8787
+ */
8788
+ get minMonth() {
8789
+ return DATE_UTIL_CONSTRAINTS.minMonth;
8790
+ }
8791
+
8792
+ /**
8793
+ * @description The minimum year value allowed by the various utilities in this class.
8794
+ * @readonly
8795
+ * @type {Number}
8796
+ */
8797
+ get minYear() {
8798
+ return DATE_UTIL_CONSTRAINTS.minYear;
8799
+ }
8800
+ }
8801
+
8802
+ /* eslint-disable no-magic-numbers */
8803
+
8804
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
8805
+
8806
+ /**
8807
+ * Returns the current century.
8808
+ * @returns {String} The current century.
8809
+ */
8810
+ getCentury () {
8811
+ return String(new Date().getFullYear()).slice(0, 2);
8812
+ }
8813
+
8814
+ /**
8815
+ * Returns a four digit year.
8816
+ * @param {String} year - The year to convert to four digits.
8817
+ * @returns {String} The four digit year.
8818
+ */
8819
+ getFourDigitYear (year) {
8820
+
8821
+ const strYear = String(year).trim();
8822
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
8823
+ }
8824
+
8825
+ constructor() {
8826
+
8827
+ super();
8828
+
8829
+ /**
8830
+ * Compares two dates to see if they match.
8831
+ * @param {Object} date1 - First date to compare.
8832
+ * @param {Object} date2 - Second date to compare.
8833
+ * @returns {Boolean} Returns true if the dates match.
8834
+ */
8835
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
8836
+
8837
+ /**
8838
+ * Returns true if value passed in is a valid date.
8839
+ * @param {String} date - Date to validate.
8840
+ * @param {String} format - Date format to validate against.
8841
+ * @returns {Boolean}
8842
+ */
8843
+ this.validDateStr = (date, format) => {
8844
+
8845
+ // The length we expect the date string to be
8846
+ const dateStrLength = format.length;
8847
+
8848
+ // Guard Clause: Date and format are defined
8849
+ if (typeof date === "undefined" || typeof format === "undefined") {
8850
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
8851
+ }
8852
+
8853
+ // Guard Clause: Date should be of type string
8854
+ if (typeof date !== "string") {
8855
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
8856
+ }
8857
+
8858
+ // Guard Clause: Format should be of type string
8859
+ if (typeof format !== "string") {
8860
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
8861
+ }
8862
+
8863
+ // Guard Clause: Length is what we expect it to be
8864
+ if (date.length !== dateStrLength) {
8865
+ return false;
8866
+ }
8867
+ // Get a formatted date string and parse it
8868
+ const dateParts = dateFormatter.parseDate(date, format);
8869
+
8870
+ // Guard Clause: Date parse succeeded
8871
+ if (!dateParts) {
8872
+ return false;
8873
+ }
8874
+
8875
+ // Create the expected date string based on the date parts
8876
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
8877
+
8878
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
8879
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
8880
+
8881
+ // Get the date string of the date object we created from the string date
8882
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
8883
+
8884
+ // Guard Clause: Generated date matches date string input
8885
+ if (expectedDateStr !== actualDateStr) {
8886
+ return false;
8887
+ }
8888
+
8889
+ // If we passed all other checks, we can assume the date is valid
8890
+ return true;
8891
+ };
8892
+
8893
+ /**
8894
+ * Determines if a string date value matches the format provided.
8895
+ * @param {string} value = The date string value.
8896
+ * @param { string} format = The date format to match against.
8897
+ * @returns {boolean}
8898
+ */
8899
+ this.dateAndFormatMatch = (value, format) => {
8900
+
8901
+ // Ensure we have both values we need to do the comparison
8902
+ if (!value || !format) {
8903
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
8904
+ }
8905
+
8906
+ // If the lengths are different, they cannot match
8907
+ if (value.length !== format.length) {
8908
+ return false;
8909
+ }
8910
+
8911
+ // Get the parts of the date
8912
+ const dateParts = dateFormatter.parseDate(value, format);
8913
+
8914
+ // Validator for day
8915
+ const dayValueIsValid = (day) => {
8916
+
8917
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
8918
+ if (!dateParts.day) {
8919
+ return true;
8920
+ }
8921
+
8922
+ // Guard clause: ensure day exists.
8923
+ if (!day) {
8924
+ return false;
8925
+ }
8926
+
8927
+ // Convert day to number
8928
+ const numDay = Number.parseInt(day, 10);
8929
+
8930
+ // Guard clause: ensure day is a valid integer
8931
+ if (Number.isNaN(numDay)) {
8932
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
8933
+ }
8934
+
8935
+ // Guard clause: ensure day is within the valid range
8936
+ if (numDay < this.minDay || numDay > this.maxDay) {
8937
+ return false;
8938
+ }
8939
+
8940
+ // Default return
8941
+ return true;
8942
+ };
8943
+
8944
+ // Validator for month
8945
+ const monthValueIsValid = (month) => {
8946
+
8947
+ // Guard clause: ensure month exists.
8948
+ if (!month) {
8949
+ return false;
8950
+ }
8951
+
8952
+ // Convert month to number
8953
+ const numMonth = Number.parseInt(month, 10);
8954
+
8955
+ // Guard clause: ensure month is a valid integer
8956
+ if (Number.isNaN(numMonth)) {
8957
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
8958
+ }
8959
+
8960
+ // Guard clause: ensure month is within the valid range
8961
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
8962
+ return false;
8963
+ }
8964
+
8965
+ // Default return
8966
+ return true;
8967
+ };
8968
+
8969
+ // Validator for year
8970
+ const yearIsValid = (_year) => {
8971
+
8972
+ // Guard clause: ensure year exists.
8973
+ if (!_year) {
8974
+ return false;
8975
+ }
8976
+
8977
+ // Get the full year
8978
+ const year = this.getFourDigitYear(_year);
8979
+
8980
+ // Convert year to number
8981
+ const numYear = Number.parseInt(year, 10);
8982
+
8983
+ // Guard clause: ensure year is a valid integer
8984
+ if (Number.isNaN(numYear)) {
8985
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
8986
+ }
8987
+
8988
+ // Guard clause: ensure year is within the valid range
8989
+ if (numYear < this.minYear || numYear > this.maxYear) {
8990
+ return false;
8991
+ }
8992
+
8993
+ // Default return
8994
+ return true;
8995
+ };
8996
+
8997
+ // Self-contained checks for month, day, and year
8998
+ const checks = [
8999
+ monthValueIsValid(dateParts.month),
9000
+ dayValueIsValid(dateParts.day),
9001
+ yearIsValid(dateParts.year)
9002
+ ];
9003
+
9004
+ // If any of the checks failed, the date format does not match and the result is invalid
9005
+ const isValid = checks.every((check) => check === true);
9006
+
9007
+ // If the check is invalid, return false
9008
+ if (!isValid) {
9009
+ return false;
9010
+ }
9011
+
9012
+ // Default case
9013
+ return true;
9014
+ };
9015
+ }
9016
+ }
9017
+
9018
+ // Export a class instance
9019
+ const dateUtilities = new AuroDateUtilities();
9020
+
9021
+ // Export the class instance methods individually
9022
+ const {
9023
+ datesMatch,
9024
+ validDateStr,
9025
+ dateAndFormatMatch,
9026
+ minDay,
9027
+ minMonth,
9028
+ minYear,
9029
+ maxDay,
9030
+ maxMonth,
9031
+ maxYear
9032
+ } = dateUtilities;
9033
+
9034
+ const {
9035
+ toNorthAmericanFormat,
9036
+ parseDate,
9037
+ getDateAsString
9038
+ } = dateFormatter;
9039
+
8107
9040
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
8108
9041
  // See LICENSE in the project root for license information.
8109
9042
 
@@ -8179,6 +9112,7 @@ let AuroLibraryRuntimeUtils$1$1 = class AuroLibraryRuntimeUtils {
8179
9112
 
8180
9113
 
8181
9114
  class AuroFormValidation {
9115
+
8182
9116
  constructor() {
8183
9117
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1$1();
8184
9118
  }
@@ -8270,17 +9204,17 @@ class AuroFormValidation {
8270
9204
  ]
8271
9205
  }
8272
9206
  };
8273
-
9207
+
8274
9208
  let elementType;
8275
9209
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
8276
9210
  elementType = 'input';
8277
9211
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
8278
9212
  elementType = 'counter';
8279
9213
  }
8280
-
9214
+
8281
9215
  if (elementType) {
8282
9216
  const rules = validationRules[elementType];
8283
-
9217
+
8284
9218
  if (rules) {
8285
9219
  Object.values(rules).flat().forEach(rule => {
8286
9220
  if (rule.check(elem)) {
@@ -8306,48 +9240,82 @@ class AuroFormValidation {
8306
9240
  if (!elem.value.match(emailRegex)) {
8307
9241
  elem.validity = 'patternMismatch';
8308
9242
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9243
+ return;
8309
9244
  }
8310
9245
  } else if (elem.type === 'credit-card') {
8311
9246
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
8312
9247
  elem.validity = 'tooShort';
8313
9248
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9249
+ return;
8314
9250
  }
8315
9251
  } else if (elem.type === 'number') {
8316
9252
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
8317
9253
  elem.validity = 'rangeOverflow';
8318
9254
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9255
+ return;
8319
9256
  }
8320
9257
 
8321
9258
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
8322
9259
  elem.validity = 'rangeUnderflow';
8323
9260
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9261
+ return;
8324
9262
  }
8325
- } else if (elem.type === 'date') {
8326
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
9263
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
9264
+
9265
+ // Guard Clause: if the value is too short
9266
+ if (elem.value.length < elem.lengthForType) {
9267
+
8327
9268
  elem.validity = 'tooShort';
8328
9269
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
8329
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
8330
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
8331
- const valueDate = new Date(formattedValue.dateForComparison);
9270
+ return;
9271
+ }
9272
+
9273
+ // Guard Clause: If the value is too long for the type
9274
+ if (elem.value?.length > elem.lengthForType) {
9275
+
9276
+ elem.validity = 'tooLong';
9277
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9278
+ return;
9279
+ }
9280
+
9281
+ // Validate that the date passed was the correct format
9282
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
9283
+ elem.validity = 'patternMismatch';
9284
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
9285
+ return;
9286
+ }
9287
+
9288
+ // Validate that the date passed was a valid date
9289
+ if (!validDateStr(elem.value, elem.format)) {
9290
+ elem.validity = 'invalidDate';
9291
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
9292
+ return;
9293
+ }
8332
9294
 
8333
- // validate max
8334
- if (elem.max?.length === elem.lengthForType) {
8335
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
9295
+ // Perform the rest of the validation
9296
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
9297
+ const valueDate = new Date(formattedValue);
8336
9298
 
8337
- if (valueDate > maxDate) {
8338
- elem.validity = 'rangeOverflow';
8339
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
8340
- }
9299
+ // // Validate max date
9300
+ if (elem.max?.length === elem.lengthForType) {
9301
+
9302
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
9303
+
9304
+ if (valueDate > maxDate) {
9305
+ elem.validity = 'rangeOverflow';
9306
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9307
+ return;
8341
9308
  }
9309
+ }
8342
9310
 
8343
- // validate min
8344
- if (elem.min?.length === elem.lengthForType) {
8345
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
9311
+ // Validate min date
9312
+ if (elem.min?.length === elem.lengthForType) {
9313
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
8346
9314
 
8347
- if (valueDate < minDate) {
8348
- elem.validity = 'rangeUnderflow';
8349
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
8350
- }
9315
+ if (valueDate < minDate) {
9316
+ elem.validity = 'rangeUnderflow';
9317
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9318
+ return;
8351
9319
  }
8352
9320
  }
8353
9321
  }
@@ -8466,7 +9434,7 @@ class AuroFormValidation {
8466
9434
  if (input.validationMessage.length > 0) {
8467
9435
  elem.errorMessage = input.validationMessage;
8468
9436
  }
8469
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
9437
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
8470
9438
  const firstInput = this.inputElements[0];
8471
9439
 
8472
9440
  if (firstInput.validationMessage.length > 0) {
@@ -8588,6 +9556,33 @@ class BaseInput extends r$1 {
8588
9556
  static get properties() {
8589
9557
  return {
8590
9558
 
9559
+ /**
9560
+ * The value for the role attribute.
9561
+ */
9562
+ a11yRole: {
9563
+ type: String,
9564
+ attribute: true,
9565
+ reflect: true
9566
+ },
9567
+
9568
+ /**
9569
+ * The value for the aria-expanded attribute.
9570
+ */
9571
+ a11yExpanded: {
9572
+ type: Boolean,
9573
+ attribute: true,
9574
+ reflect: true
9575
+ },
9576
+
9577
+ /**
9578
+ * The value for the aria-controls attribute.
9579
+ */
9580
+ a11yControls: {
9581
+ type: String,
9582
+ attribute: true,
9583
+ reflect: true
9584
+ },
9585
+
8591
9586
  /**
8592
9587
  * If set, the label will remain fixed in the active position.
8593
9588
  */
@@ -9229,6 +10224,10 @@ class BaseInput extends r$1 {
9229
10224
  } else if (this.type === 'number') {
9230
10225
  this.inputMode = 'numeric';
9231
10226
  }
10227
+
10228
+ if (this.type === "date" && !this.format) {
10229
+ this.format = 'mm/dd/yyyy';
10230
+ }
9232
10231
  }
9233
10232
 
9234
10233
  /**
@@ -10472,6 +11471,7 @@ var helpTextVersion = '1.0.0';
10472
11471
 
10473
11472
  // build the component class
10474
11473
  class AuroInput extends BaseInput {
11474
+
10475
11475
  constructor() {
10476
11476
  super();
10477
11477
 
@@ -10584,7 +11584,7 @@ class AuroInput extends BaseInput {
10584
11584
  ?required="${this.required}"
10585
11585
  ?disabled="${this.disabled}"
10586
11586
  aria-describedby="${this.uniqueId}"
10587
- aria-invalid="${this.validity !== 'valid'}"
11587
+ ?aria-invalid="${this.validity !== 'valid'}"
10588
11588
  placeholder=${this.getPlaceholder()}
10589
11589
  lang="${o(this.lang)}"
10590
11590
  ?activeLabel="${this.activeLabel}"
@@ -10593,7 +11593,10 @@ class AuroInput extends BaseInput {
10593
11593
  autocapitalize="${o(this.autocapitalize ? this.autocapitalize : undefined)}"
10594
11594
  autocomplete="${o(this.autocomplete ? this.autocomplete : undefined)}"
10595
11595
  part="input"
10596
- />
11596
+ role="${o(this.a11yRole)}"
11597
+ aria-expanded="${o(this.a11yExpanded)}"
11598
+ aria-controls="${o(this.a11yControls)}"
11599
+ />
10597
11600
  </div>
10598
11601
  <div
10599
11602
  class="notificationIcons"
@@ -11585,6 +12588,7 @@ var styleCss$3 = i$5`.util_displayInline{display:inline}.util_displayInlineBlock
11585
12588
 
11586
12589
  // build the component class
11587
12590
  class AuroCombobox extends r$1 {
12591
+
11588
12592
  constructor() {
11589
12593
  super();
11590
12594
 
@@ -11596,6 +12600,8 @@ class AuroCombobox extends r$1 {
11596
12600
  * @returns {void} Internal defaults.
11597
12601
  */
11598
12602
  privateDefaults() {
12603
+ this.dropdownOpen = false;
12604
+ this.dropdownId = undefined;
11599
12605
  this.onDark = false;
11600
12606
 
11601
12607
  this.noFilter = false;
@@ -11675,6 +12681,26 @@ class AuroCombobox extends r$1 {
11675
12681
  reflect: true
11676
12682
  },
11677
12683
 
12684
+ /**
12685
+ * ID for the dropdown
12686
+ * @private
12687
+ */
12688
+ dropdownId: {
12689
+ type: String,
12690
+ reflect: false,
12691
+ attribute: false
12692
+ },
12693
+
12694
+ /**
12695
+ * Whether or not the dropdown is open
12696
+ * @private
12697
+ */
12698
+ dropdownOpen: {
12699
+ type: Boolean,
12700
+ reflect: false,
12701
+ attribute: false
12702
+ },
12703
+
11678
12704
  /**
11679
12705
  * When defined, sets persistent validity to `customError` and sets the validation message to the attribute value.
11680
12706
  */
@@ -11833,8 +12859,19 @@ class AuroCombobox extends r$1 {
11833
12859
  * @private
11834
12860
  */
11835
12861
  isDropdownFullscreen: {
11836
- type: Boolean
11837
- }
12862
+ type: Boolean,
12863
+ reflect: false
12864
+ },
12865
+
12866
+ /**
12867
+ * @private
12868
+ * specifies the currently active option
12869
+ */
12870
+ optionActive: {
12871
+ type: Object,
12872
+ reflect: false,
12873
+ attribute: false
12874
+ },
11838
12875
  };
11839
12876
  }
11840
12877
 
@@ -11986,6 +13023,18 @@ class AuroCombobox extends r$1 {
11986
13023
  * @returns {void}
11987
13024
  */
11988
13025
  configureDropdown() {
13026
+
13027
+ // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
13028
+ this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
13029
+ this.dropdownId = event.detail.id;
13030
+ });
13031
+
13032
+ // Listen for the dropdown to be shown or hidden
13033
+ this.dropdown.addEventListener("auroDropdown-toggled", (ev) => {
13034
+ this.dropdownOpen = ev.detail.expanded;
13035
+ });
13036
+
13037
+ // this.dropdown.addEventListener('auroDropdown-show', () => {
11989
13038
  this.menuWrapper = this.dropdown.querySelector('.menuWrapper');
11990
13039
  this.menuWrapper.append(this.menu);
11991
13040
 
@@ -11999,7 +13048,6 @@ class AuroCombobox extends r$1 {
11999
13048
  this.hideBib = this.hideBib.bind(this);
12000
13049
  this.bibtemplate.addEventListener('close-click', this.hideBib);
12001
13050
 
12002
- this.dropdown.setAttribute('role', 'combobox');
12003
13051
  this.dropdown.addEventListener('auroDropdown-triggerClick', () => {
12004
13052
  this.showBib();
12005
13053
  });
@@ -12015,7 +13063,6 @@ class AuroCombobox extends r$1 {
12015
13063
  this.isDropdownFullscreen = event.detail.strategy === 'fullscreen';
12016
13064
  setTimeout(this.transportInput);
12017
13065
  });
12018
-
12019
13066
  }
12020
13067
 
12021
13068
  /**
@@ -12509,6 +13556,10 @@ class AuroCombobox extends r$1 {
12509
13556
  ?noFlip="${this.noFlip}"
12510
13557
  disableEventShow>
12511
13558
  <${this.inputTag}
13559
+ .a11yRole="${"combobox"}"
13560
+ .a11yExpanded="${this.dropdownOpen}"
13561
+ .a11yControls="${this.dropdownId}"
13562
+ id="${this.id || 'auro-combobox-input'}"
12512
13563
  slot="trigger"
12513
13564
  bordered
12514
13565
  ?onDark="${this.onDark}"