@aurodesignsystem/auro-formkit 2.0.0-beta.15 → 2.0.0-beta.16

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 (136) hide show
  1. package/.turbo/cache/000950e13093845f-meta.json +1 -1
  2. package/.turbo/cache/000950e13093845f.tar.zst +0 -0
  3. package/.turbo/cache/02ee016619902665-meta.json +1 -0
  4. package/.turbo/cache/02ee016619902665.tar.zst +0 -0
  5. package/.turbo/cache/1b97859030a6d60b-meta.json +1 -1
  6. package/.turbo/cache/1e855b2163b7c980-meta.json +1 -0
  7. package/.turbo/cache/1e855b2163b7c980.tar.zst +0 -0
  8. package/.turbo/cache/1f6653e2ed4c0087-meta.json +1 -1
  9. package/.turbo/cache/1f6653e2ed4c0087.tar.zst +0 -0
  10. package/.turbo/cache/2059bc724ac24519-meta.json +1 -1
  11. package/.turbo/cache/2059bc724ac24519.tar.zst +0 -0
  12. package/.turbo/cache/23b3a2fea223679d-meta.json +1 -1
  13. package/.turbo/cache/23b3a2fea223679d.tar.zst +0 -0
  14. package/.turbo/cache/2c0d681132c153dd-meta.json +1 -1
  15. package/.turbo/cache/31bd5687f46c7eba-meta.json +1 -1
  16. package/.turbo/cache/3488cff10dd06acd-meta.json +1 -1
  17. package/.turbo/cache/3488cff10dd06acd.tar.zst +0 -0
  18. package/.turbo/cache/3c8718d2ba6d3fe5-meta.json +1 -0
  19. package/.turbo/cache/3c8718d2ba6d3fe5.tar.zst +0 -0
  20. package/.turbo/cache/4006a206400d5c7b-meta.json +1 -1
  21. package/.turbo/cache/43693504bf6e7c4c-meta.json +1 -1
  22. package/.turbo/cache/45cd32cd38eacbcb-meta.json +1 -1
  23. package/.turbo/cache/492dda333b8d15f1-meta.json +1 -1
  24. package/.turbo/cache/50cd7dcfc9f820c5-meta.json +1 -1
  25. package/.turbo/cache/51eaa58d5c167de8-meta.json +1 -1
  26. package/.turbo/cache/56f2745125fdd552-meta.json +1 -1
  27. package/.turbo/cache/56f2745125fdd552.tar.zst +0 -0
  28. package/.turbo/cache/5f43911cbacf7df1-meta.json +1 -1
  29. package/.turbo/cache/5f43911cbacf7df1.tar.zst +0 -0
  30. package/.turbo/cache/6081837e8943b62e-meta.json +1 -1
  31. package/.turbo/cache/60ad74320c682a2b-meta.json +1 -1
  32. package/.turbo/cache/61e218aba69cff58-meta.json +1 -1
  33. package/.turbo/cache/6951c2a52e5ab5f7-meta.json +1 -1
  34. package/.turbo/cache/6951c2a52e5ab5f7.tar.zst +0 -0
  35. package/.turbo/cache/77da375a012de9d0-meta.json +1 -1
  36. package/.turbo/cache/78418f9089673a21-meta.json +1 -1
  37. package/.turbo/cache/7964d1656e9e702a-meta.json +1 -1
  38. package/.turbo/cache/7bf2b06a479d0b30-meta.json +1 -1
  39. package/.turbo/cache/7c9ca6163e61285c-meta.json +1 -1
  40. package/.turbo/cache/80410f4b5990ab83-meta.json +1 -1
  41. package/.turbo/cache/876b8fa390c8ec81-meta.json +1 -1
  42. package/.turbo/cache/8bb856bd31b5b479-meta.json +1 -1
  43. package/.turbo/cache/8f287fd5d33579ae-meta.json +1 -1
  44. package/.turbo/cache/8f287fd5d33579ae.tar.zst +0 -0
  45. package/.turbo/cache/8f5fef3e04a6fcfa-meta.json +1 -1
  46. package/.turbo/cache/8fcce3ba8357be57-meta.json +1 -0
  47. package/.turbo/cache/8fcce3ba8357be57.tar.zst +0 -0
  48. package/.turbo/cache/9b5868be65819fdf-meta.json +1 -0
  49. package/.turbo/cache/9b5868be65819fdf.tar.zst +0 -0
  50. package/.turbo/cache/b5e6dc7fb9ae1a2f-meta.json +1 -1
  51. package/.turbo/cache/b6a202cc85cb61a0-meta.json +1 -1
  52. package/.turbo/cache/ba270a0ef147f2e5-meta.json +1 -1
  53. package/.turbo/cache/be0b95293ea517cc-meta.json +1 -1
  54. package/.turbo/cache/c03a12ff38ba1e02-meta.json +1 -1
  55. package/.turbo/cache/c1312c6f8c051461-meta.json +1 -1
  56. package/.turbo/cache/c1312c6f8c051461.tar.zst +0 -0
  57. package/.turbo/cache/c3b2cc1b044a4135-meta.json +1 -0
  58. package/.turbo/cache/c3b2cc1b044a4135.tar.zst +0 -0
  59. package/.turbo/cache/c6c6411199b68170-meta.json +1 -1
  60. package/.turbo/cache/c6dbc49c3038946d-meta.json +1 -1
  61. package/.turbo/cache/cae7586c45bed13e-meta.json +1 -1
  62. package/.turbo/cache/cae7586c45bed13e.tar.zst +0 -0
  63. package/.turbo/cache/cf143eb1a55684db-meta.json +1 -1
  64. package/.turbo/cache/cf143eb1a55684db.tar.zst +0 -0
  65. package/.turbo/cache/d5b85352bc667f19-meta.json +1 -1
  66. package/.turbo/cache/d5b85352bc667f19.tar.zst +0 -0
  67. package/.turbo/cache/d5db503b2eaf239c-meta.json +1 -1
  68. package/.turbo/cache/d775555355d6b8fc-meta.json +1 -1
  69. package/.turbo/cache/d7c3007be148d2a1-meta.json +1 -1
  70. package/.turbo/cache/dad3d78b33edd9e4-meta.json +1 -1
  71. package/.turbo/cache/db5e65d819bfe66b-meta.json +1 -1
  72. package/.turbo/cache/dc597b3ea4f61ec8-meta.json +1 -1
  73. package/.turbo/cache/e392fe0927e4af23-meta.json +1 -0
  74. package/.turbo/cache/e392fe0927e4af23.tar.zst +0 -0
  75. package/.turbo/cache/e62cfee068e3ef36-meta.json +1 -1
  76. package/.turbo/cache/e9e36823f6c98f07-meta.json +1 -1
  77. package/.turbo/cache/ea8dd91dba19ddee-meta.json +1 -1
  78. package/.turbo/cache/ea8dd91dba19ddee.tar.zst +0 -0
  79. package/.turbo/cache/f2913bf19939d840-meta.json +1 -1
  80. package/.turbo/cache/f2913bf19939d840.tar.zst +0 -0
  81. package/.turbo/cache/f86b28e5ea2c66fe-meta.json +1 -1
  82. package/CHANGELOG.md +15 -0
  83. package/components/checkbox/.turbo/turbo-build.log +3 -3
  84. package/components/checkbox/.turbo/turbo-bundler.log +3 -3
  85. package/components/checkbox/README.md +1 -1
  86. package/components/combobox/.turbo/turbo-build.log +3 -3
  87. package/components/combobox/README.md +4 -4
  88. package/components/counter/.turbo/turbo-build.log +3 -3
  89. package/components/counter/.turbo/turbo-bundler.log +3 -3
  90. package/components/counter/README.md +1 -1
  91. package/components/datepicker/.turbo/turbo-build.log +3 -3
  92. package/components/datepicker/README.md +4 -4
  93. package/components/dropdown/.turbo/turbo-build.log +3 -3
  94. package/components/dropdown/.turbo/turbo-bundler.log +2 -2
  95. package/components/dropdown/README.md +1 -1
  96. package/components/form/.turbo/turbo-build.log +3 -3
  97. package/components/form/.turbo/turbo-bundler.log +3 -3
  98. package/components/form/README.md +11 -9
  99. package/components/form/demo/api.min.js +187 -22
  100. package/components/form/demo/index.min.js +187 -22
  101. package/components/form/demo/registerDemoDeps.js +5 -0
  102. package/components/form/demo/working.html +86 -0
  103. package/components/form/dist/auro-form.d.ts +83 -2
  104. package/components/form/dist/auro-form.d.ts.map +1 -1
  105. package/components/form/dist/index.js +187 -22
  106. package/components/form/package.json +3 -1
  107. package/components/form/src/auro-form.js +187 -25
  108. package/components/form/src/styles/style-css.js +1 -1
  109. package/components/form/src/styles/style.css +8 -0
  110. package/components/form/src/styles/style.scss +10 -0
  111. package/components/input/.turbo/turbo-build.log +3 -3
  112. package/components/input/.turbo/turbo-bundler.log +3 -3
  113. package/components/input/README.md +1 -1
  114. package/components/menu/.turbo/turbo-build.log +3 -3
  115. package/components/menu/.turbo/turbo-bundler.log +3 -3
  116. package/components/menu/README.md +1 -1
  117. package/components/radio/.turbo/turbo-build.log +3 -3
  118. package/components/radio/.turbo/turbo-bundler.log +3 -3
  119. package/components/radio/README.md +1 -1
  120. package/components/select/.turbo/turbo-build.log +3 -3
  121. package/components/select/README.md +3 -3
  122. package/package.json +1 -1
  123. package/.turbo/cache/07028b4d43bdf4e1-meta.json +0 -1
  124. package/.turbo/cache/07028b4d43bdf4e1.tar.zst +0 -0
  125. package/.turbo/cache/0c8124a987c1cc05-meta.json +0 -1
  126. package/.turbo/cache/0c8124a987c1cc05.tar.zst +0 -0
  127. package/.turbo/cache/2690f6279766d11d-meta.json +0 -1
  128. package/.turbo/cache/2690f6279766d11d.tar.zst +0 -0
  129. package/.turbo/cache/50993de942ec15a9-meta.json +0 -1
  130. package/.turbo/cache/50993de942ec15a9.tar.zst +0 -0
  131. package/.turbo/cache/6830a9e37c9d391f-meta.json +0 -1
  132. package/.turbo/cache/6830a9e37c9d391f.tar.zst +0 -0
  133. package/.turbo/cache/9ae99e8e7bd83d06-meta.json +0 -1
  134. package/.turbo/cache/9ae99e8e7bd83d06.tar.zst +0 -0
  135. package/.turbo/cache/bfefe028012c089a-meta.json +0 -1
  136. package/.turbo/cache/bfefe028012c089a.tar.zst +0 -0
