@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
@@ -114,11 +114,420 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
114
114
  }
115
115
  };
116
116
 
117
+ let DateFormatter$1 = class DateFormatter {
118
+
119
+ constructor() {
120
+
121
+ /**
122
+ * @description Parses a date string into its components.
123
+ * @param {string} dateStr - Date string to parse.
124
+ * @param {string} format - Date format to parse.
125
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
126
+ */
127
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
128
+
129
+ // Guard Clause: Date string is defined
130
+ if (!dateStr) {
131
+ return undefined;
132
+ }
133
+
134
+ // Assume the separator is a "/" a defined in our code base
135
+ const separator = '/';
136
+
137
+ // Get the parts of the date and format
138
+ const valueParts = dateStr.split(separator);
139
+ const formatParts = format.split(separator);
140
+
141
+ // Check if the value and format have the correct number of parts
142
+ if (valueParts.length !== formatParts.length) {
143
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
144
+ }
145
+
146
+ // Holds the result to be returned
147
+ const result = formatParts.reduce((acc, part, index) => {
148
+ const value = valueParts[index];
149
+
150
+ if ((/m/iu).test(part)) {
151
+ acc.month = value;
152
+ } else if ((/d/iu).test(part)) {
153
+ acc.day = value;
154
+ } else if ((/y/iu).test(part)) {
155
+ acc.year = value;
156
+ }
157
+
158
+ return acc;
159
+ }, {});
160
+
161
+ // If we found all the parts, return the result
162
+ if (result.month && result.year) {
163
+ return result;
164
+ }
165
+
166
+ // Throw an error to let the dev know we were unable to parse the date string
167
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
168
+ };
169
+
170
+ /**
171
+ * Convert a date object to string format.
172
+ * @param {Object} date - Date to convert to string.
173
+ * @returns {Object} Returns the date as a string.
174
+ */
175
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
176
+ year: "numeric",
177
+ month: "2-digit",
178
+ day: "2-digit",
179
+ });
180
+
181
+ /**
182
+ * Converts a date string to a North American date format.
183
+ * @param {String} dateStr - Date to validate.
184
+ * @param {String} format - Date format to validate against.
185
+ * @returns {Boolean}
186
+ */
187
+ this.toNorthAmericanFormat = (dateStr, format) => {
188
+
189
+ if (format === 'mm/dd/yyyy') {
190
+ return dateStr;
191
+ }
192
+
193
+ const parsedDate = this.parseDate(dateStr, format);
194
+
195
+ if (!parsedDate) {
196
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
197
+ }
198
+
199
+ const { month, day, year } = parsedDate;
200
+
201
+ const dateParts = [];
202
+ if (month) {
203
+ dateParts.push(month);
204
+ }
205
+
206
+ if (day) {
207
+ dateParts.push(day);
208
+ }
209
+
210
+ if (year) {
211
+ dateParts.push(year);
212
+ }
213
+
214
+ return dateParts.join('/');
215
+ };
216
+ }
217
+ };
218
+ const dateFormatter$1 = new DateFormatter$1();
219
+
220
+ // filepath: dateConstraints.mjs
221
+ const DATE_UTIL_CONSTRAINTS$1 = {
222
+ maxDay: 31,
223
+ maxMonth: 12,
224
+ maxYear: 2400,
225
+ minDay: 1,
226
+ minMonth: 1,
227
+ minYear: 1900,
228
+ };
229
+
230
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
231
+
232
+ /**
233
+ * @description The maximum day value allowed by the various utilities in this class.
234
+ * @readonly
235
+ * @type {Number}
236
+ */
237
+ get maxDay() {
238
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
239
+ }
240
+
241
+ /**
242
+ * @description The maximum month value allowed by the various utilities in this class.
243
+ * @readonly
244
+ * @type {Number}
245
+ */
246
+ get maxMonth() {
247
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
248
+ }
249
+
250
+ /**
251
+ * @description The maximum year value allowed by the various utilities in this class.
252
+ * @readonly
253
+ * @type {Number}
254
+ */
255
+ get maxYear() {
256
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
257
+ }
258
+
259
+ /**
260
+ * @description The minimum day value allowed by the various utilities in this class.
261
+ * @readonly
262
+ * @type {Number}
263
+ */
264
+ get minDay() {
265
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
266
+ }
267
+
268
+ /**
269
+ * @description The minimum month value allowed by the various utilities in this class.
270
+ * @readonly
271
+ * @type {Number}
272
+ */
273
+ get minMonth() {
274
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
275
+ }
276
+
277
+ /**
278
+ * @description The minimum year value allowed by the various utilities in this class.
279
+ * @readonly
280
+ * @type {Number}
281
+ */
282
+ get minYear() {
283
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
284
+ }
285
+ };
286
+
287
+ /* eslint-disable no-magic-numbers */
288
+
289
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
290
+
291
+ /**
292
+ * Returns the current century.
293
+ * @returns {String} The current century.
294
+ */
295
+ getCentury () {
296
+ return String(new Date().getFullYear()).slice(0, 2);
297
+ }
298
+
299
+ /**
300
+ * Returns a four digit year.
301
+ * @param {String} year - The year to convert to four digits.
302
+ * @returns {String} The four digit year.
303
+ */
304
+ getFourDigitYear (year) {
305
+
306
+ const strYear = String(year).trim();
307
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
308
+ }
309
+
310
+ constructor() {
311
+
312
+ super();
313
+
314
+ /**
315
+ * Compares two dates to see if they match.
316
+ * @param {Object} date1 - First date to compare.
317
+ * @param {Object} date2 - Second date to compare.
318
+ * @returns {Boolean} Returns true if the dates match.
319
+ */
320
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
321
+
322
+ /**
323
+ * Returns true if value passed in is a valid date.
324
+ * @param {String} date - Date to validate.
325
+ * @param {String} format - Date format to validate against.
326
+ * @returns {Boolean}
327
+ */
328
+ this.validDateStr = (date, format) => {
329
+
330
+ // The length we expect the date string to be
331
+ const dateStrLength = format.length;
332
+
333
+ // Guard Clause: Date and format are defined
334
+ if (typeof date === "undefined" || typeof format === "undefined") {
335
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
336
+ }
337
+
338
+ // Guard Clause: Date should be of type string
339
+ if (typeof date !== "string") {
340
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
341
+ }
342
+
343
+ // Guard Clause: Format should be of type string
344
+ if (typeof format !== "string") {
345
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
346
+ }
347
+
348
+ // Guard Clause: Length is what we expect it to be
349
+ if (date.length !== dateStrLength) {
350
+ return false;
351
+ }
352
+ // Get a formatted date string and parse it
353
+ const dateParts = dateFormatter$1.parseDate(date, format);
354
+
355
+ // Guard Clause: Date parse succeeded
356
+ if (!dateParts) {
357
+ return false;
358
+ }
359
+
360
+ // Create the expected date string based on the date parts
361
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
362
+
363
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
364
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
365
+
366
+ // Get the date string of the date object we created from the string date
367
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
368
+
369
+ // Guard Clause: Generated date matches date string input
370
+ if (expectedDateStr !== actualDateStr) {
371
+ return false;
372
+ }
373
+
374
+ // If we passed all other checks, we can assume the date is valid
375
+ return true;
376
+ };
377
+
378
+ /**
379
+ * Determines if a string date value matches the format provided.
380
+ * @param {string} value = The date string value.
381
+ * @param { string} format = The date format to match against.
382
+ * @returns {boolean}
383
+ */
384
+ this.dateAndFormatMatch = (value, format) => {
385
+
386
+ // Ensure we have both values we need to do the comparison
387
+ if (!value || !format) {
388
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
389
+ }
390
+
391
+ // If the lengths are different, they cannot match
392
+ if (value.length !== format.length) {
393
+ return false;
394
+ }
395
+
396
+ // Get the parts of the date
397
+ const dateParts = dateFormatter$1.parseDate(value, format);
398
+
399
+ // Validator for day
400
+ const dayValueIsValid = (day) => {
401
+
402
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
403
+ if (!dateParts.day) {
404
+ return true;
405
+ }
406
+
407
+ // Guard clause: ensure day exists.
408
+ if (!day) {
409
+ return false;
410
+ }
411
+
412
+ // Convert day to number
413
+ const numDay = Number.parseInt(day, 10);
414
+
415
+ // Guard clause: ensure day is a valid integer
416
+ if (Number.isNaN(numDay)) {
417
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
418
+ }
419
+
420
+ // Guard clause: ensure day is within the valid range
421
+ if (numDay < this.minDay || numDay > this.maxDay) {
422
+ return false;
423
+ }
424
+
425
+ // Default return
426
+ return true;
427
+ };
428
+
429
+ // Validator for month
430
+ const monthValueIsValid = (month) => {
431
+
432
+ // Guard clause: ensure month exists.
433
+ if (!month) {
434
+ return false;
435
+ }
436
+
437
+ // Convert month to number
438
+ const numMonth = Number.parseInt(month, 10);
439
+
440
+ // Guard clause: ensure month is a valid integer
441
+ if (Number.isNaN(numMonth)) {
442
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
443
+ }
444
+
445
+ // Guard clause: ensure month is within the valid range
446
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
447
+ return false;
448
+ }
449
+
450
+ // Default return
451
+ return true;
452
+ };
453
+
454
+ // Validator for year
455
+ const yearIsValid = (_year) => {
456
+
457
+ // Guard clause: ensure year exists.
458
+ if (!_year) {
459
+ return false;
460
+ }
461
+
462
+ // Get the full year
463
+ const year = this.getFourDigitYear(_year);
464
+
465
+ // Convert year to number
466
+ const numYear = Number.parseInt(year, 10);
467
+
468
+ // Guard clause: ensure year is a valid integer
469
+ if (Number.isNaN(numYear)) {
470
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
471
+ }
472
+
473
+ // Guard clause: ensure year is within the valid range
474
+ if (numYear < this.minYear || numYear > this.maxYear) {
475
+ return false;
476
+ }
477
+
478
+ // Default return
479
+ return true;
480
+ };
481
+
482
+ // Self-contained checks for month, day, and year
483
+ const checks = [
484
+ monthValueIsValid(dateParts.month),
485
+ dayValueIsValid(dateParts.day),
486
+ yearIsValid(dateParts.year)
487
+ ];
488
+
489
+ // If any of the checks failed, the date format does not match and the result is invalid
490
+ const isValid = checks.every((check) => check === true);
491
+
492
+ // If the check is invalid, return false
493
+ if (!isValid) {
494
+ return false;
495
+ }
496
+
497
+ // Default case
498
+ return true;
499
+ };
500
+ }
501
+ };
502
+
503
+ // Export a class instance
504
+ const dateUtilities$1 = new AuroDateUtilities$1();
505
+
506
+ // Export the class instance methods individually
507
+ const {
508
+ datesMatch: datesMatch$1,
509
+ validDateStr: validDateStr$1,
510
+ dateAndFormatMatch: dateAndFormatMatch$1,
511
+ minDay: minDay$1,
512
+ minMonth: minMonth$1,
513
+ minYear: minYear$1,
514
+ maxDay: maxDay$1,
515
+ maxMonth: maxMonth$1,
516
+ maxYear: maxYear$1
517
+ } = dateUtilities$1;
518
+
519
+ const {
520
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
521
+ parseDate: parseDate$1,
522
+ getDateAsString: getDateAsString$1
523
+ } = dateFormatter$1;
524
+
117
525
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
118
526
  // See LICENSE in the project root for license information.
