@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
@@ -5,6 +5,414 @@ import { classMap } from 'lit/directives/class-map.js';
5
5
  import { ifDefined } from 'lit/directives/if-defined.js';
6
6
  import { repeat } from 'lit/directives/repeat.js';
7
7
 
8
+ let DateFormatter$1 = class DateFormatter {
9
+
10
+ constructor() {
11
+
12
+ /**
13
+ * @description Parses a date string into its components.
14
+ * @param {string} dateStr - Date string to parse.
15
+ * @param {string} format - Date format to parse.
16
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
17
+ */
18
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
19
+
20
+ // Guard Clause: Date string is defined
21
+ if (!dateStr) {
22
+ return undefined;
23
+ }
24
+
25
+ // Assume the separator is a "/" a defined in our code base
26
+ const separator = '/';
27
+
28
+ // Get the parts of the date and format
29
+ const valueParts = dateStr.split(separator);
30
+ const formatParts = format.split(separator);
31
+
32
+ // Check if the value and format have the correct number of parts
33
+ if (valueParts.length !== formatParts.length) {
34
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
35
+ }
36
+
37
+ // Holds the result to be returned
38
+ const result = formatParts.reduce((acc, part, index) => {
39
+ const value = valueParts[index];
40
+
41
+ if ((/m/iu).test(part)) {
42
+ acc.month = value;
43
+ } else if ((/d/iu).test(part)) {
44
+ acc.day = value;
45
+ } else if ((/y/iu).test(part)) {
46
+ acc.year = value;
47
+ }
48
+
49
+ return acc;
50
+ }, {});
51
+
52
+ // If we found all the parts, return the result
53
+ if (result.month && result.year) {
54
+ return result;
55
+ }
56
+
57
+ // Throw an error to let the dev know we were unable to parse the date string
58
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
59
+ };
60
+
61
+ /**
62
+ * Convert a date object to string format.
63
+ * @param {Object} date - Date to convert to string.
64
+ * @returns {Object} Returns the date as a string.
65
+ */
66
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
67
+ year: "numeric",
68
+ month: "2-digit",
69
+ day: "2-digit",
70
+ });
71
+
72
+ /**
73
+ * Converts a date string to a North American date format.
74
+ * @param {String} dateStr - Date to validate.
75
+ * @param {String} format - Date format to validate against.
76
+ * @returns {Boolean}
77
+ */
78
+ this.toNorthAmericanFormat = (dateStr, format) => {
79
+
80
+ if (format === 'mm/dd/yyyy') {
81
+ return dateStr;
82
+ }
83
+
84
+ const parsedDate = this.parseDate(dateStr, format);
85
+
86
+ if (!parsedDate) {
87
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
88
+ }
89
+
90
+ const { month, day, year } = parsedDate;
91
+
92
+ const dateParts = [];
93
+ if (month) {
94
+ dateParts.push(month);
95
+ }
96
+
97
+ if (day) {
98
+ dateParts.push(day);
99
+ }
100
+
101
+ if (year) {
102
+ dateParts.push(year);
103
+ }
104
+
105
+ return dateParts.join('/');
106
+ };
107
+ }
108
+ };
109
+ const dateFormatter$1 = new DateFormatter$1();
110
+
111
+ // filepath: dateConstraints.mjs
112
+ const DATE_UTIL_CONSTRAINTS$1 = {
113
+ maxDay: 31,
114
+ maxMonth: 12,
115
+ maxYear: 2400,
116
+ minDay: 1,
117
+ minMonth: 1,
118
+ minYear: 1900,
119
+ };
120
+
121
+ let AuroDateUtilitiesBase$1 = class AuroDateUtilitiesBase {
122
+
123
+ /**
124
+ * @description The maximum day value allowed by the various utilities in this class.
125
+ * @readonly
126
+ * @type {Number}
127
+ */
128
+ get maxDay() {
129
+ return DATE_UTIL_CONSTRAINTS$1.maxDay;
130
+ }
131
+
132
+ /**
133
+ * @description The maximum month value allowed by the various utilities in this class.
134
+ * @readonly
135
+ * @type {Number}
136
+ */
137
+ get maxMonth() {
138
+ return DATE_UTIL_CONSTRAINTS$1.maxMonth;
139
+ }
140
+
141
+ /**
142
+ * @description The maximum year value allowed by the various utilities in this class.
143
+ * @readonly
144
+ * @type {Number}
145
+ */
146
+ get maxYear() {
147
+ return DATE_UTIL_CONSTRAINTS$1.maxYear;
148
+ }
149
+
150
+ /**
151
+ * @description The minimum day value allowed by the various utilities in this class.
152
+ * @readonly
153
+ * @type {Number}
154
+ */
155
+ get minDay() {
156
+ return DATE_UTIL_CONSTRAINTS$1.minDay;
157
+ }
158
+
159
+ /**
160
+ * @description The minimum month value allowed by the various utilities in this class.
161
+ * @readonly
162
+ * @type {Number}
163
+ */
164
+ get minMonth() {
165
+ return DATE_UTIL_CONSTRAINTS$1.minMonth;
166
+ }
167
+
168
+ /**
169
+ * @description The minimum year value allowed by the various utilities in this class.
170
+ * @readonly
171
+ * @type {Number}
172
+ */
173
+ get minYear() {
174
+ return DATE_UTIL_CONSTRAINTS$1.minYear;
175
+ }
176
+ };
177
+
178
+ /* eslint-disable no-magic-numbers */
179
+
180
+ let AuroDateUtilities$1 = class AuroDateUtilities extends AuroDateUtilitiesBase$1 {
181
+
182
+ /**
183
+ * Returns the current century.
184
+ * @returns {String} The current century.
185
+ */
186
+ getCentury () {
187
+ return String(new Date().getFullYear()).slice(0, 2);
188
+ }
189
+
190
+ /**
191
+ * Returns a four digit year.
192
+ * @param {String} year - The year to convert to four digits.
193
+ * @returns {String} The four digit year.
194
+ */
195
+ getFourDigitYear (year) {
196
+
197
+ const strYear = String(year).trim();
198
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
199
+ }
200
+
201
+ constructor() {
202
+
203
+ super();
204
+
205
+ /**
206
+ * Compares two dates to see if they match.
207
+ * @param {Object} date1 - First date to compare.
208
+ * @param {Object} date2 - Second date to compare.
209
+ * @returns {Boolean} Returns true if the dates match.
210
+ */
211
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
212
+
213
+ /**
214
+ * Returns true if value passed in is a valid date.
215
+ * @param {String} date - Date to validate.
216
+ * @param {String} format - Date format to validate against.
217
+ * @returns {Boolean}
218
+ */
219
+ this.validDateStr = (date, format) => {
220
+
221
+ // The length we expect the date string to be
222
+ const dateStrLength = format.length;
223
+
224
+ // Guard Clause: Date and format are defined
225
+ if (typeof date === "undefined" || typeof format === "undefined") {
226
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
227
+ }
228
+
229
+ // Guard Clause: Date should be of type string
230
+ if (typeof date !== "string") {
231
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
232
+ }
233
+
234
+ // Guard Clause: Format should be of type string
235
+ if (typeof format !== "string") {
236
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
237
+ }
238
+
239
+ // Guard Clause: Length is what we expect it to be
240
+ if (date.length !== dateStrLength) {
241
+ return false;
242
+ }
243
+ // Get a formatted date string and parse it
244
+ const dateParts = dateFormatter$1.parseDate(date, format);
245
+
246
+ // Guard Clause: Date parse succeeded
247
+ if (!dateParts) {
248
+ return false;
249
+ }
250
+
251
+ // Create the expected date string based on the date parts
252
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
253
+
254
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
255
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
256
+
257
+ // Get the date string of the date object we created from the string date
258
+ const actualDateStr = dateFormatter$1.getDateAsString(dateObj);
259
+
260
+ // Guard Clause: Generated date matches date string input
261
+ if (expectedDateStr !== actualDateStr) {
262
+ return false;
263
+ }
264
+
265
+ // If we passed all other checks, we can assume the date is valid
266
+ return true;
267
+ };
268
+
269
+ /**
270
+ * Determines if a string date value matches the format provided.
271
+ * @param {string} value = The date string value.
272
+ * @param { string} format = The date format to match against.
273
+ * @returns {boolean}
274
+ */
275
+ this.dateAndFormatMatch = (value, format) => {
276
+
277
+ // Ensure we have both values we need to do the comparison
278
+ if (!value || !format) {
279
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
280
+ }
281
+
282
+ // If the lengths are different, they cannot match
283
+ if (value.length !== format.length) {
284
+ return false;
285
+ }
286
+
287
+ // Get the parts of the date
288
+ const dateParts = dateFormatter$1.parseDate(value, format);
289
+
290
+ // Validator for day
291
+ const dayValueIsValid = (day) => {
292
+
293
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
294
+ if (!dateParts.day) {
295
+ return true;
296
+ }
297
+
298
+ // Guard clause: ensure day exists.
299
+ if (!day) {
300
+ return false;
301
+ }
302
+
303
+ // Convert day to number
304
+ const numDay = Number.parseInt(day, 10);
305
+
306
+ // Guard clause: ensure day is a valid integer
307
+ if (Number.isNaN(numDay)) {
308
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
309
+ }
310
+
311
+ // Guard clause: ensure day is within the valid range
312
+ if (numDay < this.minDay || numDay > this.maxDay) {
313
+ return false;
314
+ }
315
+
316
+ // Default return
317
+ return true;
318
+ };
319
+
320
+ // Validator for month
321
+ const monthValueIsValid = (month) => {
322
+
323
+ // Guard clause: ensure month exists.
324
+ if (!month) {
325
+ return false;
326
+ }
327
+
328
+ // Convert month to number
329
+ const numMonth = Number.parseInt(month, 10);
330
+
331
+ // Guard clause: ensure month is a valid integer
332
+ if (Number.isNaN(numMonth)) {
333
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
334
+ }
335
+
336
+ // Guard clause: ensure month is within the valid range
337
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
338
+ return false;
339
+ }
340
+
341
+ // Default return
342
+ return true;
343
+ };
344
+
345
+ // Validator for year
346
+ const yearIsValid = (_year) => {
347
+
348
+ // Guard clause: ensure year exists.
349
+ if (!_year) {
350
+ return false;
351
+ }
352
+
353
+ // Get the full year
354
+ const year = this.getFourDigitYear(_year);
355
+
356
+ // Convert year to number
357
+ const numYear = Number.parseInt(year, 10);
358
+
359
+ // Guard clause: ensure year is a valid integer
360
+ if (Number.isNaN(numYear)) {
361
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
362
+ }
363
+
364
+ // Guard clause: ensure year is within the valid range
365
+ if (numYear < this.minYear || numYear > this.maxYear) {
366
+ return false;
367
+ }
368
+
369
+ // Default return
370
+ return true;
371
+ };
372
+
373
+ // Self-contained checks for month, day, and year
374
+ const checks = [
375
+ monthValueIsValid(dateParts.month),
376
+ dayValueIsValid(dateParts.day),
377
+ yearIsValid(dateParts.year)
378
+ ];
379
+
380
+ // If any of the checks failed, the date format does not match and the result is invalid
381
+ const isValid = checks.every((check) => check === true);
382
+
383
+ // If the check is invalid, return false
384
+ if (!isValid) {
385
+ return false;
386
+ }
387
+
388
+ // Default case
389
+ return true;
390
+ };
391
+ }
392
+ };
393
+
394
+ // Export a class instance
395
+ const dateUtilities$1 = new AuroDateUtilities$1();
396
+
397
+ // Export the class instance methods individually
398
+ const {
399
+ datesMatch: datesMatch$1,
400
+ validDateStr: validDateStr$1,
401
+ dateAndFormatMatch: dateAndFormatMatch$1,
402
+ minDay: minDay$1,
403
+ minMonth: minMonth$1,
404
+ minYear: minYear$1,
405
+ maxDay: maxDay$1,
406
+ maxMonth: maxMonth$1,
407
+ maxYear: maxYear$1
408
+ } = dateUtilities$1;
409
+
410
+ const {
411
+ toNorthAmericanFormat: toNorthAmericanFormat$1,
412
+ parseDate: parseDate$1,
413
+ getDateAsString: getDateAsString$1
414
+ } = dateFormatter$1;
415
+
8
416
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
9
417
  // See LICENSE in the project root for license information.