@@ -1,3 +1,13 @@
1
+ /**
2
+ * @typedef {Object} FormStateMember - The form state member.
3
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
4
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
5
+ * @property {boolean} required - Whether the form element is required or not.
6
+ * @property {HTMLElement} element - Whether the form element is required or not.
7
+ */
8
+ /**
9
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
10
+ */
1
11
  /**
2
12
  * The auro-form element provides users a way to ... (it would be great if you fill this out).
3
13
  *
@@ -6,10 +16,12 @@
6
16
  */
7
17
  export class AuroForm extends LitElement {
8
18
  static get properties(): {
9
- cssClass: {
10
- type: StringConstructor;
19
+ _formState: {
20
+ attribute: boolean;
11
21
  };
12
22
  };
23
+ static get formElementTags(): string[];
24
+ static get submitElementTags(): string[];
13
25
  static get styles(): import("lit").CSSResult[];
14
26
  /**
15
27
  * This will register this element with the browser.
@@ -20,7 +32,76 @@ export class AuroForm extends LitElement {
20
32
  *
21
33
  */
22
34
  static register(name?: string): void;
35
+ /** @type {FormState} */
36
+ _formState: FormState;
37
+ /**
38
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
39
+ * @param {string[]} collection - The array to use for tag name search.
40
+ * @param {HTMLElement} element - The element to compare against the master list.
41
+ * @returns boolean
42
+ * @private
43
+ */
44
+ private _isInElementCollection;
45
+ /**
46
+ * Check if the tag name is a form element.
47
+ * @param {HTMLElement} element - The element to check (attr or tag name).
48
+ * @returns {boolean}
49
+ */
50
+ isFormElement(element: HTMLElement): boolean;
51
+ /**
52
+ * Check if the tag name is a submit element.
53
+ * @param {HTMLElement} element - The element to check.
54
+ * @returns {boolean}
55
+ */
56
+ isSubmitElement(element: HTMLElement): boolean;
57
+ /**
58
+ * Reduce the form value into a key-value pair.
59
+ *
60
+ * NOTE: form keys use `name` first, and `id` second if `name` is not available.
61
+ * This follows standard HTML5 form behavior - submission uses `name` by default when creating
62
+ * the FormData object.
63
+ *
64
+ * @returns {Record<string, string | number | boolean | string[] | null>} The form value.
65
+ */
66
+ get value(): Record<string, string | number | boolean | string[] | null>;
67
+ get validity(): "valid" | "invalid";
68
+ get isInitialState(): boolean;
69
+ getSubmitFunction(): (event: any) => void;
70
+ /**
71
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
72
+ * @returns {NodeList}
73
+ */
74
+ queryAuroElements(): NodeList;
75
+ firstUpdated(_changedProperties: any): void;
76
+ onSlotChange(): void;
23
77
  render(): import("lit-html").TemplateResult<1>;
24
78
  }
79
+ /**
80
+ * - The form state member.
81
+ */
82
+ export type FormStateMember = {
83
+ /**
84
+ * - The value of the form element.
85
+ */
86
+ value: string | number | boolean | string[] | null;
87
+ /**
88
+ * - The validity state of the form element, stored when fired from the form element.
89
+ */
90
+ validity: ValidityState;
91
+ /**
92
+ * - Whether the form element is required or not.
93
+ */
94
+ required: boolean;
95
+ /**
96
+ * - Whether the form element is required or not.
97
+ */
98
+ element: HTMLElement;
99
+ };
100
+ /**
101
+ * - The form state.
102
+ */
103
+ export type FormState = {
104
+ [x: string]: FormStateMember;
105
+ };
25
106
  import { LitElement } from "lit";
26
107
  //# sourceMappingURL=auro-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auro-form.d.ts","sourceRoot":"","sources":["../src/auro-form.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAGH;IAQE;;;;MAOC;IAED,+CAEC;IAED;;;;;;;OAOG;IACH,uBANW,MAAM,QAQhB;IAMD,+CAQC;CACF;2BAnEgC,KAAK"}
1
+ {"version":3,"file":"auro-form.d.ts","sourceRoot":"","sources":["../src/auro-form.js"],"names":[],"mappings":"AAcA;;;;;;GAMG;AAEH;;GAEG;AAIH;;;;;GAKG;AAGH;IACE;;;;MAIC;IAWD,uCAOC;IAsBD,yCAKC;IAWD,+CAEC;IAsED;;;;;;;OAOG;IACH,uBANW,MAAM,QAQhB;IArIC,wBAAwB;IACxB,YADW,SAAS,CACA;IActB;;;;;;OAMG;IACH,+BAEC;IAED;;;;OAIG;IACH,uBAHW,WAAW,GACT,OAAO,CAInB;IASD;;;;OAIG;IACH,yBAHW,WAAW,GACT,OAAO,CAInB;IAMD;;;;;;;;OAQG;IACH,aAFa,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAOvE;IAED,oCAYC;IAGD,8BAIC;IAED,0CAYC;IAED;;;OAGG;IACH,qBAFa,QAAQ,CAapB;IAcD,4CAqBC;IAED,qBAkBC;IAGD,+CASC;CACF;;;;;;;;WA3Na,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI;;;;cAC3C,aAAa;;;;cACb,OAAO;;;;aACP,WAAW;;;;;;;;2BAXQ,KAAK"}
@@ -24,7 +24,7 @@ const t=globalThis,i$1=t.trustedTypes,s=i$1?i$1.createPolicy("lit-html",{createH
24
24
  * SPDX-License-Identifier: BSD-3-Clause
25
25
  */class r extends b{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=undefined;}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const s=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=B(s,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return T}}r._$litElement$=true,r["finalized"]=true,globalThis.litElementHydrateSupport?.({LitElement:r});const i=globalThis.litElementPolyfillSupport;i?.({LitElement:r});(globalThis.litElementVersions??=[]).push("4.1.1");
26
26
 
27
- var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}`;
27
+ var styleCss = i$3`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}:host form{display:block;width:100%;padding:1rem;border:1px solid #2a2a2a;border-radius:1rem}`;
28
28
 
29
29
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
30
30
  // See LICENSE in the project root for license information.
@@ -96,8 +96,20 @@ class AuroLibraryRuntimeUtils {
96
96
  }
97
97
  }