119
527
 
120
528
 
121
529
  let AuroFormValidation$1 = class AuroFormValidation {
530
+
122
531
  constructor() {
123
532
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
124
533
  }
@@ -210,17 +619,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
210
619
  ]
211
620
  }
212
621
  };
213
-
622
+
214
623
  let elementType;
215
624
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
216
625
  elementType = 'input';
217
626
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
218
627
  elementType = 'counter';
219
628
  }
220
-
629
+
221
630
  if (elementType) {
222
631
  const rules = validationRules[elementType];
223
-
632
+
224
633
  if (rules) {
225
634
  Object.values(rules).flat().forEach(rule => {
226
635
  if (rule.check(elem)) {
@@ -246,48 +655,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
246
655
  if (!elem.value.match(emailRegex)) {
247
656
  elem.validity = 'patternMismatch';
248
657
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
658
+ return;
249
659
  }
250
660
  } else if (elem.type === 'credit-card') {
251
661
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
252
662
  elem.validity = 'tooShort';
253
663
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
664
+ return;
254
665
  }
255
666
  } else if (elem.type === 'number') {
256
667
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
257
668
  elem.validity = 'rangeOverflow';
258
669
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
670
+ return;
259
671
  }
260
672
 
261
673
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
262
674
  elem.validity = 'rangeUnderflow';
263
675
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
676
+ return;
264
677
  }
265
- } else if (elem.type === 'date') {
266
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
678
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
679
+
680
+ // Guard Clause: if the value is too short
681
+ if (elem.value.length < elem.lengthForType) {
682
+
267
683
  elem.validity = 'tooShort';
268
684
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
269
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
270
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
271
- const valueDate = new Date(formattedValue.dateForComparison);
685
+ return;
686
+ }
687
+
688
+ // Guard Clause: If the value is too long for the type
689
+ if (elem.value?.length > elem.lengthForType) {
690
+
691
+ elem.validity = 'tooLong';
692
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
693
+ return;
694
+ }
695
+
696
+ // Validate that the date passed was the correct format
697
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
698
+ elem.validity = 'patternMismatch';
699
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
700
+ return;
701
+ }
702
+
703
+ // Validate that the date passed was a valid date
704
+ if (!validDateStr$1(elem.value, elem.format)) {
705
+ elem.validity = 'invalidDate';
706
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
707
+ return;
708
+ }
272
709
 
273
- // validate max
274
- if (elem.max?.length === elem.lengthForType) {
275
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
710
+ // Perform the rest of the validation
711
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
712
+ const valueDate = new Date(formattedValue);
276
713
 
277
- if (valueDate > maxDate) {
278
- elem.validity = 'rangeOverflow';
279
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
280
- }
714
+ // // Validate max date
715
+ if (elem.max?.length === elem.lengthForType) {
716
+
717
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
718
+
719
+ if (valueDate > maxDate) {
720
+ elem.validity = 'rangeOverflow';
721
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
722
+ return;
281
723
  }
724
+ }
282
725
 
283
- // validate min
284
- if (elem.min?.length === elem.lengthForType) {
285
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
726
+ // Validate min date
727
+ if (elem.min?.length === elem.lengthForType) {
728
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
286
729
 
287
- if (valueDate < minDate) {
288
- elem.validity = 'rangeUnderflow';
289
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
290
- }
730
+ if (valueDate < minDate) {
731
+ elem.validity = 'rangeUnderflow';
732
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
733
+ return;
291
734
  }
292
735
  }
293
736
  }
@@ -406,7 +849,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
406
849
  if (input.validationMessage.length > 0) {
407
850
  elem.errorMessage = input.validationMessage;
408
851
  }
409
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
852
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
410
853
  const firstInput = this.inputElements[0];
411
854
 
412
855
  if (firstInput.validationMessage.length > 0) {
@@ -2268,7 +2711,7 @@ class AuroFloatingUI {
2268
2711
  /**
2269
2712
  * @private
2270
2713
  * getting called on 'blur' in trigger or `focusin` in document
2271
- *
2714
+ *
2272
2715
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2273
2716
  * This method checks if the currently active element is still within the trigger or bib.
2274
2717
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -2384,7 +2827,7 @@ class AuroFloatingUI {
2384
2827
  // Close any other dropdown that is already open
2385
2828
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2386
2829
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2387
- existedVisibleFloatingUI.isPopoverVisible &&
2830
+ existedVisibleFloatingUI.element.isPopoverVisible &&
2388
2831
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2389
2832
  document.expandedAuroFloater.hideBib();
2390
2833
  }
@@ -2560,7 +3003,7 @@ class AuroFloatingUI {
2560
3003
  this.id = window.crypto.randomUUID();
2561
3004
  this.element.setAttribute('id', this.id);
2562
3005
  }
2563
-
3006
+
2564
3007
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2565
3008
  }
2566
3009
 
@@ -2613,7 +3056,7 @@ class AuroFloatingUI {
2613
3056
  if (this.element.bib) {
2614
3057
  this.element.shadowRoot.append(this.element.bib);
2615
3058
  }
2616
-
3059
+
2617
3060
  // Remove event & keyboard listeners
2618
3061
  if (this.element?.trigger) {
2619
3062
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -3003,7 +3446,6 @@ var tokensCss$1$1 = css`:host{--ds-auro-dropdown-label-text-color: var(--ds-basi
3003
3446
 
3004
3447
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
3005
3448
  const DESIGN_TOKEN_BREAKPOINT_OPTIONS = [
3006
- 'xl',
3007
3449
  'lg',
3008
3450
  'md',
3009
3451
  'sm',
@@ -3075,7 +3517,6 @@ class AuroDropdownBib extends LitElement {
3075
3517
 
3076
3518
  set mobileFullscreenBreakpoint(value) {
3077
3519
  // verify the defined breakpoint is valid and exit out if not
3078
- // 'disabled' is a design token breakpoint so it acts as our "undefined" value
3079
3520
  const validatedValue = DESIGN_TOKEN_BREAKPOINT_OPTIONS.includes(value) ? value : undefined;
3080
3521
  if (!validatedValue) {
3081
3522
  this._mobileBreakpointValue = undefined;
@@ -3344,6 +3785,7 @@ var helpTextVersion$1 = '1.0.0';
3344
3785
  * @csspart helpText - The helpText content container.
3345
3786
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3346
3787
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3788
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3347
3789
  */
3348
3790
  class AuroDropdown extends LitElement {
3349
3791
  constructor() {
@@ -3389,7 +3831,9 @@ class AuroDropdown extends LitElement {
3389
3831
  this.rounded = false;
3390
3832
  this.tabIndex = 0;
3391
3833
  this.noToggle = false;
3834
+ this.a11yAutocomplete = 'none';
3392
3835
  this.labeled = true;
3836
+ this.a11yRole = 'combobox';
3393
3837
  this.onDark = false;
3394
3838
 
3395
3839
  // floaterConfig
@@ -3525,6 +3969,16 @@ class AuroDropdown extends LitElement {
3525
3969
  type: Number
3526
3970
  },
3527
3971
 
3972
+ /**
3973
+ * The unique ID for the dropdown bib element.
3974
+ * @private
3975
+ */
3976
+ dropdownId: {
3977
+ type: String,
3978
+ reflect: false,
3979
+ attribute: false
3980
+ },
3981
+
3528
3982
  /**
3529
3983
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3530
3984
  */
@@ -3588,12 +4042,7 @@ class AuroDropdown extends LitElement {
3588
4042
  },
3589
4043
 
3590
4044
  /**
3591
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
3592
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
3593
- *
3594
- * When expanded, the dropdown will automatically display in fullscreen mode
3595
- * if the screen size is equal to or smaller than the selected breakpoint.
3596
- * @default sm
4045
+ * 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.
3597
4046
  */
3598
4047
  fullscreenBreakpoint: {
3599
4048
  type: String,
@@ -3692,6 +4141,23 @@ class AuroDropdown extends LitElement {
3692
4141
  */
3693
4142
  tabIndex: {
3694
4143
  type: Number
4144
+ },
4145
+
4146
+ /**
4147
+ * The value for the role attribute of the trigger element.
4148
+ */
4149
+ a11yRole: {
4150
+ type: String || undefined,
4151
+ attribute: false,
4152
+ reflect: false
4153
+ },
4154
+
4155
+ /**
4156
+ * The value for the aria-autocomplete attribute of the trigger element.
4157
+ */
4158
+ a11yAutocomplete: {
4159
+ type: String,
4160
+ attribute: false,
3695
4161
  }
3696
4162
  };
3697
4163
  }
@@ -3716,15 +4182,6 @@ class AuroDropdown extends LitElement {
3716
4182
  AuroLibraryRuntimeUtils$1$2.prototype.registerComponent(name, AuroDropdown);
3717
4183
  }
3718
4184
 
3719
- /**
3720
- * Accessor for reusing the focusable entity query string.
3721
- * @private
3722
- * @returns {string}
3723
- */
3724
- get focusableEntityQuery () {
3725
- return 'auro-input, [auro-input], auro-button, [auro-button], button, input';
3726
- }
3727
-
3728
4185
  connectedCallback() {
3729
4186
  super.connectedCallback();
3730
4187
  }
@@ -3738,8 +4195,6 @@ class AuroDropdown extends LitElement {
3738
4195
  updated(changedProperties) {
3739
4196
  this.floater.handleUpdate(changedProperties);
3740
4197
 
3741
- // Note: `disabled` is not a breakpoint (it is not a screen size),
3742
- // so it looks like we never consume this - however, dropdownBib handles this in the setter as "undefined"
3743
4198
  if (changedProperties.has('fullscreenBreakpoint')) {
3744
4199
  this.bibContent.mobileFullscreenBreakpoint = this.fullscreenBreakpoint;
3745
4200
  }
@@ -3753,7 +4208,22 @@ class AuroDropdown extends LitElement {
3753
4208
  }
3754
4209
 
3755
4210
  firstUpdated() {
4211
+
4212
+ // Configure the floater to, this will generate the ID for the bib
3756
4213
  this.floater.configure(this, 'auroDropdown');
4214
+
4215
+ /**
4216
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4217
+ * @event auroDropdown-idAdded
4218
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4219
+ */
4220
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4221
+
4222
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4223
+ if (!this.triggerContentFocusable) {
4224
+ this.dropdownId = this.floater.element.id;
4225
+ }
4226
+
3757
4227
  this.bibContent = this.floater.element.bib;
3758
4228
 
3759
4229
  // Add the tag name as an attribute if it is different than the component name
@@ -3875,7 +4345,7 @@ class AuroDropdown extends LitElement {
3875
4345
 
3876
4346
  this.triggerContentSlot.forEach((node) => {
3877
4347
  if (node.querySelectorAll) {
3878
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
4348
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
3879
4349
  auroElements.forEach((auroEl) => {
3880
4350
  auroEl.addEventListener('focus', this.bindFocusEventToTrigger);
3881
4351
  auroEl.addEventListener('blur', this.bindFocusEventToTrigger);
@@ -3896,7 +4366,7 @@ class AuroDropdown extends LitElement {
3896
4366
 
3897
4367
  this.triggerContentSlot.forEach((node) => {
3898
4368
  if (node.querySelectorAll) {
3899
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
4369
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
3900
4370
  auroElements.forEach((auroEl) => {
3901
4371
  auroEl.removeEventListener('focus', this.bindFocusEventToTrigger);
3902
4372
  auroEl.removeEventListener('blur', this.bindFocusEventToTrigger);
@@ -3905,6 +4375,30 @@ class AuroDropdown extends LitElement {
3905
4375
  });
3906
4376
  }
3907
4377
 
4378
+ /*
4379
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4380
+ * @private
4381
+ * @method setTriggerAriaAttributes
4382
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4383
+ */
4384
+ clearTriggerA11yAttributes(triggerElement) {
4385
+
4386
+ if (!triggerElement || !triggerElement.removeAttribute) {
4387
+ return;
4388
+ }
4389
+
4390
+ // Reset appropriate attributes for a11y
4391
+ triggerElement.removeAttribute('aria-labelledby');
4392
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4393
+ triggerElement.removeAttribute('id');
4394
+ }
4395
+ triggerElement.removeAttribute('role');
4396
+ triggerElement.removeAttribute('aria-expanded');
4397
+
4398
+ triggerElement.removeAttribute('aria-controls');
4399
+ triggerElement.removeAttribute('aria-autocomplete');
4400
+ }
4401
+
3908
4402
  /**
3909
4403
  * Handles changes to the trigger content slot and updates related properties.
3910
4404
  *
@@ -3918,32 +4412,41 @@ class AuroDropdown extends LitElement {
3918
4412
  * @returns {void}
3919
4413
  */
3920
4414
  handleTriggerContentSlotChange(event) {
4415
+
3921
4416
  this.floater.handleTriggerTabIndex();
3922
4417
 
4418
+ // Get the trigger
4419
+ const trigger = this.shadowRoot.querySelector('#trigger');
4420
+
4421
+ // Get the trigger slot
3923
4422
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
3924
4423
 
4424
+ // If there's a trigger slot
3925
4425
  if (triggerSlot) {
3926
4426
 
4427
+ // Get the content nodes to see if there are any children
3927
4428
  const triggerContentNodes = triggerSlot.assignedNodes();
3928
4429
 
4430
+ // If there are children
3929
4431
  if (triggerContentNodes) {
3930
4432
 
3931
- triggerContentNodes.forEach((node) => {
3932
- if (!this.triggerContentFocusable) {
3933
- this.triggerContentFocusable = this.containsFocusableElement(node);
3934
- }
3935
- });
3936
- }
3937
- }
4433
+ // See if any of them are focusable elemeents
4434
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
3938
4435
 
3939
- const trigger = this.shadowRoot.querySelector('#trigger');
4436
+ // If any of them are focusable elements
4437
+ if (this.triggerContentFocusable) {
3940
4438
 
3941
- if (!this.triggerContentFocusable) {
3942
- trigger.setAttribute('tabindex', '0');
3943
- trigger.setAttribute('role', 'button');
3944
- } else {
3945
- trigger.removeAttribute('tabindex');
3946
- trigger.removeAttribute('role');
4439
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4440
+ this.clearTriggerA11yAttributes(trigger);
4441
+
4442
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4443
+ trigger.removeAttribute('tabindex');
4444
+ } else {
4445
+
4446
+ // Add the tabindex to the trigger so that it's in the focus flow
4447
+ trigger.setAttribute('tabindex', '0');
4448
+ }
4449
+ }
3947
4450
  }
3948
4451
 
3949
4452
  if (event) {
@@ -3953,6 +4456,7 @@ class AuroDropdown extends LitElement {
3953
4456
 
3954
4457
  if (this.triggerContentSlot) {
3955
4458
  this.setupTriggerFocusEventBinding();
4459
+
3956
4460
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
3957
4461
  if (slot.textContent.trim()) {
3958
4462
  return true;
@@ -4020,10 +4524,13 @@ class AuroDropdown extends LitElement {
4020
4524
  id="trigger"
4021
4525
  class="trigger"
4022
4526
  part="trigger"
4023
- aria-labelledby="triggerLabel"
4024
4527
  tabindex="${this.tabIndex}"
4025
4528
  ?showBorder="${this.showTriggerBorders}"
4026
- >
4529
+ role="${ifDefined(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4530
+ aria-expanded="${ifDefined(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4531
+ aria-controls="${ifDefined(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4532
+ aria-labelledby="${ifDefined(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4533
+ >
4027
4534
  <div class="triggerContentWrapper">
4028
4535
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4029
4536
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4057,12 +4564,12 @@ class AuroDropdown extends LitElement {
4057
4564
  <div id="bibSizer" part="size"></div>
4058
4565
  <${this.dropdownBibTag}
4059
4566
  id="bib"
4060
- role="tooltip"
4061
4567
  ?data-show="${this.isPopoverVisible}"
4062
4568
  ?isfullscreen="${this.isBibFullscreen}"
4063
4569
  ?common="${this.common}"
4064
4570
  ?rounded="${this.common || this.rounded}"
4065
- ?inset="${this.common || this.inset}">
4571
+ ?inset="${this.common || this.inset}"
4572
+ >
4066
4573
  </${this.dropdownBibTag}>
4067
4574
  </div>
4068
4575
  `;
@@ -8040,6 +8547,414 @@ class AuroInputUtilities {
8040
8547
  }
8041
8548
  }
8042
8549
 
8550
+ class DateFormatter {
8551
+
8552
+ constructor() {
8553
+
8554
+ /**
8555
+ * @description Parses a date string into its components.
8556
+ * @param {string} dateStr - Date string to parse.
8557
+ * @param {string} format - Date format to parse.
8558
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
8559
+ */
8560
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
8561
+
8562
+ // Guard Clause: Date string is defined
8563
+ if (!dateStr) {
8564
+ return undefined;
8565
+ }
8566
+
8567
+ // Assume the separator is a "/" a defined in our code base
8568
+ const separator = '/';
8569
+
8570
+ // Get the parts of the date and format
8571
+ const valueParts = dateStr.split(separator);
8572
+ const formatParts = format.split(separator);
8573
+
8574
+ // Check if the value and format have the correct number of parts
8575
+ if (valueParts.length !== formatParts.length) {
8576
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
8577
+ }
8578
+
8579
+ // Holds the result to be returned
8580
+ const result = formatParts.reduce((acc, part, index) => {
8581
+ const value = valueParts[index];
8582
+
8583
+ if ((/m/iu).test(part)) {
8584
+ acc.month = value;
8585
+ } else if ((/d/iu).test(part)) {
8586
+ acc.day = value;
8587
+ } else if ((/y/iu).test(part)) {
8588
+ acc.year = value;
8589
+ }
8590
+
8591
+ return acc;
8592
+ }, {});
8593
+
8594
+ // If we found all the parts, return the result
8595
+ if (result.month && result.year) {
8596
+ return result;
8597
+ }
8598
+
8599
+ // Throw an error to let the dev know we were unable to parse the date string
8600
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
8601
+ };
8602
+
8603
+ /**
8604
+ * Convert a date object to string format.
8605
+ * @param {Object} date - Date to convert to string.
8606
+ * @returns {Object} Returns the date as a string.
8607
+ */
8608
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
8609
+ year: "numeric",
8610
+ month: "2-digit",
8611
+ day: "2-digit",
8612
+ });
8613
+
8614
+ /**
8615
+ * Converts a date string to a North American date format.
8616
+ * @param {String} dateStr - Date to validate.
8617
+ * @param {String} format - Date format to validate against.
8618
+ * @returns {Boolean}
8619
+ */
8620
+ this.toNorthAmericanFormat = (dateStr, format) => {
8621
+
8622
+ if (format === 'mm/dd/yyyy') {
8623
+ return dateStr;
8624
+ }
8625
+
8626
+ const parsedDate = this.parseDate(dateStr, format);
8627
+
8628
+ if (!parsedDate) {
8629
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
8630
+ }
8631
+
8632
+ const { month, day, year } = parsedDate;
8633
+
8634
+ const dateParts = [];
8635
+ if (month) {
8636
+ dateParts.push(month);
8637
+ }
8638
+
8639
+ if (day) {
8640
+ dateParts.push(day);
8641
+ }
8642
+
8643
+ if (year) {
8644
+ dateParts.push(year);
8645
+ }
8646
+
8647
+ return dateParts.join('/');
8648
+ };
8649
+ }
8650
+ }
8651
+ const dateFormatter = new DateFormatter();
8652
+
8653
+ // filepath: dateConstraints.mjs
8654
+ const DATE_UTIL_CONSTRAINTS = {
8655
+ maxDay: 31,
8656
+ maxMonth: 12,
8657
+ maxYear: 2400,
8658
+ minDay: 1,
8659
+ minMonth: 1,
8660
+ minYear: 1900,
8661
+ };
8662
+
8663
+ class AuroDateUtilitiesBase {
8664
+
8665
+ /**
8666
+ * @description The maximum day value allowed by the various utilities in this class.
8667
+ * @readonly
8668
+ * @type {Number}
8669
+ */
8670
+ get maxDay() {
8671
+ return DATE_UTIL_CONSTRAINTS.maxDay;
8672
+ }
8673
+
8674
+ /**
8675
+ * @description The maximum month value allowed by the various utilities in this class.
8676
+ * @readonly
8677
+ * @type {Number}
8678
+ */
8679
+ get maxMonth() {
8680
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
8681
+ }
8682
+
8683
+ /**
8684
+ * @description The maximum year value allowed by the various utilities in this class.
8685
+ * @readonly
8686
+ * @type {Number}
8687
+ */
8688
+ get maxYear() {
8689
+ return DATE_UTIL_CONSTRAINTS.maxYear;
8690
+ }
8691
+
8692
+ /**
8693
+ * @description The minimum day value allowed by the various utilities in this class.
8694
+ * @readonly
8695
+ * @type {Number}
8696
+ */
8697
+ get minDay() {
8698
+ return DATE_UTIL_CONSTRAINTS.minDay;
8699
+ }
8700
+
8701
+ /**
8702
+ * @description The minimum month value allowed by the various utilities in this class.
8703
+ * @readonly
8704
+ * @type {Number}
8705
+ */
8706
+ get minMonth() {
8707
+ return DATE_UTIL_CONSTRAINTS.minMonth;
8708
+ }
8709
+
8710
+ /**
8711
+ * @description The minimum year value allowed by the various utilities in this class.
8712
+ * @readonly
8713
+ * @type {Number}
8714
+ */
8715
+ get minYear() {
8716
+ return DATE_UTIL_CONSTRAINTS.minYear;
8717
+ }
8718
+ }
8719
+
8720
+ /* eslint-disable no-magic-numbers */
8721
+
8722
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
8723
+
8724
+ /**
8725
+ * Returns the current century.
8726
+ * @returns {String} The current century.
8727
+ */
8728
+ getCentury () {
8729
+ return String(new Date().getFullYear()).slice(0, 2);
8730
+ }
8731
+
8732
+ /**
8733
+ * Returns a four digit year.
8734
+ * @param {String} year - The year to convert to four digits.
8735
+ * @returns {String} The four digit year.
8736
+ */
8737
+ getFourDigitYear (year) {
8738
+
8739
+ const strYear = String(year).trim();
8740
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
8741
+ }
8742
+
8743
+ constructor() {
8744
+
8745
+ super();
8746
+
8747
+ /**
8748
+ * Compares two dates to see if they match.
8749
+ * @param {Object} date1 - First date to compare.
8750
+ * @param {Object} date2 - Second date to compare.
8751
+ * @returns {Boolean} Returns true if the dates match.
8752
+ */
8753
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
8754
+
8755
+ /**
8756
+ * Returns true if value passed in is a valid date.
8757
+ * @param {String} date - Date to validate.
8758
+ * @param {String} format - Date format to validate against.
8759
+ * @returns {Boolean}
8760
+ */
8761
+ this.validDateStr = (date, format) => {
8762
+
8763
+ // The length we expect the date string to be
8764
+ const dateStrLength = format.length;
8765
+
8766
+ // Guard Clause: Date and format are defined
8767
+ if (typeof date === "undefined" || typeof format === "undefined") {
8768
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
8769
+ }
8770
+
8771
+ // Guard Clause: Date should be of type string
8772
+ if (typeof date !== "string") {
8773
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
8774
+ }
8775
+
8776
+ // Guard Clause: Format should be of type string
8777
+ if (typeof format !== "string") {
8778
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
8779
+ }
8780
+
8781
+ // Guard Clause: Length is what we expect it to be
8782
+ if (date.length !== dateStrLength) {
8783
+ return false;
8784
+ }
8785
+ // Get a formatted date string and parse it
8786
+ const dateParts = dateFormatter.parseDate(date, format);
8787
+
8788
+ // Guard Clause: Date parse succeeded
8789
+ if (!dateParts) {
8790
+ return false;
8791
+ }
8792
+
8793
+ // Create the expected date string based on the date parts
8794
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
8795
+
8796
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
8797
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
8798
+
8799
+ // Get the date string of the date object we created from the string date
8800
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
8801
+
8802
+ // Guard Clause: Generated date matches date string input
8803
+ if (expectedDateStr !== actualDateStr) {
8804
+ return false;
8805
+ }
8806
+
8807
+ // If we passed all other checks, we can assume the date is valid
8808
+ return true;
8809
+ };
8810
+
8811
+ /**
8812
+ * Determines if a string date value matches the format provided.
8813
+ * @param {string} value = The date string value.
8814
+ * @param { string} format = The date format to match against.
8815
+ * @returns {boolean}
8816
+ */
8817
+ this.dateAndFormatMatch = (value, format) => {
8818
+
8819
+ // Ensure we have both values we need to do the comparison
8820
+ if (!value || !format) {
8821
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
8822
+ }
8823
+
8824
+ // If the lengths are different, they cannot match
8825
+ if (value.length !== format.length) {
8826
+ return false;
8827
+ }
8828
+
8829
+ // Get the parts of the date
8830
+ const dateParts = dateFormatter.parseDate(value, format);
8831
+
8832
+ // Validator for day
8833
+ const dayValueIsValid = (day) => {
8834
+
8835
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
8836
+ if (!dateParts.day) {
8837
+ return true;
8838
+ }
8839
+
8840
+ // Guard clause: ensure day exists.
8841
+ if (!day) {
8842
+ return false;
8843
+ }
8844
+
8845
+ // Convert day to number
8846
+ const numDay = Number.parseInt(day, 10);
8847
+
8848
+ // Guard clause: ensure day is a valid integer
8849
+ if (Number.isNaN(numDay)) {
8850
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
8851
+ }
8852
+
8853
+ // Guard clause: ensure day is within the valid range
8854
+ if (numDay < this.minDay || numDay > this.maxDay) {
8855
+ return false;
8856
+ }
8857
+
8858
+ // Default return
8859
+ return true;
8860
+ };
8861
+
8862
+ // Validator for month
8863
+ const monthValueIsValid = (month) => {
8864
+
8865
+ // Guard clause: ensure month exists.
8866
+ if (!month) {
8867
+ return false;
8868
+ }
8869
+
8870
+ // Convert month to number
8871
+ const numMonth = Number.parseInt(month, 10);
8872
+
8873
+ // Guard clause: ensure month is a valid integer
8874
+ if (Number.isNaN(numMonth)) {
8875
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
8876
+ }
8877
+
8878
+ // Guard clause: ensure month is within the valid range
8879
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
8880
+ return false;
8881
+ }
8882
+
8883
+ // Default return
8884
+ return true;
8885
+ };
8886
+
8887
+ // Validator for year
8888
+ const yearIsValid = (_year) => {
8889
+
8890
+ // Guard clause: ensure year exists.
8891
+ if (!_year) {
8892
+ return false;
8893
+ }
8894
+
8895
+ // Get the full year
8896
+ const year = this.getFourDigitYear(_year);
8897
+
8898
+ // Convert year to number
8899
+ const numYear = Number.parseInt(year, 10);
8900
+
8901
+ // Guard clause: ensure year is a valid integer
8902
+ if (Number.isNaN(numYear)) {
8903
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
8904
+ }
8905
+
8906
+ // Guard clause: ensure year is within the valid range
8907
+ if (numYear < this.minYear || numYear > this.maxYear) {
8908
+ return false;
8909
+ }
8910
+
8911
+ // Default return
8912
+ return true;
8913
+ };
8914
+
8915
+ // Self-contained checks for month, day, and year
8916
+ const checks = [
8917
+ monthValueIsValid(dateParts.month),
8918
+ dayValueIsValid(dateParts.day),
8919
+ yearIsValid(dateParts.year)
8920
+ ];
8921
+
8922
+ // If any of the checks failed, the date format does not match and the result is invalid
8923
+ const isValid = checks.every((check) => check === true);
8924
+
8925
+ // If the check is invalid, return false
8926
+ if (!isValid) {
8927
+ return false;
8928
+ }
8929
+
8930
+ // Default case
8931
+ return true;
8932
+ };
8933
+ }
8934
+ }
8935
+
8936
+ // Export a class instance
8937
+ const dateUtilities = new AuroDateUtilities();
8938
+
8939
+ // Export the class instance methods individually
8940
+ const {
8941
+ datesMatch,
8942
+ validDateStr,
8943
+ dateAndFormatMatch,
8944
+ minDay,
8945
+ minMonth,
8946
+ minYear,
8947
+ maxDay,
8948
+ maxMonth,
8949
+ maxYear
8950
+ } = dateUtilities;
8951
+
8952
+ const {
8953
+ toNorthAmericanFormat,
8954
+ parseDate,
8955
+ getDateAsString
8956
+ } = dateFormatter;
8957
+
8043
8958
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
8044
8959
  // See LICENSE in the project root for license information.
8045
8960
 
@@ -8115,6 +9030,7 @@ let AuroLibraryRuntimeUtils$1$1 = class AuroLibraryRuntimeUtils {
8115
9030
 
8116
9031
 
8117
9032
  class AuroFormValidation {
9033
+
8118
9034
  constructor() {
8119
9035
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1$1();
8120
9036
  }
@@ -8206,17 +9122,17 @@ class AuroFormValidation {
8206
9122
  ]
8207
9123
  }
8208
9124
  };
8209
-
9125
+
8210
9126
  let elementType;
8211
9127
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
8212
9128
  elementType = 'input';
8213
9129
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
8214
9130
  elementType = 'counter';
8215
9131
  }
8216
-
9132
+
8217
9133
  if (elementType) {
8218
9134
  const rules = validationRules[elementType];
8219
-
9135
+
8220
9136
  if (rules) {
8221
9137
  Object.values(rules).flat().forEach(rule => {
8222
9138
  if (rule.check(elem)) {
@@ -8242,48 +9158,82 @@ class AuroFormValidation {
8242
9158
  if (!elem.value.match(emailRegex)) {
8243
9159
  elem.validity = 'patternMismatch';
8244
9160
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9161
+ return;
8245
9162
  }
8246
9163
  } else if (elem.type === 'credit-card') {
8247
9164
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
8248
9165
  elem.validity = 'tooShort';
8249
9166
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9167
+ return;
8250
9168
  }
8251
9169
  } else if (elem.type === 'number') {
8252
9170
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
8253
9171
  elem.validity = 'rangeOverflow';
8254
9172
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9173
+ return;
8255
9174
  }
8256
9175
 
8257
9176
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
8258
9177
  elem.validity = 'rangeUnderflow';
8259
9178
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9179
+ return;
8260
9180
  }
8261
- } else if (elem.type === 'date') {
8262
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
9181
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
9182
+
9183
+ // Guard Clause: if the value is too short
9184
+ if (elem.value.length < elem.lengthForType) {
9185
+
8263
9186
  elem.validity = 'tooShort';
8264
9187
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
8265
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
8266
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
8267
- const valueDate = new Date(formattedValue.dateForComparison);
9188
+ return;
9189
+ }
9190
+
9191
+ // Guard Clause: If the value is too long for the type
9192
+ if (elem.value?.length > elem.lengthForType) {
9193
+
9194
+ elem.validity = 'tooLong';
9195
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
9196
+ return;
9197
+ }
9198
+
9199
+ // Validate that the date passed was the correct format
9200
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
9201
+ elem.validity = 'patternMismatch';
9202
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
9203
+ return;
9204
+ }
9205
+
9206
+ // Validate that the date passed was a valid date
9207
+ if (!validDateStr(elem.value, elem.format)) {
9208
+ elem.validity = 'invalidDate';
9209
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
9210
+ return;
9211
+ }
8268
9212
 
8269
- // validate max
8270
- if (elem.max?.length === elem.lengthForType) {
8271
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
9213
+ // Perform the rest of the validation
9214
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
9215
+ const valueDate = new Date(formattedValue);
8272
9216
 
8273
- if (valueDate > maxDate) {
8274
- elem.validity = 'rangeOverflow';
8275
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
8276
- }
9217
+ // // Validate max date
9218
+ if (elem.max?.length === elem.lengthForType) {
9219
+
9220
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
9221
+
9222
+ if (valueDate > maxDate) {
9223
+ elem.validity = 'rangeOverflow';
9224
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
9225
+ return;
8277
9226
  }
9227
+ }
8278
9228
 
8279
- // validate min
8280
- if (elem.min?.length === elem.lengthForType) {
8281
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
9229
+ // Validate min date
9230
+ if (elem.min?.length === elem.lengthForType) {
9231
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
8282
9232
 
8283
- if (valueDate < minDate) {
8284
- elem.validity = 'rangeUnderflow';
8285
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
8286
- }
9233
+ if (valueDate < minDate) {
9234
+ elem.validity = 'rangeUnderflow';
9235
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
9236
+ return;
8287
9237
  }
8288
9238
  }
8289
9239
  }
@@ -8402,7 +9352,7 @@ class AuroFormValidation {
8402
9352
  if (input.validationMessage.length > 0) {
8403
9353
  elem.errorMessage = input.validationMessage;
8404
9354
  }
8405
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
9355
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
8406
9356
  const firstInput = this.inputElements[0];
8407
9357
 
8408
9358
  if (firstInput.validationMessage.length > 0) {
@@ -8524,6 +9474,33 @@ class BaseInput extends LitElement {
8524
9474
  static get properties() {
8525
9475
  return {
8526
9476
 
9477
+ /**
9478
+ * The value for the role attribute.
9479
+ */
9480
+ a11yRole: {
9481
+ type: String,
9482
+ attribute: true,
9483
+ reflect: true
9484
+ },
9485
+
9486
+ /**
9487
+ * The value for the aria-expanded attribute.
9488
+ */
9489
+ a11yExpanded: {
9490
+ type: Boolean,
9491
+ attribute: true,
9492
+ reflect: true
9493
+ },
9494
+
9495
+ /**
9496
+ * The value for the aria-controls attribute.
9497
+ */
9498
+ a11yControls: {
9499
+ type: String,
9500
+ attribute: true,
9501
+ reflect: true
9502
+ },
9503
+
8527
9504
  /**
8528
9505
  * If set, the label will remain fixed in the active position.
8529
9506
  */
@@ -9165,6 +10142,10 @@ class BaseInput extends LitElement {
9165
10142
  } else if (this.type === 'number') {
9166
10143
  this.inputMode = 'numeric';
9167
10144
  }
10145
+
10146
+ if (this.type === "date" && !this.format) {
10147
+ this.format = 'mm/dd/yyyy';
10148
+ }
9168
10149
  }
9169
10150
 
9170
10151
  /**
@@ -10408,6 +11389,7 @@ var helpTextVersion = '1.0.0';
10408
11389
 
10409
11390
  // build the component class
10410
11391
  class AuroInput extends BaseInput {
11392
+
10411
11393
  constructor() {
10412
11394
  super();
10413
11395
 
@@ -10520,7 +11502,7 @@ class AuroInput extends BaseInput {
10520
11502
  ?required="${this.required}"
10521
11503
  ?disabled="${this.disabled}"
10522
11504
  aria-describedby="${this.uniqueId}"
10523
- aria-invalid="${this.validity !== 'valid'}"
11505
+ ?aria-invalid="${this.validity !== 'valid'}"
10524
11506
  placeholder=${this.getPlaceholder()}
10525
11507
  lang="${ifDefined(this.lang)}"
10526
11508
  ?activeLabel="${this.activeLabel}"
@@ -10529,7 +11511,10 @@ class AuroInput extends BaseInput {
10529
11511
  autocapitalize="${ifDefined(this.autocapitalize ? this.autocapitalize : undefined)}"
10530
11512
  autocomplete="${ifDefined(this.autocomplete ? this.autocomplete : undefined)}"
10531
11513
  part="input"
10532
- />
11514
+ role="${ifDefined(this.a11yRole)}"
11515
+ aria-expanded="${ifDefined(this.a11yExpanded)}"
11516
+ aria-controls="${ifDefined(this.a11yControls)}"
11517
+ />
10533
11518
  </div>
10534
11519
  <div
10535
11520
  class="notificationIcons"
@@ -11521,6 +12506,7 @@ var styleCss = css`.util_displayInline{display:inline}.util_displayInlineBlock{d
11521
12506
 
11522
12507
  // build the component class
11523
12508
  class AuroCombobox extends LitElement {
12509
+
11524
12510
  constructor() {
11525
12511
  super();
11526
12512
 
@@ -11532,6 +12518,8 @@ class AuroCombobox extends LitElement {
11532
12518
  * @returns {void} Internal defaults.
11533
12519
  */
11534
12520
  privateDefaults() {
12521
+ this.dropdownOpen = false;
12522
+ this.dropdownId = undefined;
11535
12523
  this.onDark = false;
11536
12524
 
11537
12525
  this.noFilter = false;
@@ -11611,6 +12599,26 @@ class AuroCombobox extends LitElement {
11611
12599
  reflect: true
11612
12600
  },
11613
12601
 
12602
+ /**
12603
+ * ID for the dropdown
12604
+ * @private
12605
+ */
12606
+ dropdownId: {
12607
+ type: String,
12608
+ reflect: false,
12609
+ attribute: false
12610
+ },
12611
+
12612
+ /**
12613
+ * Whether or not the dropdown is open
12614
+ * @private
12615
+ */
12616
+ dropdownOpen: {
12617
+ type: Boolean,
12618
+ reflect: false,
12619
+ attribute: false
12620
+ },
12621
+
11614
12622
  /**
11615
12623
  * When defined, sets persistent validity to `customError` and sets the validation message to the attribute value.
11616
12624
  */
@@ -11756,11 +12764,8 @@ class AuroCombobox extends LitElement {
11756
12764
  },
11757
12765
 
11758
12766
  /**
11759
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
11760
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
11761
- *
11762
- * When expanded, the dropdown will automatically display in fullscreen mode
11763
- * if the screen size is equal to or smaller than the selected breakpoint.
12767
+ * Defines the screen size breakpoint (`lg`, `md`, `sm`, or `xs`) at which the dropdown switches to fullscreen mode on mobile.
12768
+ * When expanded, the dropdown will automatically display in fullscreen mode if the screen size is equal to or smaller than the selected breakpoint.
11764
12769
  * @default sm
11765
12770
  */
11766
12771
  fullscreenBreakpoint: {
@@ -11772,8 +12777,19 @@ class AuroCombobox extends LitElement {
11772
12777
  * @private
11773
12778
  */
11774
12779
  isDropdownFullscreen: {
11775
- type: Boolean
11776
- }
12780
+ type: Boolean,
12781
+ reflect: false
12782
+ },
12783
+
12784
+ /**
12785
+ * @private
12786
+ * specifies the currently active option
12787
+ */
12788
+ optionActive: {
12789
+ type: Object,
12790
+ reflect: false,
12791
+ attribute: false
12792
+ },
11777
12793
  };
11778
12794
  }
11779
12795
 
@@ -11925,6 +12941,18 @@ class AuroCombobox extends LitElement {
11925
12941
  * @returns {void}
11926
12942
  */
11927
12943
  configureDropdown() {
12944
+
12945
+ // Listen for the ID to be added to the dropdown so we can capture it and use it for accessibility.
12946
+ this.dropdown.addEventListener('auroDropdown-idAdded', (event) => {
12947
+ this.dropdownId = event.detail.id;
12948
+ });
12949
+
12950
+ // Listen for the dropdown to be shown or hidden
12951
+ this.dropdown.addEventListener("auroDropdown-toggled", (ev) => {
12952
+ this.dropdownOpen = ev.detail.expanded;
12953
+ });
12954
+
12955
+ // this.dropdown.addEventListener('auroDropdown-show', () => {
11928
12956
  this.menuWrapper = this.dropdown.querySelector('.menuWrapper');
11929
12957
  this.menuWrapper.append(this.menu);
11930
12958
 
@@ -11938,7 +12966,6 @@ class AuroCombobox extends LitElement {
11938
12966
  this.hideBib = this.hideBib.bind(this);
11939
12967
  this.bibtemplate.addEventListener('close-click', this.hideBib);
11940
12968
 
11941
- this.dropdown.setAttribute('role', 'combobox');
11942
12969
  this.dropdown.addEventListener('auroDropdown-triggerClick', () => {
11943
12970
  this.showBib();
11944
12971
  });
@@ -11954,7 +12981,6 @@ class AuroCombobox extends LitElement {
11954
12981
  this.isDropdownFullscreen = event.detail.strategy === 'fullscreen';
11955
12982
  setTimeout(this.transportInput);
11956
12983
  });
11957
-
11958
12984
  }
11959
12985
 
11960
12986
  /**
@@ -12448,6 +13474,10 @@ class AuroCombobox extends LitElement {
12448
13474
  ?noFlip="${this.noFlip}"
12449
13475
  disableEventShow>
12450
13476
  <${this.inputTag}
13477
+ .a11yRole="${"combobox"}"
13478
+ .a11yExpanded="${this.dropdownOpen}"
13479
+ .a11yControls="${this.dropdownId}"
13480
+ id="${this.id || 'auro-combobox-input'}"
12451
13481
  slot="trigger"
12452
13482
  bordered
12453
13483
  ?onDark="${this.onDark}"