@aurodesignsystem/auro-formkit 3.1.0-beta.1 → 3.2.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/components/checkbox/README.md +1 -1
  3. package/components/checkbox/demo/api.min.js +468 -25
  4. package/components/checkbox/demo/index.min.js +468 -25
  5. package/components/checkbox/demo/readme.md +1 -1
  6. package/components/checkbox/dist/index.js +468 -25
  7. package/components/checkbox/dist/registered.js +468 -25
  8. package/components/combobox/README.md +1 -1
  9. package/components/combobox/demo/api.min.js +1125 -74
  10. package/components/combobox/demo/index.min.js +1125 -74
  11. package/components/combobox/demo/readme.md +1 -1
  12. package/components/combobox/dist/auro-combobox.d.ts +30 -0
  13. package/components/combobox/dist/index.js +1125 -74
  14. package/components/combobox/dist/registered.js +1125 -74
  15. package/components/counter/README.md +1 -1
  16. package/components/counter/demo/api.min.js +570 -45
  17. package/components/counter/demo/index.min.js +570 -45
  18. package/components/counter/demo/readme.md +1 -1
  19. package/components/counter/dist/index.js +570 -45
  20. package/components/counter/dist/registered.js +570 -45
  21. package/components/datepicker/README.md +1 -1
  22. package/components/datepicker/demo/api.min.js +1073 -70
  23. package/components/datepicker/demo/index.min.js +1073 -70
  24. package/components/datepicker/demo/readme.md +1 -1
  25. package/components/datepicker/dist/index.js +1073 -70
  26. package/components/datepicker/dist/registered.js +1073 -70
  27. package/components/dropdown/README.md +1 -1
  28. package/components/dropdown/demo/api.md +8 -5
  29. package/components/dropdown/demo/api.min.js +104 -22
  30. package/components/dropdown/demo/index.min.js +104 -22
  31. package/components/dropdown/demo/readme.md +1 -1
  32. package/components/dropdown/dist/auro-dropdown.d.ts +29 -0
  33. package/components/dropdown/dist/index.js +104 -22
  34. package/components/dropdown/dist/registered.js +104 -22
  35. package/components/form/README.md +1 -1
  36. package/components/form/demo/readme.md +1 -1
  37. package/components/input/README.md +1 -1
  38. package/components/input/demo/api.md +4 -1
  39. package/components/input/demo/api.min.js +503 -25
  40. package/components/input/demo/index.min.js +503 -25
  41. package/components/input/demo/readme.md +1 -1
  42. package/components/input/dist/base-input.d.ts +24 -0
  43. package/components/input/dist/index.js +503 -25
  44. package/components/input/dist/registered.js +503 -25
  45. package/components/menu/README.md +1 -1
  46. package/components/menu/demo/readme.md +1 -1
  47. package/components/radio/README.md +1 -1
  48. package/components/radio/demo/api.min.js +468 -25
  49. package/components/radio/demo/index.min.js +468 -25
  50. package/components/radio/demo/readme.md +1 -1
  51. package/components/radio/dist/index.js +468 -25
  52. package/components/radio/dist/registered.js +468 -25
  53. package/components/select/README.md +1 -1
  54. package/components/select/demo/api.min.js +570 -45
  55. package/components/select/demo/index.min.js +570 -45
  56. package/components/select/demo/readme.md +1 -1
  57. package/components/select/dist/index.js +570 -45
  58. package/components/select/dist/registered.js +570 -45
  59. package/package.json +2 -2
@@ -44,6 +44,414 @@ const t={ATTRIBUTE:1},e$1=t=>(...e)=>({_$litDirective$:t,values:e});let i$1 = cl
44
44
  */