98
98
 
99
- // Copyright (c) 2024 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
100
- // See LICENSE in the project root for license information.
99
+ /* eslint-disable no-underscore-dangle */
100
+
101
+
102
+ /**
103
+ * @typedef {Object} FormStateMember - The form state member.
104
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
105
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
106
+ * @property {boolean} required - Whether the form element is required or not.
107
+ * @property {HTMLElement} element - Whether the form element is required or not.
108
+ */
109
+
110
+ /**
111
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
112
+ */
101
113
 
102
114
 
103
115
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
@@ -110,26 +122,138 @@ class AuroLibraryRuntimeUtils {
110
122
 
111
123
  // build the component class
112
124
  class AuroForm extends r {
113
- // constructor() {
114
- // super();
115
- // }
116
-
117
- // This function is to define props used within the scope of this component
118
- // Be sure to review https://lit.dev/docs/components/properties/
119
- // to understand how to use reflected attributes with your property settings.
120
125
  static get properties() {
121
126
  return {
122
- // ...super.properties,
123
-
124
- // this property is DEMO ONLY! Please delete.
125
- cssClass: { type: String }
127
+ _formState: { attribute: false },
126
128
  };
127
129
  }
128
130
 
131
+ constructor() {
132
+ super();
133
+
134
+ /** @type {FormState} */
135
+ this._formState = {};
136
+ }
137
+
138
+ // Note: button is NOT considered a form element in this context
139
+ // as it does not have a .value property.
140
+ static get formElementTags() {
141
+ return [
142
+ 'auro-input',
143
+ 'auro-select',
144
+ 'auro-datepicker',
145
+ 'auro-checkbox-group',
146
+ ];
147
+ }
148
+
149
+ /**
150
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
151
+ * @param {string[]} collection - The array to use for tag name search.
152
+ * @param {HTMLElement} element - The element to compare against the master list.
153
+ * @returns boolean
154
+ * @private
155
+ */
156
+ _isInElementCollection(collection, element) {
157
+ return collection.some((elementTag) => element.tagName.toLowerCase() === elementTag || element.hasAttribute(elementTag.toLowerCase()));
158
+ }
159
+
160
+ /**
161
+ * Check if the tag name is a form element.
162
+ * @param {HTMLElement} element - The element to check (attr or tag name).
163
+ * @returns {boolean}
164
+ */
165
+ isFormElement(element) {
166
+ return this._isInElementCollection(AuroForm.formElementTags, element);
167
+ }
168
+
169
+ static get submitElementTags() {
170
+ return [
171
+ 'button',
172
+ 'auro-button',
173
+ ];
174
+ }
175
+
176
+ /**
177
+ * Check if the tag name is a submit element.
178
+ * @param {HTMLElement} element - The element to check.
179
+ * @returns {boolean}
180
+ */
181
+ isSubmitElement(element) {
182
+ return this._isInElementCollection(AuroForm.submitElementTags, element);
183
+ }
184
+
129
185
  static get styles() {
130
186
  return [styleCss];
131
187
  }
132
188
 
189
+ /**
190
+ * Reduce the form value into a key-value pair.
191
+ *
192
+ * NOTE: form keys use `name` first, and `id` second if `name` is not available.
193
+ * This follows standard HTML5 form behavior - submission uses `name` by default when creating
194
+ * the FormData object.
195
+ *
196
+ * @returns {Record<string, string | number | boolean | string[] | null>} The form value.
197
+ */
198
+ get value() {
199
+ return Object.keys(this._formState).reduce((acc, key) => {
200
+ acc[key] = this._formState[key].value;
201
+ return acc;
202
+ }, {});
203
+ }
204
+
205
+ get validity() {
206
+ // go through validity states and return the first invalid state (if any)
207
+ const invalidKey = Object.keys(this._formState).
208
+ find((key) => {
209
+ const formKey = this._formState[key];
210
+
211
+ // these are NOT extra :(
212
+ // eslint-disable-next-line no-extra-parens
213
+ return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
214
+ });
215
+
216
+ return invalidKey ? 'invalid' : 'valid';
217
+ }
218
+
219
+ // Below is not implemented yet
220
+ get isInitialState() {
221
+ const anyTainted = Object.keys(this._formState).some((key) => this._formState[key].validity !== null);
222
+
223
+ return !anyTainted;
224
+ }
225
+
226
+ getSubmitFunction() {
227
+ // We return an arrow function here to ensure that the `this` context points at this same AuroForm context.
228
+ // Otherwise, submission tries to read `this.value` on the button element.
229
+ return (event) => {
230
+ event.preventDefault();
231
+
232
+ this.dispatchEvent(new CustomEvent('submit', {
233
+ detail: {
234
+ value: this.value
235
+ }
236
+ }));
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
242
+ * @returns {NodeList}
243
+ */
244
+ queryAuroElements() {
245
+ const formElementQuery = AuroForm.formElementTags.map((tag) => `${tag}[name]`).join(',');
246
+ const submitterQuery = AuroForm.submitElementTags.map((tag) => `${tag}[type=submit]`).join(',');
247
+
248
+ // Alternatively, for renamed components...
249
+ const renamedFormElementQuery = AuroForm.formElementTags.map((tag) => `[${tag}][name]`).join(',');
250
+ const renamedSubmitterQuery = AuroForm.formElementTags.map((tag) => `[${tag}][type=submit]`).join(',');
251
+
252
+ const unifiedElementQuery = `${formElementQuery},${submitterQuery},${renamedFormElementQuery},${renamedSubmitterQuery}`;
253
+
254
+ return this.querySelectorAll(unifiedElementQuery);
255
+ }
256
+
133
257
  /**
134
258
  * This will register this element with the browser.
135
259
  * @param {string} [name="auro-form"] - The name of element that you want to register to.
@@ -142,17 +266,58 @@ class AuroForm extends r {
142
266
  AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroForm);
143
267
  }
144
268
 
145
- // When using auroElement, use the following attribute and function when hiding content from screen readers.
146
- // aria-hidden="${this.hideAudible(this.hiddenAudible)}"
269
+ firstUpdated(_changedProperties) {
270
+ super.firstUpdated(_changedProperties);
271
+
272
+ const slot = this.shadowRoot.querySelector('slot');
273
+
274
+ // Update the form state when a form element is detected
275
+ slot.addEventListener('input', (event) => {
276
+
277
+ /** @type {HTMLInputElement} */
278
+ const eventTarget = event.target;
279
+ if (this.isFormElement(eventTarget)) {
280
+ this._formState[eventTarget.getAttribute("name")].value = eventTarget.value;
281
+ }
282
+ });
283
+
284
+ slot.addEventListener('auroFormElement-validated', (event) => {
285
+ const oldValue = this._formState;
286
+
287
+ this._formState[event.target.getAttribute("name")].validity = event.detail.validity;
288
+ this.requestUpdate('formState', oldValue);
289
+ });
290
+ }
291
+
292
+ onSlotChange() {
293
+ this._formState = {};
147
294
 
148
- // function that renders the HTML and CSS into the scope of the component
295
+ this.queryAuroElements().forEach((element) => {
296
+ if (this.isFormElement(element)) {
297
+ this._formState[element.getAttribute('name')] = {
298
+ value: element.getAttribute('value'),
299
+ validity: element.getAttribute('validity'),
300
+ required: element.hasAttribute('required'),
301
+ element
302
+ };
303
+ }
304
+
305
+ if (this.isSubmitElement(element) && element.getAttribute('type') === 'submit') {
306
+ element.removeEventListener('click', this.getSubmitFunction());
307
+ element.addEventListener('click', this.getSubmitFunction());
308
+ }
309
+ });
310
+ }
311
+
312
+ // function that renders the HTML and CSS into the scope of the component
149
313
  render() {
150
314
  return x`
151
-
152
- <!-- this is demo code, DO NOT USE IN YOUR ELEMENT -->
153
- <div class=${this.cssClass} tabindex="0">
154
- <slot></slot>
155
- </div>
315
+ <form>
316
+ <p>Value: ${JSON.stringify(this.value)}</p>
317
+ <p>Validity: ${this.validity}</p>
318
+ <h3>Auro form example</h3>
319
+ <slot @slotchange="${this.onSlotChange}"></slot>
320
+ </form>
156
321
  `;
157
322
  }
158
323
  }
