@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
@@ -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) {
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(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
+ }
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(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(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);
@@ -3049,7 +3492,6 @@ var tokensCss$1$1 = i$5`:host{--ds-auro-dropdown-label-text-color: var(--ds-basi
3049
3492
 
3050
3493
  const DESIGN_TOKEN_BREAKPOINT_PREFIX = '--ds-grid-breakpoint-';
3051
3494
  const DESIGN_TOKEN_BREAKPOINT_OPTIONS = [
3052
- 'xl',
3053
3495
  'lg',
3054
3496
  'md',
3055
3497
  'sm',
@@ -3121,7 +3563,6 @@ class AuroDropdownBib extends r {
3121
3563
 
3122
3564
  set mobileFullscreenBreakpoint(value) {
3123
3565
  // verify the defined breakpoint is valid and exit out if not
3124
- // 'disabled' is a design token breakpoint so it acts as our "undefined" value
3125
3566
  const validatedValue = DESIGN_TOKEN_BREAKPOINT_OPTIONS.includes(value) ? value : undefined;
3126
3567
  if (!validatedValue) {
3127
3568
  this._mobileBreakpointValue = undefined;
@@ -3390,6 +3831,7 @@ var helpTextVersion = '1.0.0';
3390
3831
  * @csspart helpText - The helpText content container.
3391
3832
  * @event auroDropdown-triggerClick - Notifies that the trigger has been clicked.
3392
3833
  * @event auroDropdown-toggled - Notifies that the visibility of the dropdown bib has changed.
3834
+ * @event auroDropdown-idAdded - Notifies consumers that the unique ID for the dropdown bib has been generated.
3393
3835
  */
3394
3836
  class AuroDropdown extends r {
3395
3837
  constructor() {
@@ -3435,7 +3877,9 @@ class AuroDropdown extends r {
3435
3877
  this.rounded = false;
3436
3878
  this.tabIndex = 0;
3437
3879
  this.noToggle = false;
3880
+ this.a11yAutocomplete = 'none';
3438
3881
  this.labeled = true;
3882
+ this.a11yRole = 'combobox';
3439
3883
  this.onDark = false;
3440
3884
 
3441
3885
  // floaterConfig
@@ -3571,6 +4015,16 @@ class AuroDropdown extends r {
3571
4015
  type: Number
3572
4016
  },
3573
4017
 
4018
+ /**
4019
+ * The unique ID for the dropdown bib element.
4020
+ * @private
4021
+ */
4022
+ dropdownId: {
4023
+ type: String,
4024
+ reflect: false,
4025
+ attribute: false
4026
+ },
4027
+
3574
4028
  /**
3575
4029
  * If declared in combination with `bordered` property or `helpText` slot content, will apply red color to both.
3576
4030
  */
@@ -3634,12 +4088,7 @@ class AuroDropdown extends r {
3634
4088
  },
3635
4089
 
3636
4090
  /**
3637
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
3638
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
3639
- *
3640
- * When expanded, the dropdown will automatically display in fullscreen mode
3641
- * if the screen size is equal to or smaller than the selected breakpoint.
3642
- * @default sm
4091
+ * 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.
3643
4092
  */
3644
4093
  fullscreenBreakpoint: {
3645
4094
  type: String,
@@ -3738,6 +4187,23 @@ class AuroDropdown extends r {
3738
4187
  */
3739
4188
  tabIndex: {
3740
4189
  type: Number
4190
+ },
4191
+
4192
+ /**
4193
+ * The value for the role attribute of the trigger element.
4194
+ */
4195
+ a11yRole: {
4196
+ type: String || undefined,
4197
+ attribute: false,
4198
+ reflect: false
4199
+ },
4200
+
4201
+ /**
4202
+ * The value for the aria-autocomplete attribute of the trigger element.
4203
+ */
4204
+ a11yAutocomplete: {
4205
+ type: String,
4206
+ attribute: false,
3741
4207
  }
3742
4208
  };
3743
4209
  }
@@ -3762,15 +4228,6 @@ class AuroDropdown extends r {
3762
4228
  AuroLibraryRuntimeUtils$1$1.prototype.registerComponent(name, AuroDropdown);
3763
4229
  }
3764
4230
 
3765
- /**
3766
- * Accessor for reusing the focusable entity query string.
3767
- * @private
3768
- * @returns {string}
3769
- */
3770
- get focusableEntityQuery () {
3771
- return 'auro-input, [auro-input], auro-button, [auro-button], button, input';
3772
- }
3773
-
3774
4231
  connectedCallback() {
3775
4232
  super.connectedCallback();
3776
4233
  }
@@ -3784,8 +4241,6 @@ class AuroDropdown extends r {
3784
4241
  updated(changedProperties) {
3785
4242
  this.floater.handleUpdate(changedProperties);
3786
4243
 
3787
- // Note: `disabled` is not a breakpoint (it is not a screen size),
3788
- // so it looks like we never consume this - however, dropdownBib handles this in the setter as "undefined"
3789
4244
  if (changedProperties.has('fullscreenBreakpoint')) {
3790
4245
  this.bibContent.mobileFullscreenBreakpoint = this.fullscreenBreakpoint;
3791
4246
  }
@@ -3799,7 +4254,22 @@ class AuroDropdown extends r {
3799
4254
  }
3800
4255
 
3801
4256
  firstUpdated() {
4257
+
4258
+ // Configure the floater to, this will generate the ID for the bib
3802
4259
  this.floater.configure(this, 'auroDropdown');
4260
+
4261
+ /**
4262
+ * @description Let subscribers know that the dropdown ID ha been generated and added.
4263
+ * @event auroDropdown-idAdded
4264
+ * @type {Object<key: 'id', value: string>} - The ID of the dropdown bib element.
4265
+ */
4266
+ this.dispatchEvent(new CustomEvent('auroDropdown-idAdded', {detail: {id: this.floater.element.id}}));
4267
+
4268
+ // Set the bib ID locally if the user hasn't provided a focusable trigger
4269
+ if (!this.triggerContentFocusable) {
4270
+ this.dropdownId = this.floater.element.id;
4271
+ }
4272
+
3803
4273
  this.bibContent = this.floater.element.bib;
3804
4274
 
3805
4275
  // Add the tag name as an attribute if it is different than the component name
@@ -3921,7 +4391,7 @@ class AuroDropdown extends r {
3921
4391
 
3922
4392
  this.triggerContentSlot.forEach((node) => {
3923
4393
  if (node.querySelectorAll) {
3924
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
4394
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
3925
4395
  auroElements.forEach((auroEl) => {
3926
4396
  auroEl.addEventListener('focus', this.bindFocusEventToTrigger);
3927
4397
  auroEl.addEventListener('blur', this.bindFocusEventToTrigger);
@@ -3942,7 +4412,7 @@ class AuroDropdown extends r {
3942
4412
 
3943
4413
  this.triggerContentSlot.forEach((node) => {
3944
4414
  if (node.querySelectorAll) {
3945
- const auroElements = node.querySelectorAll(this.focusableEntityQuery);
4415
+ const auroElements = node.querySelectorAll('auro-input, [auro-input], auro-button, [auro-button], button, input');
3946
4416
  auroElements.forEach((auroEl) => {
3947
4417
  auroEl.removeEventListener('focus', this.bindFocusEventToTrigger);
3948
4418
  auroEl.removeEventListener('blur', this.bindFocusEventToTrigger);
@@ -3951,6 +4421,30 @@ class AuroDropdown extends r {
3951
4421
  });
3952
4422
  }
3953
4423
 
4424
+ /*
4425
+ * Sets aria attributes for the trigger element if a custom one is passed in.
4426
+ * @private
4427
+ * @method setTriggerAriaAttributes
4428
+ * @param { HTMLElement } triggerElement - The custom trigger element.
4429
+ */
4430
+ clearTriggerA11yAttributes(triggerElement) {
4431
+
4432
+ if (!triggerElement || !triggerElement.removeAttribute) {
4433
+ return;
4434
+ }
4435
+
4436
+ // Reset appropriate attributes for a11y
4437
+ triggerElement.removeAttribute('aria-labelledby');
4438
+ if (triggerElement.getAttribute('id') === `${this.id}-trigger-element`) {
4439
+ triggerElement.removeAttribute('id');
4440
+ }
4441
+ triggerElement.removeAttribute('role');
4442
+ triggerElement.removeAttribute('aria-expanded');
4443
+
4444
+ triggerElement.removeAttribute('aria-controls');
4445
+ triggerElement.removeAttribute('aria-autocomplete');
4446
+ }
4447
+
3954
4448
  /**
3955
4449
  * Handles changes to the trigger content slot and updates related properties.
3956
4450
  *
@@ -3964,32 +4458,41 @@ class AuroDropdown extends r {
3964
4458
  * @returns {void}
3965
4459
  */
3966
4460
  handleTriggerContentSlotChange(event) {
4461
+
3967
4462
  this.floater.handleTriggerTabIndex();
3968
4463
 
4464
+ // Get the trigger
4465
+ const trigger = this.shadowRoot.querySelector('#trigger');
4466
+
4467
+ // Get the trigger slot
3969
4468
  const triggerSlot = this.shadowRoot.querySelector('.triggerContent slot');
3970
4469
 
4470
+ // If there's a trigger slot
3971
4471
  if (triggerSlot) {
3972
4472
 
4473
+ // Get the content nodes to see if there are any children
3973
4474
  const triggerContentNodes = triggerSlot.assignedNodes();
3974
4475
 
4476
+ // If there are children
3975
4477
  if (triggerContentNodes) {
3976
4478
 
3977
- triggerContentNodes.forEach((node) => {
3978
- if (!this.triggerContentFocusable) {
3979
- this.triggerContentFocusable = this.containsFocusableElement(node);
3980
- }
3981
- });
3982
- }
3983
- }
4479
+ // See if any of them are focusable elemeents
4480
+ this.triggerContentFocusable = triggerContentNodes.some((node) => this.containsFocusableElement(node));
3984
4481
 
3985
- const trigger = this.shadowRoot.querySelector('#trigger');
4482
+ // If any of them are focusable elements
4483
+ if (this.triggerContentFocusable) {
3986
4484
 
3987
- if (!this.triggerContentFocusable) {
3988
- trigger.setAttribute('tabindex', '0');
3989
- trigger.setAttribute('role', 'button');
3990
- } else {
3991
- trigger.removeAttribute('tabindex');
3992
- trigger.removeAttribute('role');
4485
+ // Assume the consumer will be providing their own a11y in whatever they passed in
4486
+ this.clearTriggerA11yAttributes(trigger);
4487
+
4488
+ // Remove the tabindex from the trigger so it doesn't interrupt focus flow
4489
+ trigger.removeAttribute('tabindex');
4490
+ } else {
4491
+
4492
+ // Add the tabindex to the trigger so that it's in the focus flow
4493
+ trigger.setAttribute('tabindex', '0');
4494
+ }
4495
+ }
3993
4496
  }
3994
4497
 
3995
4498
  if (event) {
@@ -3999,6 +4502,7 @@ class AuroDropdown extends r {
3999
4502
 
4000
4503
  if (this.triggerContentSlot) {
4001
4504
  this.setupTriggerFocusEventBinding();
4505
+
4002
4506
  this.hasTriggerContent = this.triggerContentSlot.some((slot) => {
4003
4507
  if (slot.textContent.trim()) {
4004
4508
  return true;
@@ -4066,10 +4570,13 @@ class AuroDropdown extends r {
4066
4570
  id="trigger"
4067
4571
  class="trigger"
4068
4572
  part="trigger"
4069
- aria-labelledby="triggerLabel"
4070
4573
  tabindex="${this.tabIndex}"
4071
4574
  ?showBorder="${this.showTriggerBorders}"
4072
- >
4575
+ role="${o(this.triggerContentFocusable ? undefined : this.a11yRole)}"
4576
+ aria-expanded="${o(this.triggerContentFocusable ? undefined : this.isPopoverVisible)}"
4577
+ aria-controls="${o(this.triggerContentFocusable ? undefined : this.dropdownId)}"
4578
+ aria-labelledby="${o(this.triggerContentFocusable ? undefined : 'triggerLabel')}"
4579
+ >
4073
4580
  <div class="triggerContentWrapper">
4074
4581
  <label class="label" id="triggerLabel" hasTrigger=${this.hasTriggerContent}>
4075
4582
  <slot name="label" @slotchange="${this.handleLabelSlotChange}"></slot>
@@ -4103,12 +4610,12 @@ class AuroDropdown extends r {
4103
4610
  <div id="bibSizer" part="size"></div>
4104
4611
  <${this.dropdownBibTag}
4105
4612
  id="bib"
4106
- role="tooltip"
4107
4613
  ?data-show="${this.isPopoverVisible}"
4108
4614
  ?isfullscreen="${this.isBibFullscreen}"
4109
4615
  ?common="${this.common}"
4110
4616
  ?rounded="${this.common || this.rounded}"
4111
- ?inset="${this.common || this.inset}">
4617
+ ?inset="${this.common || this.inset}"
4618
+ >
4112
4619
  </${this.dropdownBibTag}>
4113
4620
  </div>
4114
4621
  `;
@@ -5129,11 +5636,8 @@ class AuroSelect extends r {
5129
5636
  },
5130
5637
 
5131
5638
  /**
5132
- * Defines the screen size breakpoint (`xs`, `sm`, `md`, `lg`, `xl`, `disabled`)
5133
- * at which the dropdown switches to fullscreen mode on mobile. `disabled` indicates a dropdown should _never_ enter fullscreen.
5134
- *
5135
- * When expanded, the dropdown will automatically display in fullscreen mode
5136
- * if the screen size is equal to or smaller than the selected breakpoint.
5639
+ * Defines the screen size breakpoint (`lg`, `md`, `sm`, or `xs`) at which the dropdown switches to fullscreen mode on mobile.
5640
+ * When expanded, the dropdown will automatically display in fullscreen mode if the screen size is equal to or smaller than the selected breakpoint.
5137
5641
  * @default sm
5138
5642
  */
5139
5643
  fullscreenBreakpoint: {