10
418
 
@@ -80,6 +488,7 @@ let AuroLibraryRuntimeUtils$4 = class AuroLibraryRuntimeUtils {
80
488
 
81
489
 
82
490
  let AuroFormValidation$1 = class AuroFormValidation {
491
+
83
492
  constructor() {
84
493
  this.runtimeUtils = new AuroLibraryRuntimeUtils$4();
85
494
  }
@@ -171,17 +580,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
171
580
  ]
172
581
  }
173
582
  };
174
-
583
+
175
584
  let elementType;
176
585
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
177
586
  elementType = 'input';
178
587
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
179
588
  elementType = 'counter';
180
589
  }
181
-
590
+
182
591
  if (elementType) {
183
592
  const rules = validationRules[elementType];
184
-
593
+
185
594
  if (rules) {
186
595
  Object.values(rules).flat().forEach(rule => {
187
596
  if (rule.check(elem)) {
@@ -207,48 +616,82 @@ let AuroFormValidation$1 = class AuroFormValidation {
207
616
  if (!elem.value.match(emailRegex)) {
208
617
  elem.validity = 'patternMismatch';
209
618
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
619
+ return;
210
620
  }
211
621
  } else if (elem.type === 'credit-card') {
212
622
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
213
623
  elem.validity = 'tooShort';
214
624
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
625
+ return;
215
626
  }
216
627
  } else if (elem.type === 'number') {
217
628
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
218
629
  elem.validity = 'rangeOverflow';
219
630
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
631
+ return;
220
632
  }
221
633
 
222
634
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
223
635
  elem.validity = 'rangeUnderflow';
224
636
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
637
+ return;
225
638
  }
226
- } else if (elem.type === 'date') {
227
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
639
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
640
+
641
+ // Guard Clause: if the value is too short
642
+ if (elem.value.length < elem.lengthForType) {
643
+
228
644
  elem.validity = 'tooShort';
229
645
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
230
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
231
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
232
- const valueDate = new Date(formattedValue.dateForComparison);
646
+ return;
647
+ }
648
+
649
+ // Guard Clause: If the value is too long for the type
650
+ if (elem.value?.length > elem.lengthForType) {
233
651
 
234
- // validate max
235
- if (elem.max?.length === elem.lengthForType) {
236
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
652
+ elem.validity = 'tooLong';
653
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
654
+ return;
655
+ }
656
+
657
+ // Validate that the date passed was the correct format
658
+ if (!dateAndFormatMatch$1(elem.value, elem.format)) {
659
+ elem.validity = 'patternMismatch';
660
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
661
+ return;
662
+ }
663
+
664
+ // Validate that the date passed was a valid date
665
+ if (!validDateStr$1(elem.value, elem.format)) {
666
+ elem.validity = 'invalidDate';
667
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
668
+ return;
669
+ }
237
670
 
238
- if (valueDate > maxDate) {
239
- elem.validity = 'rangeOverflow';
240
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
241
- }
671
+ // Perform the rest of the validation
672
+ const formattedValue = toNorthAmericanFormat$1(elem.value, elem.format);
673
+ const valueDate = new Date(formattedValue);
674
+
675
+ // // Validate max date
676
+ if (elem.max?.length === elem.lengthForType) {
677
+
678
+ const maxDate = new Date(toNorthAmericanFormat$1(elem.max, elem.format));
679
+
680
+ if (valueDate > maxDate) {
681
+ elem.validity = 'rangeOverflow';
682
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
683
+ return;
242
684
  }
685
+ }
243
686
 
244
- // validate min
245
- if (elem.min?.length === elem.lengthForType) {
246
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
687
+ // Validate min date
688
+ if (elem.min?.length === elem.lengthForType) {
689
+ const minDate = new Date(toNorthAmericanFormat$1(elem.min, elem.format));
247
690
 
248
- if (valueDate < minDate) {
249
- elem.validity = 'rangeUnderflow';
250
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
251
- }
691
+ if (valueDate < minDate) {
692
+ elem.validity = 'rangeUnderflow';
693
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
694
+ return;
252
695
  }
253
696
  }
254
697
  }
@@ -367,7 +810,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
367
810
  if (input.validationMessage.length > 0) {
368
811
  elem.errorMessage = input.validationMessage;
369
812
  }
370
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
813
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
371
814
  const firstInput = this.inputElements[0];
372
815
 
373
816
  if (firstInput.validationMessage.length > 0) {
@@ -13709,7 +14152,7 @@ class AuroFloatingUI {
13709
14152
  /**
13710
14153
  * @private
13711
14154
  * getting called on 'blur' in trigger or `focusin` in document
13712
- *
14155
+ *
13713
14156
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
13714
14157
  * This method checks if the currently active element is still within the trigger or bib.
13715
14158
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -13825,7 +14268,7 @@ class AuroFloatingUI {
13825
14268
  // Close any other dropdown that is already open
13826
14269
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
13827
14270
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
13828
- existedVisibleFloatingUI.isPopoverVisible &&
14271
+ existedVisibleFloatingUI.element.isPopoverVisible &&
13829
14272
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
13830
14273
  document.expandedAuroFloater.hideBib();
13831
14274
  }
@@ -14001,7 +14444,7 @@ class AuroFloatingUI {
14001
14444
  this.id = window.crypto.randomUUID();
14002
14445
  this.element.setAttribute('id', this.id);
14003
14446
  }
14004
-
14447
+
14005
14448
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
14006
14449
  }
14007
14450
 
@@ -14054,7 +14497,7 @@ class AuroFloatingUI {
14054
14497
  if (this.element.bib) {
14055
14498
  this.element.shadowRoot.append(this.element.bib);
14056
14499
  }
14057
-
14500
+
14058
14501
  // Remove event & keyboard listeners
14059
14502
  if (this.element?.trigger) {
14060
14503
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -14444,7 +14887,6 @@ var tokensCss$1$1 = css`:host{--ds-auro-dropdown-label-text-color: var(--ds-basi
14444
14887
 
14445
14888
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
14446
14889
  const DESIGN_TOKEN_BREAKPOINT_OPTIONS = [
14447
- 'xl',
14448
14890
  'lg',
14449
14891
  'md',
14450
14892
  'sm',
@@ -14516,7 +14958,6 @@ class AuroDropdownBib extends LitElement {
14516
14958
 
14517
14959
  set mobileFullscreenBreakpoint(value) {
14518
14960
  // verify the defined breakpoint is valid and exit out if not
14519
- // 'disabled' is a design token breakpoint so it acts as our "undefined" value
14520
14961
  const validatedValue = DESIGN_TOKEN_BREAKPOINT_OPTIONS.includes(value) ? value : undefined;
14521
14962
  if (!validatedValue) {
14522
14963
  this._mobileBreakpointValue = undefined;
@@ -14785,6 +15226,7 @@ var helpTextVersion$1 = '1.0.0';
14785
15226
  * @csspart helpText - The helpText content container.
14786
15227
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
14787
15228
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
15229
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
14788
15230
  */
14789
15231
  class AuroDropdown extends LitElement {
14790
15232
  constructor() {
@@ -14830,7 +15272,9 @@ class AuroDropdown extends LitElement {
14830
15272
  this.rounded = false;
14831
15273
  this.tabIndex = 0;
14832
15274
  this.noToggle = false;
15275
+ this.a11yAutocomplete = 'none';
14833
15276
  this.labeled = true;
15277
+ this.a11yRole = 'combobox';
14834
15278
  this.onDark = false;
14835
15279
 
14836
15280
  // floaterConfig
@@ -14966,6 +15410,16 @@ class AuroDropdown extends LitElement {
14966
15410
  type: Number
14967
15411
  },
14968
15412
 
15413
+ /**
15414
+ * The unique ID for the dropdown bib element.
15415
+ * @private
15416
+ */
15417
+ dropdownId: {
15418
+ type: String,
15419
+ reflect: false,
15420
+ attribute: false
15421
+ },
15422
+
14969
15423
  /**
14970
15424
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
14971
15425
  */
@@ -15029,12 +15483,7 @@ class AuroDropdown extends LitElement {
15029
15483
  },
15030
15484
 
15031
15485
  /**
15032
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
15033
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
15034
- *
15035
- * When expanded, the dropdown will automatically display in fullscreen mode
15036
- * if the screen size is equal to or smaller than the selected breakpoint.
15037
- * @default sm
15486
+ * 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.
15038
15487
  */
15039
15488
  fullscreenBreakpoint: {
15040
15489
  type: String,
@@ -15133,6 +15582,23 @@ class AuroDropdown extends LitElement {
15133
15582
  */
15134
15583
  tabIndex: {
15135
15584
  type: Number
15585
+ },
15586
+
15587
+ /**
15588
+ * The value for the role attribute of the trigger element.
15589
+ */
15590
+ a11yRole: {
15591
+ type: String || undefined,
15592
+ attribute: false,
15593
+ reflect: false
15594
+ },
15595
+
15596
+ /**
15597
+ * The value for the aria-autocomplete attribute of the trigger element.
15598
+ */
15599
+ a11yAutocomplete: {
15600
+ type: String,
15601
+ attribute: false,
15136
15602
  }
15137
15603
  };
15138
15604
  }
@@ -15157,15 +15623,6 @@ class AuroDropdown extends LitElement {
15157
15623
  AuroLibraryRuntimeUtils$1$1.prototype.registerComponent(name, AuroDropdown);
15158
15624
  }
15159
15625
 
15160
- /**
15161
- * Accessor for reusing the focusable entity query string.
15162
- * @private
15163
- * @returns {string}
15164
- */
15165
- get focusableEntityQuery () {
15166
- return 'auro-input, [auro-input], auro-button, [auro-button], button, input';
15167
- }
15168
-
15169
15626
  connectedCallback() {
15170
15627
  super.connectedCallback();
15171
15628
  }
@@ -15179,8 +15636,6 @@ class AuroDropdown extends LitElement {
15179
15636
  updated(changedProperties) {
15180
15637
  this.floater.handleUpdate(changedProperties);
15181
15638
 
15182
- // Note: `disabled` is not a breakpoint (it is not a screen size),
15183
- // so it looks like we never consume this - however, dropdownBib handles this in the setter as "undefined"
15184
15639
  if (changedProperties.has('fullscreenBreakpoint')) {
15185
15640
  this.bibContent.mobileFullscreenBreakpoint = this.fullscreenBreakpoint;
15186
15641
  }
@@ -15194,7 +15649,22 @@ class AuroDropdown extends LitElement {
15194
15649
  }
15195
15650
 
15196
15651
  firstUpdated() {
15652
+
15653
+ // Configure the floater to, this will generate the ID for the bib
15197
15654
  this.floater.configure(this, 'auroDropdown');
15655
+
15656
+ /**
15657
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
15658
+ * @event auroDropdown-idAdded
15659
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
15660
+ */
15661
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
15662
+
15663
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
15664
+ if (!this.triggerContentFocusable) {
15665
+ this.dropdownId = this.floater.element.id;
15666
+ }
15667
+
15198
15668
  this.bibContent = this.floater.element.bib;
15199
15669
 
15200
15670
  // Add the tag name as an attribute if it is different than the component name
@@ -15316,7 +15786,7 @@ class AuroDropdown extends LitElement {
15316
15786
 
15317
15787
  this.triggerContentSlot.forEach((node) => {
15318
15788
  if (node.querySelectorAll) {
15319
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
15789
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
15320
15790
  auroElements.forEach((auroEl) => {
15321
15791
  auroEl.addEventListener('focus', this.bindFocusEventToTrigger);
15322
15792
  auroEl.addEventListener('blur', this.bindFocusEventToTrigger);
@@ -15337,7 +15807,7 @@ class AuroDropdown extends LitElement {
15337
15807
 
15338
15808
  this.triggerContentSlot.forEach((node) => {
15339
15809
  if (node.querySelectorAll) {
15340
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
15810
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
15341
15811
  auroElements.forEach((auroEl) => {
15342
15812
  auroEl.removeEventListener('focus', this.bindFocusEventToTrigger);
15343
15813
  auroEl.removeEventListener('blur', this.bindFocusEventToTrigger);
@@ -15346,6 +15816,30 @@ class AuroDropdown extends LitElement {
15346
15816
  });
15347
15817
  }
15348
15818
 
15819
+ /*
15820
+ * Sets aria attributes for the trigger element if a custom one is passed in.
15821
+ * @private
15822
+ * @method setTriggerAriaAttributes
15823
+ * @param { HTMLElement } triggerElement - The custom trigger element.
15824
+ */
15825
+ clearTriggerA11yAttributes(triggerElement) {
15826
+
15827
+ if (!triggerElement || !triggerElement.removeAttribute) {
15828
+ return;
15829
+ }
15830
+
15831
+ // Reset appropriate attributes for a11y
15832
+ triggerElement.removeAttribute('aria-labelledby');
15833
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
15834
+ triggerElement.removeAttribute('id');
15835
+ }
15836
+ triggerElement.removeAttribute('role');
15837
+ triggerElement.removeAttribute('aria-expanded');
15838
+
15839
+ triggerElement.removeAttribute('aria-controls');
15840
+ triggerElement.removeAttribute('aria-autocomplete');
15841
+ }
15842
+
15349
15843
  /**
15350
15844
  * Handles changes to the trigger content slot and updates related properties.
15351
15845
  *
@@ -15359,32 +15853,41 @@ class AuroDropdown extends LitElement {
15359
15853
  * @returns {void}
15360
15854
  */
15361
15855
  handleTriggerContentSlotChange(event) {
15856
+
15362
15857
  this.floater.handleTriggerTabIndex();
15363
15858
 
15859
+ // Get the trigger
15860
+ const trigger = this.shadowRoot.querySelector('#trigger');
15861
+
15862
+ // Get the trigger slot
15364
15863
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
15365
15864
 
15865
+ // If there's a trigger slot
15366
15866
  if (triggerSlot) {
15367
15867
 
15868
+ // Get the content nodes to see if there are any children
15368
15869
  const triggerContentNodes = triggerSlot.assignedNodes();
15369
15870
 
15871
+ // If there are children
15370
15872
  if (triggerContentNodes) {
15371
15873
 
15372
- triggerContentNodes.forEach((node) => {
15373
- if (!this.triggerContentFocusable) {
15374
- this.triggerContentFocusable = this.containsFocusableElement(node);
15375
- }
15376
- });
15377
- }
15378
- }
15874
+ // See if any of them are focusable elemeents
15875
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
15379
15876
 
15380
- const trigger = this.shadowRoot.querySelector('#trigger');
15877
+ // If any of them are focusable elements
15878
+ if (this.triggerContentFocusable) {
15381
15879
 
15382
- if (!this.triggerContentFocusable) {
15383
- trigger.setAttribute('tabindex', '0');
15384
- trigger.setAttribute('role', 'button');
15385
- } else {
15386
- trigger.removeAttribute('tabindex');
15387
- trigger.removeAttribute('role');
15880
+ // Assume the consumer will be providing their own a11y in whatever they passed in
15881
+ this.clearTriggerA11yAttributes(trigger);
15882
+
15883
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
15884
+ trigger.removeAttribute('tabindex');
15885
+ } else {
15886
+
15887
+ // Add the tabindex to the trigger so that it's in the focus flow
15888
+ trigger.setAttribute('tabindex', '0');
15889
+ }
15890
+ }
15388
15891
  }
15389
15892
 
15390
15893
  if (event) {
@@ -15394,6 +15897,7 @@ class AuroDropdown extends LitElement {
15394
15897
 
15395
15898
  if (this.triggerContentSlot) {
15396
15899
  this.setupTriggerFocusEventBinding();
15900
+
15397
15901
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
15398
15902
  if (slot.textContent.trim()) {
15399
15903
  return true;
@@ -15461,10 +15965,13 @@ class AuroDropdown extends LitElement {
15461
15965
  id="trigger"
15462
15966
  class="trigger"
15463
15967
  part="trigger"
15464
- aria-labelledby="triggerLabel"
15465
15968
  tabindex="${this.tabIndex}"
15466
15969
  ?showBorder="${this.showTriggerBorders}"
15467
- >
15970
+ role="${ifDefined(this.triggerContentFocusable ? undefined : this.a11yRole)}"
15971
+ aria-expanded="${ifDefined(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
15972
+ aria-controls="${ifDefined(this.triggerContentFocusable ? undefined : this.dropdownId)}"
15973
+ aria-labelledby="${ifDefined(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
15974
+ >
15468
15975
  <div class="triggerContentWrapper">
15469
15976
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
15470
15977
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -15498,12 +16005,12 @@ class AuroDropdown extends LitElement {
15498
16005
  <div id="bibSizer" part="size"></div>
15499
16006
  <${this.dropdownBibTag}
15500
16007
  id="bib"
15501
- role="tooltip"
15502
16008
  ?data-show="${this.isPopoverVisible}"
15503
16009
  ?isfullscreen="${this.isBibFullscreen}"
15504
16010
  ?common="${this.common}"
15505
16011
  ?rounded="${this.common || this.rounded}"
15506
- ?inset="${this.common || this.inset}">
16012
+ ?inset="${this.common || this.inset}"
16013
+ >
15507
16014
  </${this.dropdownBibTag}>
15508
16015
  </div>
15509
16016
  `;
@@ -19481,6 +19988,414 @@ class AuroInputUtilities {
19481
19988
  }
19482
19989
  }
19483
19990
 
19991
+ class DateFormatter {
19992
+
19993
+ constructor() {
19994
+
19995
+ /**
19996
+ * @description Parses a date string into its components.
19997
+ * @param {string} dateStr - Date string to parse.
19998
+ * @param {string} format - Date format to parse.
19999
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
20000
+ */
20001
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
20002
+
20003
+ // Guard Clause: Date string is defined
20004
+ if (!dateStr) {
20005
+ return undefined;
20006
+ }
20007
+
20008
+ // Assume the separator is a "/" a defined in our code base
20009
+ const separator = '/';
20010
+
20011
+ // Get the parts of the date and format
20012
+ const valueParts = dateStr.split(separator);
20013
+ const formatParts = format.split(separator);
20014
+
20015
+ // Check if the value and format have the correct number of parts
20016
+ if (valueParts.length !== formatParts.length) {
20017
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
20018
+ }
20019
+
20020
+ // Holds the result to be returned
20021
+ const result = formatParts.reduce((acc, part, index) => {
20022
+ const value = valueParts[index];
20023
+
20024
+ if ((/m/iu).test(part)) {
20025
+ acc.month = value;
20026
+ } else if ((/d/iu).test(part)) {
20027
+ acc.day = value;
20028
+ } else if ((/y/iu).test(part)) {
20029
+ acc.year = value;
20030
+ }
20031
+
20032
+ return acc;
20033
+ }, {});
20034
+
20035
+ // If we found all the parts, return the result
20036
+ if (result.month && result.year) {
20037
+ return result;
20038
+ }
20039
+
20040
+ // Throw an error to let the dev know we were unable to parse the date string
20041
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
20042
+ };
20043
+
20044
+ /**
20045
+ * Convert a date object to string format.
20046
+ * @param {Object} date - Date to convert to string.
20047
+ * @returns {Object} Returns the date as a string.
20048
+ */
20049
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
20050
+ year: "numeric",
20051
+ month: "2-digit",
20052
+ day: "2-digit",
20053
+ });
20054
+
20055
+ /**
20056
+ * Converts a date string to a North American date format.
20057
+ * @param {String} dateStr - Date to validate.
20058
+ * @param {String} format - Date format to validate against.
20059
+ * @returns {Boolean}
20060
+ */
20061
+ this.toNorthAmericanFormat = (dateStr, format) => {
20062
+
20063
+ if (format === 'mm/dd/yyyy') {
20064
+ return dateStr;
20065
+ }
20066
+
20067
+ const parsedDate = this.parseDate(dateStr, format);
20068
+
20069
+ if (!parsedDate) {
20070
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
20071
+ }
20072
+
20073
+ const { month, day, year } = parsedDate;
20074
+
20075
+ const dateParts = [];
20076
+ if (month) {
20077
+ dateParts.push(month);
20078
+ }
20079
+
20080
+ if (day) {
20081
+ dateParts.push(day);
20082
+ }
20083
+
20084
+ if (year) {
20085
+ dateParts.push(year);
20086
+ }
20087
+
20088
+ return dateParts.join('/');
20089
+ };
20090
+ }
20091
+ }
20092
+ const dateFormatter = new DateFormatter();
20093
+
20094
+ // filepath: dateConstraints.mjs
20095
+ const DATE_UTIL_CONSTRAINTS = {
20096
+ maxDay: 31,
20097
+ maxMonth: 12,
20098
+ maxYear: 2400,
20099
+ minDay: 1,
20100
+ minMonth: 1,
20101
+ minYear: 1900,
20102
+ };
20103
+
20104
+ class AuroDateUtilitiesBase {
20105
+
20106
+ /**
20107
+ * @description The maximum day value allowed by the various utilities in this class.
20108
+ * @readonly
20109
+ * @type {Number}
20110
+ */
20111
+ get maxDay() {
20112
+ return DATE_UTIL_CONSTRAINTS.maxDay;
20113
+ }
20114
+
20115
+ /**
20116
+ * @description The maximum month value allowed by the various utilities in this class.
20117
+ * @readonly
20118
+ * @type {Number}
20119
+ */
20120
+ get maxMonth() {
20121
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
20122
+ }
20123
+
20124
+ /**
20125
+ * @description The maximum year value allowed by the various utilities in this class.
20126
+ * @readonly
20127
+ * @type {Number}
20128
+ */
20129
+ get maxYear() {
20130
+ return DATE_UTIL_CONSTRAINTS.maxYear;
20131
+ }
20132
+
20133
+ /**
20134
+ * @description The minimum day value allowed by the various utilities in this class.
20135
+ * @readonly
20136
+ * @type {Number}
20137
+ */
20138
+ get minDay() {
20139
+ return DATE_UTIL_CONSTRAINTS.minDay;
20140
+ }
20141
+
20142
+ /**
20143
+ * @description The minimum month value allowed by the various utilities in this class.
20144
+ * @readonly
20145
+ * @type {Number}
20146
+ */
20147
+ get minMonth() {
20148
+ return DATE_UTIL_CONSTRAINTS.minMonth;
20149
+ }
20150
+
20151
+ /**
20152
+ * @description The minimum year value allowed by the various utilities in this class.
20153
+ * @readonly
20154
+ * @type {Number}
20155
+ */
20156
+ get minYear() {
20157
+ return DATE_UTIL_CONSTRAINTS.minYear;
20158
+ }
20159
+ }
20160
+
20161
+ /* eslint-disable no-magic-numbers */
20162
+
20163
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
20164
+
20165
+ /**
20166
+ * Returns the current century.
20167
+ * @returns {String} The current century.
20168
+ */
20169
+ getCentury () {
20170
+ return String(new Date().getFullYear()).slice(0, 2);
20171
+ }
20172
+
20173
+ /**
20174
+ * Returns a four digit year.
20175
+ * @param {String} year - The year to convert to four digits.
20176
+ * @returns {String} The four digit year.
20177
+ */
20178
+ getFourDigitYear (year) {
20179
+
20180
+ const strYear = String(year).trim();
20181
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
20182
+ }
20183
+
20184
+ constructor() {
20185
+
20186
+ super();
20187
+
20188
+ /**
20189
+ * Compares two dates to see if they match.
20190
+ * @param {Object} date1 - First date to compare.
20191
+ * @param {Object} date2 - Second date to compare.
20192
+ * @returns {Boolean} Returns true if the dates match.
20193
+ */
20194
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
20195
+
20196
+ /**
20197
+ * Returns true if value passed in is a valid date.
20198
+ * @param {String} date - Date to validate.
20199
+ * @param {String} format - Date format to validate against.
20200
+ * @returns {Boolean}
20201
+ */
20202
+ this.validDateStr = (date, format) => {
20203
+
20204
+ // The length we expect the date string to be
20205
+ const dateStrLength = format.length;
20206
+
20207
+ // Guard Clause: Date and format are defined
20208
+ if (typeof date === "undefined" || typeof format === "undefined") {
20209
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
20210
+ }
20211
+
20212
+ // Guard Clause: Date should be of type string
20213
+ if (typeof date !== "string") {
20214
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
20215
+ }
20216
+
20217
+ // Guard Clause: Format should be of type string
20218
+ if (typeof format !== "string") {
20219
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
20220
+ }
20221
+
20222
+ // Guard Clause: Length is what we expect it to be
20223
+ if (date.length !== dateStrLength) {
20224
+ return false;
20225
+ }
20226
+ // Get a formatted date string and parse it
20227
+ const dateParts = dateFormatter.parseDate(date, format);
20228
+
20229
+ // Guard Clause: Date parse succeeded
20230
+ if (!dateParts) {
20231
+ return false;
20232
+ }
20233
+
20234
+ // Create the expected date string based on the date parts
20235
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
20236
+
20237
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
20238
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
20239
+
20240
+ // Get the date string of the date object we created from the string date
20241
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
20242
+
20243
+ // Guard Clause: Generated date matches date string input
20244
+ if (expectedDateStr !== actualDateStr) {
20245
+ return false;
20246
+ }
20247
+
20248
+ // If we passed all other checks, we can assume the date is valid
20249
+ return true;
20250
+ };
20251
+
20252
+ /**
20253
+ * Determines if a string date value matches the format provided.
20254
+ * @param {string} value = The date string value.
20255
+ * @param { string} format = The date format to match against.
20256
+ * @returns {boolean}
20257
+ */
20258
+ this.dateAndFormatMatch = (value, format) => {
20259
+
20260
+ // Ensure we have both values we need to do the comparison
20261
+ if (!value || !format) {
20262
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
20263
+ }
20264
+
20265
+ // If the lengths are different, they cannot match
20266
+ if (value.length !== format.length) {
20267
+ return false;
20268
+ }
20269
+
20270
+ // Get the parts of the date
20271
+ const dateParts = dateFormatter.parseDate(value, format);
20272
+
20273
+ // Validator for day
20274
+ const dayValueIsValid = (day) => {
20275
+
20276
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
20277
+ if (!dateParts.day) {
20278
+ return true;
20279
+ }
20280
+
20281
+ // Guard clause: ensure day exists.
20282
+ if (!day) {
20283
+ return false;
20284
+ }
20285
+
20286
+ // Convert day to number
20287
+ const numDay = Number.parseInt(day, 10);
20288
+
20289
+ // Guard clause: ensure day is a valid integer
20290
+ if (Number.isNaN(numDay)) {
20291
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
20292
+ }
20293
+
20294
+ // Guard clause: ensure day is within the valid range
20295
+ if (numDay < this.minDay || numDay > this.maxDay) {
20296
+ return false;
20297
+ }
20298
+
20299
+ // Default return
20300
+ return true;
20301
+ };
20302
+
20303
+ // Validator for month
20304
+ const monthValueIsValid = (month) => {
20305
+
20306
+ // Guard clause: ensure month exists.
20307
+ if (!month) {
20308
+ return false;
20309
+ }
20310
+
20311
+ // Convert month to number
20312
+ const numMonth = Number.parseInt(month, 10);
20313
+
20314
+ // Guard clause: ensure month is a valid integer
20315
+ if (Number.isNaN(numMonth)) {
20316
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
20317
+ }
20318
+
20319
+ // Guard clause: ensure month is within the valid range
20320
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
20321
+ return false;
20322
+ }
20323
+
20324
+ // Default return
20325
+ return true;
20326
+ };
20327
+
20328
+ // Validator for year
20329
+ const yearIsValid = (_year) => {
20330
+
20331
+ // Guard clause: ensure year exists.
20332
+ if (!_year) {
20333
+ return false;
20334
+ }
20335
+
20336
+ // Get the full year
20337
+ const year = this.getFourDigitYear(_year);
20338
+
20339
+ // Convert year to number
20340
+ const numYear = Number.parseInt(year, 10);
20341
+
20342
+ // Guard clause: ensure year is a valid integer
20343
+ if (Number.isNaN(numYear)) {
20344
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
20345
+ }
20346
+
20347
+ // Guard clause: ensure year is within the valid range
20348
+ if (numYear < this.minYear || numYear > this.maxYear) {
20349
+ return false;
20350
+ }
20351
+
20352
+ // Default return
20353
+ return true;
20354
+ };
20355
+
20356
+ // Self-contained checks for month, day, and year
20357
+ const checks = [
20358
+ monthValueIsValid(dateParts.month),
20359
+ dayValueIsValid(dateParts.day),
20360
+ yearIsValid(dateParts.year)
20361
+ ];
20362
+
20363
+ // If any of the checks failed, the date format does not match and the result is invalid
20364
+ const isValid = checks.every((check) => check === true);
20365
+
20366
+ // If the check is invalid, return false
20367
+ if (!isValid) {
20368
+ return false;
20369
+ }
20370
+
20371
+ // Default case
20372
+ return true;
20373
+ };
20374
+ }
20375
+ }
20376
+
20377
+ // Export a class instance
20378
+ const dateUtilities = new AuroDateUtilities();
20379
+
20380
+ // Export the class instance methods individually
20381
+ const {
20382
+ datesMatch,
20383
+ validDateStr,
20384
+ dateAndFormatMatch,
20385
+ minDay,
20386
+ minMonth,
20387
+ minYear,
20388
+ maxDay,
20389
+ maxMonth,
20390
+ maxYear
20391
+ } = dateUtilities;
20392
+
20393
+ const {
20394
+ toNorthAmericanFormat,
20395
+ parseDate,
20396
+ getDateAsString
20397
+ } = dateFormatter;
20398
+
19484
20399
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
19485
20400
  // See LICENSE in the project root for license information.
19486
20401
 
@@ -19556,6 +20471,7 @@ let AuroLibraryRuntimeUtils$1 = class AuroLibraryRuntimeUtils {
19556
20471
 
19557
20472
 
19558
20473
  class AuroFormValidation {
20474
+
19559
20475
  constructor() {
19560
20476
  this.runtimeUtils = new AuroLibraryRuntimeUtils$1();
19561
20477
  }
@@ -19647,17 +20563,17 @@ class AuroFormValidation {
19647
20563
  ]
19648
20564
  }
19649
20565
  };
19650
-
20566
+
19651
20567
  let elementType;
19652
20568
  if (this.runtimeUtils.elementMatch(elem, 'auro-input')) {
19653
20569
  elementType = 'input';
19654
20570
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-counter') || this.runtimeUtils.elementMatch(elem, 'auro-counter-group')) {
19655
20571
  elementType = 'counter';
19656
20572
  }
19657
-
20573
+
19658
20574
  if (elementType) {
19659
20575
  const rules = validationRules[elementType];
19660
-
20576
+
19661
20577
  if (rules) {
19662
20578
  Object.values(rules).flat().forEach(rule => {
19663
20579
  if (rule.check(elem)) {
@@ -19683,48 +20599,82 @@ class AuroFormValidation {
19683
20599
  if (!elem.value.match(emailRegex)) {
19684
20600
  elem.validity = 'patternMismatch';
19685
20601
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20602
+ return;
19686
20603
  }
19687
20604
  } else if (elem.type === 'credit-card') {
19688
20605
  if (elem.value.length > 0 && elem.value.length < elem.validationCCLength) {
19689
20606
  elem.validity = 'tooShort';
19690
20607
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20608
+ return;
19691
20609
  }
19692
20610
  } else if (elem.type === 'number') {
19693
20611
  if (elem.max !== undefined && Number(elem.max) < Number(elem.value)) {
19694
20612
  elem.validity = 'rangeOverflow';
19695
20613
  elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
20614
+ return;
19696
20615
  }
19697
20616
 
19698
20617
  if (elem.min !== undefined && elem.value?.length > 0 && Number(elem.min) > Number(elem.value)) {
19699
20618
  elem.validity = 'rangeUnderflow';
19700
20619
  elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
20620
+ return;
19701
20621
  }
19702
- } else if (elem.type === 'date') {
19703
- if (elem.value?.length > 0 && elem.value?.length < elem.lengthForType) {
20622
+ } else if (elem.type === 'date' && elem.value?.length > 0) {
20623
+
20624
+ // Guard Clause: if the value is too short
20625
+ if (elem.value.length < elem.lengthForType) {
20626
+
19704
20627
  elem.validity = 'tooShort';
19705
20628
  elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
19706
- } else if (elem.value?.length === elem.lengthForType && elem.util.toNorthAmericanFormat(elem.value, elem.format)) {
19707
- const formattedValue = elem.util.toNorthAmericanFormat(elem.value, elem.format);
19708
- const valueDate = new Date(formattedValue.dateForComparison);
20629
+ return;
20630
+ }
20631
+
20632
+ // Guard Clause: If the value is too long for the type
20633
+ if (elem.value?.length > elem.lengthForType) {
19709
20634
 
19710
- // validate max
19711
- if (elem.max?.length === elem.lengthForType) {
19712
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
20635
+ elem.validity = 'tooLong';
20636
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || '';
20637
+ return;
20638
+ }
20639
+
20640
+ // Validate that the date passed was the correct format
20641
+ if (!dateAndFormatMatch(elem.value, elem.format)) {
20642
+ elem.validity = 'patternMismatch';
20643
+ elem.errorMessage = elem.setCustomValidityForType || elem.setCustomValidity || 'Invalid Date Format Entered';
20644
+ return;
20645
+ }
20646
+
20647
+ // Validate that the date passed was a valid date
20648
+ if (!validDateStr(elem.value, elem.format)) {
20649
+ elem.validity = 'invalidDate';
20650
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
20651
+ return;
20652
+ }
19713
20653
 
19714
- if (valueDate > maxDate) {
19715
- elem.validity = 'rangeOverflow';
19716
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
19717
- }
20654
+ // Perform the rest of the validation
20655
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
20656
+ const valueDate = new Date(formattedValue);
20657
+
20658
+ // // Validate max date
20659
+ if (elem.max?.length === elem.lengthForType) {
20660
+
20661
+ const maxDate = new Date(toNorthAmericanFormat(elem.max, elem.format));
20662
+
20663
+ if (valueDate > maxDate) {
20664
+ elem.validity = 'rangeOverflow';
20665
+ elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
20666
+ return;
19718
20667
  }
20668
+ }
19719
20669
 
19720
- // validate min
19721
- if (elem.min?.length === elem.lengthForType) {
19722
- const minDate = new Date(elem.util.toNorthAmericanFormat(elem.min, elem.format).dateForComparison);
20670
+ // Validate min date
20671
+ if (elem.min?.length === elem.lengthForType) {
20672
+ const minDate = new Date(toNorthAmericanFormat(elem.min, elem.format));
19723
20673
 
19724
- if (valueDate < minDate) {
19725
- elem.validity = 'rangeUnderflow';
19726
- elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
19727
- }
20674
+ if (valueDate < minDate) {
20675
+ elem.validity = 'rangeUnderflow';
20676
+ elem.errorMessage = elem.setCustomValidityRangeUnderflow || elem.setCustomValidity || '';
20677
+ return;
19728
20678
  }
19729
20679
  }
19730
20680
  }
@@ -19843,7 +20793,7 @@ class AuroFormValidation {
19843
20793
  if (input.validationMessage.length > 0) {
19844
20794
  elem.errorMessage = input.validationMessage;
19845
20795
  }
19846
- } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
20796
+ } else if (this.inputElements?.length > 0 && elem.errorMessage === '') {
19847
20797
  const firstInput = this.inputElements[0];
19848
20798
 
19849
20799
  if (firstInput.validationMessage.length > 0) {
@@ -19965,6 +20915,33 @@ class BaseInput extends LitElement {
19965
20915
  static get properties() {
19966
20916
  return {
19967
20917
 
20918
+ /**
20919
+ * The value for the role attribute.
20920
+ */
20921
+ a11yRole: {
20922
+ type: String,
20923
+ attribute: true,
20924
+ reflect: true
20925
+ },
20926
+
20927
+ /**
20928
+ * The value for the aria-expanded attribute.
20929
+ */
20930
+ a11yExpanded: {
20931
+ type: Boolean,
20932
+ attribute: true,
20933
+ reflect: true
20934
+ },
20935
+
20936
+ /**
20937
+ * The value for the aria-controls attribute.
20938
+ */
20939
+ a11yControls: {
20940
+ type: String,
20941
+ attribute: true,
20942
+ reflect: true
20943
+ },
20944
+
19968
20945
  /**
19969
20946
  * If set, the label will remain fixed in the active position.
19970
20947
  */
@@ -20606,6 +21583,10 @@ class BaseInput extends LitElement {
20606
21583
  } else if (this.type === 'number') {
20607
21584
  this.inputMode = 'numeric';
20608
21585
  }
21586
+
21587
+ if (this.type === "date" && !this.format) {
21588
+ this.format = 'mm/dd/yyyy';
21589
+ }
20609
21590
  }
20610
21591
 
20611
21592
  /**
@@ -21849,6 +22830,7 @@ var helpTextVersion = '1.0.0';
21849
22830
 
21850
22831
  // build the component class
21851
22832
  class AuroInput extends BaseInput {
22833
+
21852
22834
  constructor() {
21853
22835
  super();
21854
22836
 
@@ -21961,7 +22943,7 @@ class AuroInput extends BaseInput {
21961
22943
  ?required="${this.required}"
21962
22944
  ?disabled="${this.disabled}"
21963
22945
  aria-describedby="${this.uniqueId}"
21964
- aria-invalid="${this.validity !== 'valid'}"
22946
+ ?aria-invalid="${this.validity !== 'valid'}"
21965
22947
  placeholder=${this.getPlaceholder()}
21966
22948
  lang="${ifDefined(this.lang)}"
21967
22949
  ?activeLabel="${this.activeLabel}"
@@ -21970,7 +22952,10 @@ class AuroInput extends BaseInput {
21970
22952
  autocapitalize="${ifDefined(this.autocapitalize ? this.autocapitalize : undefined)}"
21971
22953
  autocomplete="${ifDefined(this.autocomplete ? this.autocomplete : undefined)}"
21972
22954
  part="input"
21973
- />
22955
+ role="${ifDefined(this.a11yRole)}"
22956
+ aria-expanded="${ifDefined(this.a11yExpanded)}"
22957
+ aria-controls="${ifDefined(this.a11yControls)}"
22958
+ />
21974
22959
  </div>
21975
22960
  <div
21976
22961
  class="notificationIcons"
@@ -22116,7 +23101,6 @@ class AuroDatePicker extends LitElement {
22116
23101
  this.calendarEndDate = undefined;
22117
23102
  this.calendarFocusDate = this.value;
22118
23103
  this.format = 'mm/dd/yyyy';
22119
- this.fullscreenBreakpoint = 'sm';
22120
23104
  this.monthNames = [
22121
23105
  'January',
22122
23106
  'February',
@@ -22254,19 +23238,6 @@ class AuroDatePicker extends LitElement {
22254
23238
  reflect: true
22255
23239
  },
22256
23240
 
22257
- /**
22258
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
22259
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
22260
- *
22261
- * When expanded, the dropdown will automatically display in fullscreen mode
22262
- * if the screen size is equal to or smaller than the selected breakpoint.
22263
- * @default sm
22264
- */
22265
- fullscreenBreakpoint: {
22266
- type: String,
22267
- reflect: true
22268
- },
22269
-
22270
23241
  /**
22271
23242
  * If declared, make bib.fullscreen.headline in HeadingDisplay.
22272
23243
  * Otherwise, Heading 600.
@@ -23093,7 +24064,7 @@ class AuroDatePicker extends LitElement {
23093
24064
  ?disabled="${this.disabled}"
23094
24065
  ?error="${this.validity !== undefined && this.validity !== 'valid'}"
23095
24066
  disableEventShow
23096
- .fullscreenBreakpoint="${this.fullscreenBreakpoint}"
24067
+ fullscreenBreakpoint="sm"
23097
24068
  .placement="${this.placement}"
23098
24069
  .offset="${this.offset}"
23099
24070
  ?autoPlacement="${this.autoPlacement}"