@@ -26,6 +26,8 @@
26
26
  "@auro-formkit/build-tools": "*",
27
27
  "@auro-formkit/config": "*",
28
28
  "@auro-formkit/typescript": "*",
29
+ "@aurodesignsystem/auro-datepicker": "*",
30
+ "@aurodesignsystem/auro-input": "*",
29
31
  "@aurodesignsystem/design-tokens": "^4.12.1",
30
32
  "@aurodesignsystem/webcorestylesheets": "^5.1.2",
31
33
  "@rollup/plugin-node-resolve": "^15.3.0",
@@ -58,7 +60,7 @@
58
60
  "bundler": "rollup -c node:@auro-formkit/config/rollup",
59
61
  "build:docs": "wca analyze 'scripts/wca/*.js' --outFiles docs/api.md; node ../../packages/build-tools/src/docProcessor.mjs --component form",
60
62
  "clean": "rm -rf dist build",
61
- "dev": "web-dev-server",
63
+ "dev": "web-dev-server --node-resolve",
62
64
  "postCss:component": "node ../../node_modules/@aurodesignsystem/auro-library/scripts/build/postCss.mjs",
63
65
  "sass:render": "sass-render --load-path=../../node_modules 'src/**/*.css' -t ../../node_modules/@aurodesignsystem/auro-library/scripts/build/staticStyles-template.js",