45
45
  const a=Symbol.for(""),o$1=t=>{if(t?.r===a)return t?._$litStatic$},s=t=>({_$litStatic$:t,r:a}),i=(t,...r)=>({_$litStatic$:r.reduce(((r,e,a)=>r+(t=>{if(void 0!==t._$litStatic$)return t._$litStatic$;throw Error(`Value passed to 'literal' function must be a 'literal' result: ${t}. Use 'unsafeStatic' to pass non-literal values, but\n take care to ensure page security.`)})(e)+t[a+1]),t[0]),r:a}),l=new Map,n=t=>(r,...e)=>{const a=e.length;let s,i;const n=[],u=[];let c,$=0,f=false;for(;$<a;){for(c=r[$];$<a&&void 0!==(i=e[$],s=o$1(i));)c+=s+r[++$],f=true;$!==a&&u.push(i),n.push(c),$++;}if($===a&&n.push(r[a]),f){const t=n.join("$$lit$$");void 0===(r=l.get(t))&&(n.raw=n,l.set(t,r=n)),e=u;}return t(r,...e)},u=n(x);
46
46
 
47
+ class DateFormatter {
48
+
49
+ constructor() {
50
+
51
+ /**
52
+ * @description Parses a date string into its components.
53
+ * @param {string} dateStr - Date string to parse.
54
+ * @param {string} format - Date format to parse.
55
+ * @returns {Object<key["month" | "day" | "year"]: number>|undefined}
56
+ */
57
+ this.parseDate = (dateStr, format = 'mm/dd/yyyy') => {
58
+
59
+ // Guard Clause: Date string is defined
60
+ if (!dateStr) {
61
+ return undefined;
62
+ }
63
+
64
+ // Assume the separator is a "/" a defined in our code base
65
+ const separator = '/';
66
+
67
+ // Get the parts of the date and format
68
+ const valueParts = dateStr.split(separator);
69
+ const formatParts = format.split(separator);
70
+
71
+ // Check if the value and format have the correct number of parts
72
+ if (valueParts.length !== formatParts.length) {
73
+ throw new Error('AuroDatepickerUtilities | parseDate: Date string and format length do not match');
74
+ }
75
+
76
+ // Holds the result to be returned
77
+ const result = formatParts.reduce((acc, part, index) => {
78
+ const value = valueParts[index];
79
+
80
+ if ((/m/iu).test(part)) {
81
+ acc.month = value;
82
+ } else if ((/d/iu).test(part)) {
83
+ acc.day = value;
84
+ } else if ((/y/iu).test(part)) {
85
+ acc.year = value;
86
+ }
87
+
88
+ return acc;
89
+ }, {});
90
+
91
+ // If we found all the parts, return the result
92
+ if (result.month && result.year) {
93
+ return result;
94
+ }
95
+
96
+ // Throw an error to let the dev know we were unable to parse the date string
97
+ throw new Error('AuroDatepickerUtilities | parseDate: Unable to parse date string');
98
+ };
99
+
100
+ /**
101
+ * Convert a date object to string format.
102
+ * @param {Object} date - Date to convert to string.
103
+ * @returns {Object} Returns the date as a string.
104
+ */
105
+ this.getDateAsString = (date) => date.toLocaleDateString(undefined, {
106
+ year: "numeric",
107
+ month: "2-digit",
108
+ day: "2-digit",
109
+ });
110
+
111
+ /**
112
+ * Converts a date string to a North American date format.
113
+ * @param {String} dateStr - Date to validate.
114
+ * @param {String} format - Date format to validate against.
115
+ * @returns {Boolean}
116
+ */
117
+ this.toNorthAmericanFormat = (dateStr, format) => {
118
+
119
+ if (format === 'mm/dd/yyyy') {
120
+ return dateStr;
121
+ }
122
+
123
+ const parsedDate = this.parseDate(dateStr, format);
124
+
125
+ if (!parsedDate) {
126
+ throw new Error('AuroDatepickerUtilities | toNorthAmericanFormat: Unable to parse date string');
127
+ }
128
+
129
+ const { month, day, year } = parsedDate;
130
+
131
+ const dateParts = [];
132
+ if (month) {
133
+ dateParts.push(month);
134
+ }
135
+
136
+ if (day) {
137
+ dateParts.push(day);
138
+ }
139
+
140
+ if (year) {
141
+ dateParts.push(year);
142
+ }
143
+
144
+ return dateParts.join('/');
145
+ };
146
+ }
147
+ }
148
+ const dateFormatter = new DateFormatter();
149
+
150
+ // filepath: dateConstraints.mjs
151
+ const DATE_UTIL_CONSTRAINTS = {
152
+ maxDay: 31,
153
+ maxMonth: 12,
154
+ maxYear: 2400,
155
+ minDay: 1,
156
+ minMonth: 1,
157
+ minYear: 1900,
158
+ };
159
+
160
+ class AuroDateUtilitiesBase {
161
+
162
+ /**
163
+ * @description The maximum day value allowed by the various utilities in this class.
164
+ * @readonly
165
+ * @type {Number}
166
+ */
167
+ get maxDay() {
168
+ return DATE_UTIL_CONSTRAINTS.maxDay;
169
+ }
170
+
171
+ /**
172
+ * @description The maximum month value allowed by the various utilities in this class.
173
+ * @readonly
174
+ * @type {Number}
175
+ */
176
+ get maxMonth() {
177
+ return DATE_UTIL_CONSTRAINTS.maxMonth;
178
+ }
179
+
180
+ /**
181
+ * @description The maximum year value allowed by the various utilities in this class.
182
+ * @readonly
183
+ * @type {Number}
184
+ */
185
+ get maxYear() {
186
+ return DATE_UTIL_CONSTRAINTS.maxYear;
187
+ }
188
+
189
+ /**
190
+ * @description The minimum day value allowed by the various utilities in this class.
191
+ * @readonly
192
+ * @type {Number}
193
+ */
194
+ get minDay() {
195
+ return DATE_UTIL_CONSTRAINTS.minDay;
196
+ }
197
+
198
+ /**
199
+ * @description The minimum month value allowed by the various utilities in this class.
200
+ * @readonly
201
+ * @type {Number}
202
+ */
203
+ get minMonth() {
204
+ return DATE_UTIL_CONSTRAINTS.minMonth;
205
+ }
206
+
207
+ /**
208
+ * @description The minimum year value allowed by the various utilities in this class.
209
+ * @readonly
210
+ * @type {Number}
211
+ */
212
+ get minYear() {
213
+ return DATE_UTIL_CONSTRAINTS.minYear;
214
+ }
215
+ }
216
+
217
+ /* eslint-disable no-magic-numbers */
218
+
219
+ class AuroDateUtilities extends AuroDateUtilitiesBase {
220
+
221
+ /**
222
+ * Returns the current century.
223
+ * @returns {String} The current century.
224
+ */
225
+ getCentury () {
226
+ return String(new Date().getFullYear()).slice(0, 2);
227
+ }
228
+
229
+ /**
230
+ * Returns a four digit year.
231
+ * @param {String} year - The year to convert to four digits.
232
+ * @returns {String} The four digit year.
233
+ */
234
+ getFourDigitYear (year) {
235
+
236
+ const strYear = String(year).trim();
237
+ return strYear.length <= 2 ? this.getCentury() + strYear : strYear;
238
+ }
239
+
240
+ constructor() {
241
+
242
+ super();
243
+
244
+ /**
245
+ * Compares two dates to see if they match.
246
+ * @param {Object} date1 - First date to compare.
247
+ * @param {Object} date2 - Second date to compare.
248
+ * @returns {Boolean} Returns true if the dates match.
249
+ */
250
+ this.datesMatch = (date1, date2) => new Date(date1).getTime() === new Date(date2).getTime();
251
+
252
+ /**
253
+ * Returns true if value passed in is a valid date.
254
+ * @param {String} date - Date to validate.
255
+ * @param {String} format - Date format to validate against.
256
+ * @returns {Boolean}
257
+ */
258
+ this.validDateStr = (date, format) => {
259
+
260
+ // The length we expect the date string to be
261
+ const dateStrLength = format.length;
262
+
263
+ // Guard Clause: Date and format are defined
264
+ if (typeof date === "undefined" || typeof format === "undefined") {
265
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date and format are required');
266
+ }
267
+
268
+ // Guard Clause: Date should be of type string
269
+ if (typeof date !== "string") {
270
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Date must be a string');
271
+ }
272
+
273
+ // Guard Clause: Format should be of type string
274
+ if (typeof format !== "string") {
275
+ throw new Error('AuroDatepickerUtilities | validateDateStr: Format must be a string');
276
+ }
277
+
278
+ // Guard Clause: Length is what we expect it to be
279
+ if (date.length !== dateStrLength) {
280
+ return false;
281
+ }
282
+ // Get a formatted date string and parse it
283
+ const dateParts = dateFormatter.parseDate(date, format);
284
+
285
+ // Guard Clause: Date parse succeeded
286
+ if (!dateParts) {
287
+ return false;
288
+ }
289
+
290
+ // Create the expected date string based on the date parts
291
+ const expectedDateStr = `${dateParts.month}/${dateParts.day || "01"}/${this.getFourDigitYear(dateParts.year)}`;
292
+
293
+ // Generate a date object that we will extract a string date from to compare to the passed in date string
294
+ const dateObj = new Date(this.getFourDigitYear(dateParts.year), dateParts.month - 1, dateParts.day || 1);
295
+
296
+ // Get the date string of the date object we created from the string date
297
+ const actualDateStr = dateFormatter.getDateAsString(dateObj);
298
+
299
+ // Guard Clause: Generated date matches date string input
300
+ if (expectedDateStr !== actualDateStr) {
301
+ return false;
302
+ }
303
+
304
+ // If we passed all other checks, we can assume the date is valid
305
+ return true;
306
+ };
307
+
308
+ /**
309
+ * Determines if a string date value matches the format provided.
310
+ * @param {string} value = The date string value.
311
+ * @param { string} format = The date format to match against.
312
+ * @returns {boolean}
313
+ */
314
+ this.dateAndFormatMatch = (value, format) => {
315
+
316
+ // Ensure we have both values we need to do the comparison
317
+ if (!value || !format) {
318
+ throw new Error('AuroFormValidation | dateFormatMatch: value and format are required');
319
+ }
320
+
321
+ // If the lengths are different, they cannot match
322
+ if (value.length !== format.length) {
323
+ return false;
324
+ }
325
+
326
+ // Get the parts of the date
327
+ const dateParts = dateFormatter.parseDate(value, format);
328
+
329
+ // Validator for day
330
+ const dayValueIsValid = (day) => {
331
+
332
+ // Guard clause: if there is no day in the dateParts, we can ignore this check.
333
+ if (!dateParts.day) {
334
+ return true;
335
+ }
336
+
337
+ // Guard clause: ensure day exists.
338
+ if (!day) {
339
+ return false;
340
+ }
341
+
342
+ // Convert day to number
343
+ const numDay = Number.parseInt(day, 10);
344
+
345
+ // Guard clause: ensure day is a valid integer
346
+ if (Number.isNaN(numDay)) {
347
+ throw new Error('AuroDatepickerUtilities | dayValueIsValid: Unable to parse day value integer');
348
+ }
349
+
350
+ // Guard clause: ensure day is within the valid range
351
+ if (numDay < this.minDay || numDay > this.maxDay) {
352
+ return false;
353
+ }
354
+
355
+ // Default return
356
+ return true;
357
+ };
358
+
359
+ // Validator for month
360
+ const monthValueIsValid = (month) => {
361
+
362
+ // Guard clause: ensure month exists.
363
+ if (!month) {
364
+ return false;
365
+ }
366
+
367
+ // Convert month to number
368
+ const numMonth = Number.parseInt(month, 10);
369
+
370
+ // Guard clause: ensure month is a valid integer
371
+ if (Number.isNaN(numMonth)) {
372
+ throw new Error('AuroDatepickerUtilities | monthValueIsValid: Unable to parse month value integer');
373
+ }
374
+
375
+ // Guard clause: ensure month is within the valid range
376
+ if (numMonth < this.minMonth || numMonth > this.maxMonth) {
377
+ return false;
378
+ }
379
+
380
+ // Default return
381
+ return true;
382
+ };
383
+
384
+ // Validator for year
385
+ const yearIsValid = (_year) => {
386
+
387
+ // Guard clause: ensure year exists.
388
+ if (!_year) {
389
+ return false;
390
+ }
391
+
392
+ // Get the full year
393
+ const year = this.getFourDigitYear(_year);
394
+
395
+ // Convert year to number
396
+ const numYear = Number.parseInt(year, 10);
397
+
398
+ // Guard clause: ensure year is a valid integer
399
+ if (Number.isNaN(numYear)) {
400
+ throw new Error('AuroDatepickerUtilities | yearValueIsValid: Unable to parse year value integer');
401
+ }
402
+
403
+ // Guard clause: ensure year is within the valid range
404
+ if (numYear < this.minYear || numYear > this.maxYear) {
405
+ return false;
406
+ }
407
+
408
+ // Default return
409
+ return true;
410
+ };
411
+
412
+ // Self-contained checks for month, day, and year
413
+ const checks = [
414
+ monthValueIsValid(dateParts.month),
415
+ dayValueIsValid(dateParts.day),
416
+ yearIsValid(dateParts.year)
417
+ ];
418
+
419
+ // If any of the checks failed, the date format does not match and the result is invalid
420
+ const isValid = checks.every((check) => check === true);
421
+
422
+ // If the check is invalid, return false
423
+ if (!isValid) {
424
+ return false;
425
+ }
426
+
427
+ // Default case
428
+ return true;
429
+ };
430
+ }
431
+ }
432
+
433
+ // Export a class instance
434
+ const dateUtilities = new AuroDateUtilities();
435
+
436
+ // Export the class instance methods individually
437
+ const {
438
+ datesMatch,
439
+ validDateStr,
440
+ dateAndFormatMatch,
441
+ minDay,
442
+ minMonth,
443
+ minYear,
444
+ maxDay,
445
+ maxMonth,
446
+ maxYear
447
+ } = dateUtilities;
448
+
449
+ const {
450
+ toNorthAmericanFormat,
451
+ parseDate,
452
+ getDateAsString
453
+ } = dateFormatter;
454
+
47
455
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
48
456
  // See LICENSE in the project root for license information.