64
66
  "serve": "web-dev-server",
@@ -1,3 +1,5 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+
1
3
  // Copyright (c) 2024 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
2
4
  // See LICENSE in the project root for license information.
3
5
 
@@ -6,16 +8,23 @@
6
8
  // If using litElement base class
7
9
  import { LitElement, html } from "lit";
8
10
 
9
- // If using auroElement base class
10
- // See instructions for importing auroElement base class https://git.io/JULq4
11
- // import { LitElement, html } from "lit";
12
- // import AuroElement from '@aurodesignsystem/webcorestylesheets/dist/auroElement/auroElement';
13
-
14
- // Import touch detection lib
15
11
  import styleCss from "./styles/style-css.js";
16
12
 
17
13
  import AuroLibraryRuntimeUtils from '@aurodesignsystem/auro-library/scripts/utils/runtimeUtils.mjs';
18
14
 
15
+ /**
16
+ * @typedef {Object} FormStateMember - The form state member.
17
+ * @property {string | number | boolean | string[] | null} value - The value of the form element.
18
+ * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
19
+ * @property {boolean} required - Whether the form element is required or not.
20
+ * @property {HTMLElement} element - Whether the form element is required or not.
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object.<string, FormStateMember>} FormState - The form state.
25
+ */
26
+
27
+
19
28
  // See https://git.io/JJ6SJ for "How to document your components using JSDoc"
20
29
  /**
21
30
  * The auro-form element provides users a way to ... (it would be great if you fill this out).
@@ -26,26 +35,138 @@ import AuroLibraryRuntimeUtils from '@aurodesignsystem/auro-library/scripts/util
26
35
 
27
36
  // build the component class
28
37
  export class AuroForm extends LitElement {
29
- // constructor() {
30
- // super();
31
- // }
32
-
33
- // This function is to define props used within the scope of this component
34
- // Be sure to review https://lit.dev/docs/components/properties/
35
- // to understand how to use reflected attributes with your property settings.
36
38
  static get properties() {
37
39
  return {
38
- // ...super.properties,
39
-
40
- // this property is DEMO ONLY! Please delete.
41
- cssClass: { type: String }
40
+ _formState: { attribute: false },
42
41
  };
43
42
  }
44
43
 
44
+ constructor() {
45
+ super();
46
+
47
+ /** @type {FormState} */
48
+ this._formState = {};
49
+ }
50
+
51
+ // Note: button is NOT considered a form element in this context
52
+ // as it does not have a .value property.
53
+ static get formElementTags() {
54
+ return [
55
+ 'auro-input',
56
+ 'auro-select',
57
+ 'auro-datepicker',
58
+ 'auro-checkbox-group',
59
+ ];
60
+ }
61
+
62
+ /**
63
+ * Shared code for determining if an element is something we care about (submit, form element, etc.).
64
+ * @param {string[]} collection - The array to use for tag name search.
65
+ * @param {HTMLElement} element - The element to compare against the master list.
66
+ * @returns boolean
67
+ * @private
68
+ */
69
+ _isInElementCollection(collection, element) {
70
+ return collection.some((elementTag) => element.tagName.toLowerCase() === elementTag || element.hasAttribute(elementTag.toLowerCase()));
71
+ }
72
+
73
+ /**
74
+ * Check if the tag name is a form element.
75
+ * @param {HTMLElement} element - The element to check (attr or tag name).
76
+ * @returns {boolean}
77
+ */
78
+ isFormElement(element) {
79
+ return this._isInElementCollection(AuroForm.formElementTags, element);
80
+ }
81
+
82
+ static get submitElementTags() {
83
+ return [
84
+ 'button',
85
+ 'auro-button',
86
+ ];
87
+ }
88
+
89
+ /**
90
+ * Check if the tag name is a submit element.
91
+ * @param {HTMLElement} element - The element to check.
92
+ * @returns {boolean}
93
+ */
94
+ isSubmitElement(element) {
95
+ return this._isInElementCollection(AuroForm.submitElementTags, element);
96
+ }
97
+
45
98
  static get styles() {
46
99
  return [styleCss];
47
100
  }