49
457
 
@@ -119,6 +527,7 @@ let AuroLibraryRuntimeUtils$3 = class AuroLibraryRuntimeUtils {
119
527
 
120
528
 
121
529
  class AuroFormValidation {
530
+
122
531
  constructor() {
123
532
  this.runtimeUtils = new AuroLibraryRuntimeUtils$3();
124
533
  }
@@ -210,17 +619,17 @@ 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 @@ 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) {
272
690
 
273
- // validate max
274
- if (elem.max?.length === elem.lengthForType) {
275
- const maxDate = new Date(elem.util.toNorthAmericanFormat(elem.max, elem.format).dateForComparison);
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(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(elem.value, elem.format)) {
705
+ elem.validity = 'invalidDate';
706
+ elem.errorMessage = elem.setCustomValidityInvalidDate || elem.setCustomValidity || 'Invalid Date Entered';
707
+ return;
708
+ }
276
709
 
277
- if (valueDate > maxDate) {
278
- elem.validity = 'rangeOverflow';
279
- elem.errorMessage = elem.setCustomValidityRangeOverflow || elem.setCustomValidity || '';
280
- }
710
+ // Perform the rest of the validation
711
+ const formattedValue = toNorthAmericanFormat(elem.value, elem.format);
712
+ const valueDate = new Date(formattedValue);
713
+
714
+ // // Validate max date
715
+ if (elem.max?.length === elem.lengthForType) {
716
+
717
+ const maxDate = new Date(toNorthAmericanFormat(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(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 @@ 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) {
@@ -2314,7 +2757,7 @@ class AuroFloatingUI {
2314
2757
  /**
2315
2758
  * @private
2316
2759
  * getting called on 'blur' in trigger or `focusin` in document
2317
- *
2760
+ *
2318
2761
  * Hides the bib if focus moves outside of the trigger or bib, unless a 'noHideOnThisFocusLoss' flag is set.
2319
2762
  * This method checks if the currently active element is still within the trigger or bib.
2320
2763
  * If not, and if the bib isn't in fullscreen mode with focus lost, it hides the bib.
@@ -2430,7 +2873,7 @@ class AuroFloatingUI {
2430
2873
  // Close any other dropdown that is already open
2431
2874
  const existedVisibleFloatingUI = document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
2432
2875
  if (existedVisibleFloatingUI && existedVisibleFloatingUI !== this &&
2433
- existedVisibleFloatingUI.isPopoverVisible &&
2876
+ existedVisibleFloatingUI.element.isPopoverVisible &&
2434
2877
  document.expandedAuroFloater.eventPrefix === this.eventPrefix) {
2435
2878
  document.expandedAuroFloater.hideBib();
2436
2879
  }
@@ -2606,7 +3049,7 @@ class AuroFloatingUI {
2606
3049
  this.id = window.crypto.randomUUID();
2607
3050
  this.element.setAttribute('id', this.id);
2608
3051
  }
2609
-
3052
+
2610
3053
  this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
2611
3054
  }
2612
3055
 
@@ -2659,7 +3102,7 @@ class AuroFloatingUI {
2659
3102
  if (this.element.bib) {
2660
3103
  this.element.shadowRoot.append(this.element.bib);
2661
3104
  }
2662
-
3105
+
2663
3106
  // Remove event & keyboard listeners
2664
3107
  if (this.element?.trigger) {
2665
3108
  this.element.trigger.removeEventListener('keydown', this.handleEvent);
@@ -3390,6 +3833,7 @@ var helpTextVersion = '1.0.0';
3390
3833
  * @csspart helpText - The helpText content container.
3391
3834
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3392
3835
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3836
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3393
3837
  */
3394
3838
  class AuroDropdown extends r {
3395
3839
  constructor() {
@@ -3435,7 +3879,9 @@ class AuroDropdown extends r {
3435
3879
  this.rounded = false;
3436
3880
  this.tabIndex = 0;
3437
3881
  this.noToggle = false;
3882
+ this.a11yAutocomplete = 'none';
3438
3883
  this.labeled = true;
3884
+ this.a11yRole = 'combobox';
3439
3885
  this.onDark = false;
3440
3886
 
3441
3887
  // floaterConfig
@@ -3571,6 +4017,16 @@ class AuroDropdown extends r {
3571
4017
  type: Number
3572
4018
  },
3573
4019
 
4020
+ /**
4021
+ * The unique ID for the dropdown bib element.
4022
+ * @private
4023
+ */
4024
+ dropdownId: {
4025
+ type: String,
4026
+ reflect: false,
4027
+ attribute: false
4028
+ },
4029
+
3574
4030
  /**
3575
4031
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3576
4032
  */
@@ -3738,6 +4194,23 @@ class AuroDropdown extends r {
3738
4194
  */
3739
4195
  tabIndex: {
3740
4196
  type: Number
4197
+ },
4198
+
4199
+ /**
4200
+ * The value for the role attribute of the trigger element.
4201
+ */
4202
+ a11yRole: {
4203
+ type: String || undefined,
4204
+ attribute: false,
4205
+ reflect: false
4206
+ },
4207
+
4208
+ /**
4209
+ * The value for the aria-autocomplete attribute of the trigger element.
4210
+ */
4211
+ a11yAutocomplete: {
4212
+ type: String,
4213
+ attribute: false,
3741
4214
  }
3742
4215
  };
3743
4216
  }
@@ -3799,7 +4272,22 @@ class AuroDropdown extends r {
3799
4272
  }
3800
4273
 
3801
4274
  firstUpdated() {
4275
+
4276
+ // Configure the floater to, this will generate the ID for the bib
3802
4277
  this.floater.configure(this, 'auroDropdown');
4278
+
4279
+ /**
4280
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4281
+ * @event auroDropdown-idAdded
4282
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4283
+ */
4284
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4285
+
4286
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4287
+ if (!this.triggerContentFocusable) {
4288
+ this.dropdownId = this.floater.element.id;
4289
+ }
4290
+
3803
4291
  this.bibContent = this.floater.element.bib;
3804
4292
 
3805
4293
  // Add the tag name as an attribute if it is different than the component name
@@ -3951,6 +4439,30 @@ class AuroDropdown extends r {
3951
4439
  });
3952
4440
  }
3953
4441
 
4442
+ /*
4443
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4444
+ * @private
4445
+ * @method setTriggerAriaAttributes
4446
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4447
+ */
4448
+ clearTriggerA11yAttributes(triggerElement) {
4449
+
4450
+ if (!triggerElement || !triggerElement.removeAttribute) {
4451
+ return;
4452
+ }
4453
+
4454
+ // Reset appropriate attributes for a11y
4455
+ triggerElement.removeAttribute('aria-labelledby');
4456
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4457
+ triggerElement.removeAttribute('id');
4458
+ }
4459
+ triggerElement.removeAttribute('role');
4460
+ triggerElement.removeAttribute('aria-expanded');
4461
+
4462
+ triggerElement.removeAttribute('aria-controls');
4463
+ triggerElement.removeAttribute('aria-autocomplete');
4464
+ }
4465
+
3954
4466
  /**
3955
4467
  * Handles changes to the trigger content slot and updates related properties.
3956
4468
  *
@@ -3964,32 +4476,41 @@ class AuroDropdown extends r {
3964
4476
  * @returns {void}
3965
4477
  */
3966
4478
  handleTriggerContentSlotChange(event) {
4479
+
3967
4480
  this.floater.handleTriggerTabIndex();
3968
4481
 
4482
+ // Get the trigger
4483
+ const trigger = this.shadowRoot.querySelector('#trigger');
4484
+
4485
+ // Get the trigger slot
3969
4486
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
3970
4487
 
4488
+ // If there's a trigger slot
3971
4489
  if (triggerSlot) {
3972
4490
 
4491
+ // Get the content nodes to see if there are any children
3973
4492
  const triggerContentNodes = triggerSlot.assignedNodes();
3974
4493
 
4494
+ // If there are children
3975
4495
  if (triggerContentNodes) {
3976
4496
 
3977
- triggerContentNodes.forEach((node) => {
3978
- if (!this.triggerContentFocusable) {
3979
- this.triggerContentFocusable = this.containsFocusableElement(node);
3980
- }
3981
- });
3982
- }
3983
- }
4497
+ // See if any of them are focusable elemeents
4498
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
3984
4499
 
3985
- const trigger = this.shadowRoot.querySelector('#trigger');
4500
+ // If any of them are focusable elements
4501
+ if (this.triggerContentFocusable) {
3986
4502
 
3987
- if (!this.triggerContentFocusable) {
3988
- trigger.setAttribute('tabindex', '0');
3989
- trigger.setAttribute('role', 'button');
3990
- } else {
3991
- trigger.removeAttribute('tabindex');
3992
- trigger.removeAttribute('role');
4503
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4504
+ this.clearTriggerA11yAttributes(trigger);
4505
+
4506
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4507
+ trigger.removeAttribute('tabindex');
4508
+ } else {
4509
+
4510
+ // Add the tabindex to the trigger so that it's in the focus flow
4511
+ trigger.setAttribute('tabindex', '0');
4512
+ }
4513
+ }
3993
4514
  }
3994
4515
 
3995
4516
  if (event) {
@@ -3999,6 +4520,7 @@ class AuroDropdown extends r {
3999
4520
 
4000
4521
  if (this.triggerContentSlot) {
4001
4522
  this.setupTriggerFocusEventBinding();
4523
+
4002
4524
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
4003
4525
  if (slot.textContent.trim()) {
4004
4526
  return true;
@@ -4066,10 +4588,13 @@ class AuroDropdown extends r {
4066
4588
  id="trigger"
4067
4589
  class="trigger"
4068
4590
  part="trigger"
4069
- aria-labelledby="triggerLabel"
4070
4591
  tabindex="${this.tabIndex}"
4071
4592
  ?showBorder="${this.showTriggerBorders}"
4072
- >
4593
+ role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4594
+ aria-expanded="${o(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4595
+ aria-controls="${o(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4596
+ aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4597
+ >
4073
4598
  <div class="triggerContentWrapper">
4074
4599
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4075
4600
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4103,12 +4628,12 @@ class AuroDropdown extends r {
4103
4628
  <div id="bibSizer" part="size"></div>
4104
4629
  <${this.dropdownBibTag}
4105
4630
  id="bib"
4106
- role="tooltip"
4107
4631
  ?data-show="${this.isPopoverVisible}"
4108
4632
  ?isfullscreen="${this.isBibFullscreen}"
4109
4633
  ?common="${this.common}"
4110
4634
  ?rounded="${this.common || this.rounded}"
4111
- ?inset="${this.common || this.inset}">
4635
+ ?inset="${this.common || this.inset}"
4636
+ >
4112
4637
  </${this.dropdownBibTag}>
4113
4638
  </div>
4114
4639
  `;