48
101
 
102
+ /**
103
+ * Reduce the form value into a key-value pair.
104
+ *
105
+ * NOTE: form keys use `name` first, and `id` second if `name` is not available.
106
+ * This follows standard HTML5 form behavior - submission uses `name` by default when creating
107
+ * the FormData object.
108
+ *
109
+ * @returns {Record<string, string | number | boolean | string[] | null>} The form value.
110
+ */
111
+ get value() {
112
+ return Object.keys(this._formState).reduce((acc, key) => {
113
+ acc[key] = this._formState[key].value;
114
+ return acc;
115
+ }, {});
116
+ }
117
+
118
+ get validity() {
119
+ // go through validity states and return the first invalid state (if any)
120
+ const invalidKey = Object.keys(this._formState).
121
+ find((key) => {
122
+ const formKey = this._formState[key];
123
+
124
+ // these are NOT extra :(
125
+ // eslint-disable-next-line no-extra-parens
126
+ return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
127
+ });
128
+
129
+ return invalidKey ? 'invalid' : 'valid';
130
+ }
131
+
132
+ // Below is not implemented yet
133
+ get isInitialState() {
134
+ const anyTainted = Object.keys(this._formState).some((key) => this._formState[key].validity !== null);
135
+
136
+ return !anyTainted;
137
+ }
138
+
139
+ getSubmitFunction() {
140
+ // We return an arrow function here to ensure that the `this` context points at this same AuroForm context.
141
+ // Otherwise, submission tries to read `this.value` on the button element.
142
+ return (event) => {
143
+ event.preventDefault();
144
+
145
+ this.dispatchEvent(new CustomEvent('submit', {
146
+ detail: {
147
+ value: this.value
148
+ }
149
+ }));
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Construct the query strings from elements, append them together, execute, and return the NodeList.
155
+ * @returns {NodeList}
156
+ */
157
+ queryAuroElements() {
158
+ const formElementQuery = AuroForm.formElementTags.map((tag) => `${tag}[name]`).join(',');
159
+ const submitterQuery = AuroForm.submitElementTags.map((tag) => `${tag}[type=submit]`).join(',');
160
+
161
+ // Alternatively, for renamed components...
162
+ const renamedFormElementQuery = AuroForm.formElementTags.map((tag) => `[${tag}][name]`).join(',');
163
+ const renamedSubmitterQuery = AuroForm.formElementTags.map((tag) => `[${tag}][type=submit]`).join(',');
164
+
165
+ const unifiedElementQuery = `${formElementQuery},${submitterQuery},${renamedFormElementQuery},${renamedSubmitterQuery}`;
166
+
167
+ return this.querySelectorAll(unifiedElementQuery);
168
+ }
169
+
49
170
  /**
50
171
  * This will register this element with the browser.
51
172
  * @param {string} [name="auro-form"] - The name of element that you want to register to.
@@ -58,17 +179,58 @@ export class AuroForm extends LitElement {
58
179
  AuroLibraryRuntimeUtils.prototype.registerComponent(name, AuroForm);
59
180
  }
60
181
 
61
- // When using auroElement, use the following attribute and function when hiding content from screen readers.
62
- // aria-hidden="${this.hideAudible(this.hiddenAudible)}"
182
+ firstUpdated(_changedProperties) {
183
+ super.firstUpdated(_changedProperties);
184
+
185
+ const slot = this.shadowRoot.querySelector('slot');
186
+
187
+ // Update the form state when a form element is detected
188
+ slot.addEventListener('input', (event) => {
189
+
190
+ /** @type {HTMLInputElement} */
191
+ const eventTarget = event.target;
192
+ if (this.isFormElement(eventTarget)) {
193
+ this._formState[eventTarget.getAttribute("name")].value = eventTarget.value;
194
+ }
195
+ });
196
+
197
+ slot.addEventListener('auroFormElement-validated', (event) => {
198
+ const oldValue = this._formState;
199
+
200
+ this._formState[event.target.getAttribute("name")].validity = event.detail.validity;
201
+ this.requestUpdate('formState', oldValue);
202
+ });
203
+ }
204
+
205
+ onSlotChange() {
206
+ this._formState = {};
63
207
 
64
- // function that renders the HTML and CSS into the scope of the component
208
+ this.queryAuroElements().forEach((element) => {
209
+ if (this.isFormElement(element)) {
210
+ this._formState[element.getAttribute('name')] = {
211
+ value: element.getAttribute('value'),
212
+ validity: element.getAttribute('validity'),
213
+ required: element.hasAttribute('required'),
214
+ element
215
+ };
216
+ }
217
+
218
+ if (this.isSubmitElement(element) && element.getAttribute('type') === 'submit') {
219
+ element.removeEventListener('click', this.getSubmitFunction());
220
+ element.addEventListener('click', this.getSubmitFunction());
221
+ }
222
+ });
223
+ }
224
+
225
+ // function that renders the HTML and CSS into the scope of the component
65
226
  render() {
66
227
  return html`
67
-
68
- <!-- this is demo code, DO NOT USE IN YOUR ELEMENT -->
69
- <div class=${this.cssClass} tabindex="0">
70
- <slot></slot>
71
- </div>
228
+ <form>
229
+ <p>Value: ${JSON.stringify(this.value)}</p>
230
+ <p>Validity: ${this.validity}</p>
231
+ <h3>Auro form example</h3>
232
+ <slot @slotchange="${this.onSlotChange}"></slot>
233
+ </form>
72
234
  `;
73
235
  }
74
236
  }
@@ -1,2 +1,2 @@
1
1
  import { css } from 'lit';
2
- export default css`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}`;
2
+ export default css`*,*:before,*:after{box-sizing:border-box}@media(prefers-reduced-motion: reduce){*,*:before,*:after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}}*:focus-visible{outline:0}*:focus-visible{outline:0}:focus:not(:focus-visible){outline:3px solid transparent}.testClass{display:inline-block;padding:var(--auro-text-body-size-default);border:1px solid var(--auro-color-border-error-on-light);color:var(--auro-color-border-error-on-light)}:focus-visible{background-color:var(--auro-color-border-error-on-light);color:var(--auro-color-base-white)}:host form{display:block;width:100%;padding:1rem;border:1px solid #2a2a2a;border-radius:1rem}`;
@@ -34,3 +34,11 @@
34
34
  background-color: var(--auro-color-border-error-on-light);
35
35
  color: var(--auro-color-base-white);
36
36
  }
37
+
38
+ :host form {
39
+ display: block;
40
+ width: 100%;
41
+ padding: 1rem;
42
+ border: 1px solid #2a2a2a;
43
+ border-radius: 1rem;
44
+ }
@@ -35,3 +35,13 @@
35
35
  background-color: var(--auro-color-border-error-on-light);
36
36
  color: var(--auro-color-base-white);
37
37
  }
38
+
39
+ :host {
40
+ form {
41
+ display: block;
42
+ width: 100%;
43
+ padding: 1rem;
44
+ border: 1px solid #2a2a2a;
45
+ border-radius: 1rem;
46
+ }
47
+ }
@@ -216,13 +216,13 @@ More info and automated migrator: https://sass-lang.com/d/import
216
216
 
217
217
  
218
218
  ./src/index.js → dist...
219
- created dist in 1.6s
219
+ created dist in 1.3s
220
220
  
221
221
  ./demo/index.js → ./demo/...
222
- created ./demo/ in 997ms
222
+ created ./demo/ in 698ms
223
223
  
224
224
  ./demo/api.js → ./demo/...
225
- created ./demo/ in 895ms
225
+ created ./demo/ in 910ms
226
226
 
227
227
  > @aurodesignsystem/auro-input@4.2.0 build:docs
228
228
  > wca analyze 'scripts/wca/*.js' --outFiles docs/api.md; node ../../packages/build-tools/src/docProcessor.mjs --component input