@dile/ui 3.4.4 → 3.4.5

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 (23) hide show
  1. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--does-not-leak-a-stale-value-when-the-form-is-submitted-again-without-a-button-click-1.png +0 -0
  2. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--includes-name-value-in-the-formdata-event-entry-list-when-clicked-1.png +0 -0
  3. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--includes-name-value-when-read-synchronously-inside-the-submit-listener-1.png +0 -0
  4. package/components/button/__screenshots__/button.spec.js/dile-button-Native-form-submission--DEBUG---timing-probe--only-includes-the-clicked-button-among-two-submit-buttons-with-different-values-1.png +0 -0
  5. package/components/button/button.spec.js +134 -0
  6. package/components/button/src/DileButton.js +8 -2
  7. package/components/checkbox/checkbox.spec.js +235 -0
  8. package/components/checkbox/src/DileCheckbox.js +27 -15
  9. package/components/color-picker/color-picker.spec.js +16 -0
  10. package/components/color-picker/src/DileColorPicker.js +1 -1
  11. package/components/drop-file/src/DileDropFile.js +1 -1
  12. package/components/input/src/DileInput.js +1 -1
  13. package/components/number-picker/src/DileNumberPicker.js +1 -1
  14. package/components/otp-input/src/DileOtpInput.js +1 -1
  15. package/components/radio-group/src/DileRadioGroup.js +1 -1
  16. package/components/range/src/DileRange.js +1 -1
  17. package/components/select/select.spec.js +39 -0
  18. package/components/select/src/DileSelect.js +1 -1
  19. package/components/select/src/DileSelectAjax.js +1 -1
  20. package/components/select/src/DileSelectBoolean.js +1 -1
  21. package/components/textarea/src/DileTextarea.js +1 -1
  22. package/components/time-picker/src/DileTimePicker.js +1 -1
  23. package/package.json +2 -2
@@ -45,4 +45,138 @@ describe('dile-button', () => {
45
45
 
46
46
  expect(el.shadowRoot.querySelector('dile-spinner-icon')).toBeTruthy();
47
47
  });
48
+
49
+ describe('Native form submission', () => {
50
+ it('includes name/value in the FormData when clicked', async () => {
51
+ document.body.innerHTML = `
52
+ <form id="testForm">
53
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
54
+ </form>
55
+ `;
56
+ const form = document.getElementById('testForm');
57
+ const el = document.body.querySelector('dile-button');
58
+ await el.updateComplete;
59
+
60
+ let capturedFormData = null;
61
+ form.addEventListener('submit', (e) => {
62
+ e.preventDefault();
63
+ capturedFormData = new FormData(form);
64
+ });
65
+
66
+ el.shadowRoot.querySelector('button').click();
67
+
68
+ expect(capturedFormData.get('action')).toBe('save');
69
+ });
70
+
71
+ it('does not submit a name/value pair when the button has no value', async () => {
72
+ document.body.innerHTML = `
73
+ <form id="testForm">
74
+ <dile-button type="submit" name="action">Save</dile-button>
75
+ </form>
76
+ `;
77
+ const form = document.getElementById('testForm');
78
+ const el = document.body.querySelector('dile-button');
79
+ await el.updateComplete;
80
+
81
+ let capturedFormData = null;
82
+ form.addEventListener('submit', (e) => {
83
+ e.preventDefault();
84
+ capturedFormData = new FormData(form);
85
+ });
86
+
87
+ el.shadowRoot.querySelector('button').click();
88
+
89
+ expect(capturedFormData.has('action')).toBe(false);
90
+ });
91
+
92
+ it('participates in FormData when name is set as a JS property', async () => {
93
+ document.body.innerHTML = `
94
+ <form id="testForm">
95
+ <dile-button type="submit" value="save">Save</dile-button>
96
+ </form>
97
+ `;
98
+ const form = document.getElementById('testForm');
99
+ const el = document.body.querySelector('dile-button');
100
+ el.name = 'action';
101
+ await el.updateComplete;
102
+
103
+ expect(el.getAttribute('name')).toBe('action');
104
+
105
+ let capturedFormData = null;
106
+ form.addEventListener('submit', (e) => {
107
+ e.preventDefault();
108
+ capturedFormData = new FormData(form);
109
+ });
110
+
111
+ el.shadowRoot.querySelector('button').click();
112
+
113
+ expect(capturedFormData.get('action')).toBe('save');
114
+ });
115
+
116
+ it('only includes the clicked button among two submit buttons with different values', async () => {
117
+ document.body.innerHTML = `
118
+ <form id="testForm">
119
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
120
+ <dile-button type="submit" name="action" value="delete">Delete</dile-button>
121
+ </form>
122
+ `;
123
+ const form = document.getElementById('testForm');
124
+ const buttons = document.body.querySelectorAll('dile-button');
125
+ for (const b of buttons) await b.updateComplete;
126
+
127
+ let capturedFormData = null;
128
+ form.addEventListener('submit', (e) => {
129
+ e.preventDefault();
130
+ capturedFormData = new FormData(form);
131
+ });
132
+
133
+ buttons[1].shadowRoot.querySelector('button').click();
134
+
135
+ expect(capturedFormData.getAll('action')).toEqual(['delete']);
136
+ });
137
+
138
+ it('does not leak a stale value when the form is submitted again without a button click', async () => {
139
+ document.body.innerHTML = `
140
+ <form id="testForm">
141
+ <input name="text" value="hello" />
142
+ <dile-button type="submit" name="action" value="save">Save</dile-button>
143
+ </form>
144
+ `;
145
+ const form = document.getElementById('testForm');
146
+ const el = document.body.querySelector('dile-button');
147
+ await el.updateComplete;
148
+
149
+ let capturedFormData = null;
150
+ form.addEventListener('submit', (e) => {
151
+ e.preventDefault();
152
+ capturedFormData = new FormData(form);
153
+ });
154
+
155
+ // First submit via the button click - action=save should be present.
156
+ el.shadowRoot.querySelector('button').click();
157
+ expect(capturedFormData.get('action')).toBe('save');
158
+
159
+ // Second submit programmatically, with no submitter clicked - action should NOT leak.
160
+ form.requestSubmit();
161
+ expect(capturedFormData.get('action')).toBeNull();
162
+ });
163
+
164
+ it('does not submit when the button is disabled', async () => {
165
+ document.body.innerHTML = `
166
+ <form id="testForm">
167
+ <dile-button type="submit" name="action" value="save" disabled>Save</dile-button>
168
+ </form>
169
+ `;
170
+ const form = document.getElementById('testForm');
171
+ const el = document.body.querySelector('dile-button');
172
+ await el.updateComplete;
173
+
174
+ let submitted = false;
175
+ form.addEventListener('submit', (e) => { e.preventDefault(); submitted = true; });
176
+
177
+ el.shadowRoot.querySelector('button').click();
178
+
179
+ expect(submitted).toBe(false);
180
+ });
181
+ });
48
182
  });
@@ -9,8 +9,9 @@ export class DileButton extends LitElement {
9
9
  return {
10
10
  disabled: { type: Boolean },
11
11
  loading: { type: Boolean, reflect: true },
12
- name: { type: String },
12
+ name: { type: String, reflect: true },
13
13
  type: { type: String },
14
+ value: { type: String },
14
15
  };
15
16
  }
16
17
 
@@ -134,7 +135,12 @@ export class DileButton extends LitElement {
134
135
  }
135
136
 
136
137
  if (this.type === "submit") {
137
- this._internals.form?.requestSubmit();
138
+ const form = this._internals.form;
139
+ if (form) {
140
+ this._internals.setFormValue(this.value ?? null);
141
+ form.requestSubmit();
142
+ this._internals.setFormValue(null);
143
+ }
138
144
  } else if (this.type === "reset") {
139
145
  this._internals.form?.reset();
140
146
  }
@@ -0,0 +1,235 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './checkbox.js';
3
+
4
+ describe('dile-checkbox', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderCheckbox(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-checkbox');
12
+ await el.updateComplete;
13
+ return el;
14
+ }
15
+
16
+ function getControl(el) {
17
+ return el.shadowRoot.querySelector('[role="checkbox"]');
18
+ }
19
+
20
+ describe('Rendering', () => {
21
+ it('starts unchecked by default', async () => {
22
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
23
+ expect(el.checked).toBe(false);
24
+ expect(getControl(el).getAttribute('aria-checked')).toBe('false');
25
+ });
26
+
27
+ it('renders the slotted label content', async () => {
28
+ const el = await renderCheckbox('<dile-checkbox name="terms">Accept terms</dile-checkbox>');
29
+ const label = el.shadowRoot.querySelector('label.label');
30
+ expect(label).toBeTruthy();
31
+ expect(el.textContent.trim()).toBe('Accept terms');
32
+ });
33
+
34
+ it('does not render a label element when there is no slotted content', async () => {
35
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
36
+ expect(el.shadowRoot.querySelector('label.label')).toBeNull();
37
+ });
38
+
39
+ it('renders a message when the message property is set', async () => {
40
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
41
+ el.message = 'You must accept';
42
+ el.errored = true;
43
+ await el.updateComplete;
44
+
45
+ const message = el.shadowRoot.querySelector('dile-input-message');
46
+ expect(message).toBeTruthy();
47
+ expect(message.getAttribute('message')).toBe('You must accept');
48
+ });
49
+ });
50
+
51
+ describe('Toggling', () => {
52
+ it('toggles checked state on click', async () => {
53
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
54
+ el.shadowRoot.querySelector('div').click();
55
+ await el.updateComplete;
56
+
57
+ expect(el.checked).toBe(true);
58
+ });
59
+
60
+ it('toggles checked state on Space key press', async () => {
61
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
62
+ const control = getControl(el);
63
+ control.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true, cancelable: true }));
64
+ await el.updateComplete;
65
+
66
+ expect(el.checked).toBe(true);
67
+ });
68
+
69
+ it('does not toggle when disabled', async () => {
70
+ const el = await renderCheckbox('<dile-checkbox name="terms" disabled></dile-checkbox>');
71
+ el.shadowRoot.querySelector('div').click();
72
+ await el.updateComplete;
73
+
74
+ expect(el.checked).toBe(false);
75
+ });
76
+
77
+ it('clears the checked state via clear()', async () => {
78
+ const el = await renderCheckbox('<dile-checkbox name="terms" checked></dile-checkbox>');
79
+ expect(el.checked).toBe(true);
80
+
81
+ el.clear();
82
+ await el.updateComplete;
83
+
84
+ expect(el.checked).toBe(false);
85
+ });
86
+
87
+ it('mirrors checked through the value property', async () => {
88
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
89
+ el.value = true;
90
+ await el.updateComplete;
91
+
92
+ expect(el.checked).toBe(true);
93
+ expect(el.value).toBe(true);
94
+ });
95
+ });
96
+
97
+ describe('Events', () => {
98
+ it('dispatches dile-checkbox-changed with checked and name', async () => {
99
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
100
+ let detail = null;
101
+ el.addEventListener('dile-checkbox-changed', (e) => { detail = e.detail; });
102
+
103
+ el.shadowRoot.querySelector('div').click();
104
+ await el.updateComplete;
105
+
106
+ expect(detail).toEqual({ checked: true, name: 'terms' });
107
+ });
108
+
109
+ it('dispatches element-changed with name and value', async () => {
110
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
111
+ let detail = null;
112
+ el.addEventListener('element-changed', (e) => { detail = e.detail; });
113
+
114
+ el.shadowRoot.querySelector('div').click();
115
+ await el.updateComplete;
116
+
117
+ expect(detail).toEqual({ name: 'terms', value: true });
118
+ });
119
+
120
+ it('clears the error state on toggle when hideErrorOnInput is set', async () => {
121
+ const el = await renderCheckbox('<dile-checkbox name="terms" hideErrorOnInput></dile-checkbox>');
122
+ el.message = 'Required';
123
+ el.errored = true;
124
+ await el.updateComplete;
125
+
126
+ el.shadowRoot.querySelector('div').click();
127
+ await el.updateComplete;
128
+
129
+ expect(el.message).toBe('');
130
+ expect(el.errored).toBe(false);
131
+ });
132
+ });
133
+
134
+ describe('Native form submission', () => {
135
+ it('participates in FormData when name is set as an attribute', async () => {
136
+ document.body.innerHTML = `
137
+ <form id="testForm">
138
+ <dile-checkbox name="terms" checked></dile-checkbox>
139
+ </form>
140
+ `;
141
+ const el = document.body.querySelector('dile-checkbox');
142
+ await el.updateComplete;
143
+
144
+ const data = new FormData(document.getElementById('testForm'));
145
+ expect(data.get('terms')).toBe('true');
146
+ });
147
+
148
+ it('participates in FormData when name is set as a JS property', async () => {
149
+ document.body.innerHTML = `
150
+ <form id="testForm">
151
+ <dile-checkbox checked></dile-checkbox>
152
+ </form>
153
+ `;
154
+ const el = document.body.querySelector('dile-checkbox');
155
+ el.name = 'terms';
156
+ await el.updateComplete;
157
+
158
+ expect(el.getAttribute('name')).toBe('terms');
159
+
160
+ const data = new FormData(document.getElementById('testForm'));
161
+ expect(data.get('terms')).toBe('true');
162
+ });
163
+
164
+ it('is excluded from FormData when unchecked', async () => {
165
+ document.body.innerHTML = `
166
+ <form id="testForm">
167
+ <dile-checkbox name="terms"></dile-checkbox>
168
+ </form>
169
+ `;
170
+ const el = document.body.querySelector('dile-checkbox');
171
+ await el.updateComplete;
172
+
173
+ const data = new FormData(document.getElementById('testForm'));
174
+ expect(data.get('terms')).toBeNull();
175
+ });
176
+ });
177
+
178
+ describe('Accessibility', () => {
179
+ it('exposes role="checkbox" on the interactive control', async () => {
180
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
181
+ expect(getControl(el)).toBeTruthy();
182
+ });
183
+
184
+ it('keeps aria-checked in sync with the checked state', async () => {
185
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
186
+ const control = getControl(el);
187
+ expect(control.getAttribute('aria-checked')).toBe('false');
188
+
189
+ el.checked = true;
190
+ await el.updateComplete;
191
+
192
+ expect(control.getAttribute('aria-checked')).toBe('true');
193
+ });
194
+
195
+ it('is keyboard focusable when enabled', async () => {
196
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
197
+ const control = getControl(el);
198
+ expect(control.getAttribute('tabindex')).toBe('0');
199
+ });
200
+
201
+ it('is removed from the tab order and marked aria-disabled when disabled', async () => {
202
+ const el = await renderCheckbox('<dile-checkbox name="terms" disabled></dile-checkbox>');
203
+ const control = getControl(el);
204
+ expect(control.getAttribute('tabindex')).toBe('-1');
205
+ expect(control.getAttribute('aria-disabled')).toBe('true');
206
+ });
207
+
208
+ it('links the control to the slotted label via aria-labelledby', async () => {
209
+ const el = await renderCheckbox('<dile-checkbox name="terms">Accept terms</dile-checkbox>');
210
+ const control = getControl(el);
211
+ const labelledbyId = control.getAttribute('aria-labelledby');
212
+
213
+ expect(labelledbyId).toBeTruthy();
214
+ expect(el.shadowRoot.getElementById(labelledbyId)).toBeTruthy();
215
+ });
216
+
217
+ it('does not set aria-labelledby when there is no slotted label', async () => {
218
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
219
+ expect(getControl(el).hasAttribute('aria-labelledby')).toBe(false);
220
+ });
221
+
222
+ it('links the control to the message via aria-describedby when a message is set', async () => {
223
+ const el = await renderCheckbox('<dile-checkbox name="terms"></dile-checkbox>');
224
+ el.message = 'Required';
225
+ el.errored = true;
226
+ await el.updateComplete;
227
+
228
+ const control = getControl(el);
229
+ const describedbyId = control.getAttribute('aria-describedby');
230
+
231
+ expect(describedbyId).toBeTruthy();
232
+ expect(el.shadowRoot.getElementById(describedbyId)).toBeTruthy();
233
+ });
234
+ });
235
+ });
@@ -1,5 +1,6 @@
1
1
  import { html, css, LitElement } from "lit";
2
- import { DileEmmitChange } from '../../../mixins/form/index.js';
2
+ import { ifDefined } from 'lit/directives/if-defined.js';
3
+ import { DileEmmitChange } from '../../../mixins/form/index.js';
3
4
  import { checkboxBlankIcon, checkboxCheckedIcon } from '@dile/icons';
4
5
  import '../../icon/icon.js';
5
6
  import '../../input/input-message.js';
@@ -10,7 +11,7 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
10
11
  checked: { type: Boolean, reflect: true, },
11
12
  disabled: { type: Boolean, reflect: true, },
12
13
  _hasInner: { type: Boolean },
13
- name: { type: String },
14
+ name: { type: String, reflect: true, },
14
15
  message: { type: String },
15
16
  errored: { type: Boolean },
16
17
  hideErrorOnInput: { type: Boolean },
@@ -24,9 +25,11 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
24
25
  constructor() {
25
26
  super();
26
27
  this.checked = false;
28
+ this.disabled = false;
27
29
  this.name = '';
28
30
  this.internals = this.attachInternals();
29
- this.message = ''
31
+ this.message = '';
32
+ this._id = `dile-checkbox-${Math.random().toString(36).substr(2, 9)}`;
30
33
  }
31
34
 
32
35
  static get styles() {
@@ -50,6 +53,10 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
50
53
  align-items: center;
51
54
  justify-content: center;
52
55
  }
56
+ .checkbox:focus-visible {
57
+ outline: var(--dile-checkbox-focus-outline, 2px solid #4A90E2);
58
+ outline-offset: 2px;
59
+ }
53
60
  .isUnchecked {
54
61
  --dile-icon-color: var(--dile-checkbox-unchecked-color, var(--dile-on-background-color, #303030));
55
62
  }
@@ -86,13 +93,22 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
86
93
  render() {
87
94
  return html`
88
95
  <div @click="${this.doClick}" class="${this.disabled ? "disabled" : ""}">
89
- <a href="#" @click="${this.linkClick}" @keypress="${this.doKeyPress}" class="checkbox ${this.checked ? "isChecked" : "isUnchecked"}">
96
+ <span
97
+ role="checkbox"
98
+ aria-checked="${this.checked}"
99
+ aria-disabled="${this.disabled ? "true" : "false"}"
100
+ aria-labelledby="${ifDefined(this._hasInner ? `${this._id}-label` : undefined)}"
101
+ aria-describedby="${ifDefined(this.message ? `${this._id}-message` : undefined)}"
102
+ tabindex="${this.disabled ? "-1" : "0"}"
103
+ @keydown="${this.doKeyDown}"
104
+ class="checkbox ${this.checked ? "isChecked" : "isUnchecked"}"
105
+ >
90
106
  ${this.checked ? this.checkedIcon : this.unCheckedIcon}
91
- </a>
107
+ </span>
92
108
  ${this.innerTemplate}
93
109
  </div>
94
110
  ${this.message
95
- ? html`<dile-input-message message="${this.message}" ?errored=${this.errored}></dile-input-message>`
111
+ ? html`<dile-input-message id="${this._id}-message" message="${this.message}" ?errored=${this.errored}></dile-input-message>`
96
112
  : ''
97
113
  }
98
114
  `;
@@ -101,14 +117,14 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
101
117
  get innerTemplate() {
102
118
  return html`
103
119
  ${this._hasInner
104
- ? html` <label class="label">
120
+ ? html` <label id="${this._id}-label" class="label">
105
121
  <slot></slot>
106
122
  </label>`
107
123
  : ""
108
124
  }
109
125
  `
110
126
  }
111
-
127
+
112
128
  doClick() {
113
129
  if (this.disabled) {
114
130
  return;
@@ -137,16 +153,12 @@ export class DileCheckbox extends DileEmmitChange(LitElement) {
137
153
  return html`<dile-icon .icon="${checkboxBlankIcon}"></dile-icon>`;
138
154
  }
139
155
 
140
- doKeyPress(e) {
141
- e.preventDefault();
142
- if (e.keyCode == 32 || e.code == "Space") {
156
+ doKeyDown(e) {
157
+ if (e.key == " " || e.code == "Space" || e.keyCode == 32) {
158
+ e.preventDefault();
143
159
  this.doClick();
144
160
  }
145
161
  }
146
-
147
- linkClick(e) {
148
- e.preventDefault();
149
- }
150
162
 
151
163
  clear() {
152
164
  this.checked = false;
@@ -114,4 +114,20 @@ describe('dile-color-picker', () => {
114
114
 
115
115
  expect(data.get('color')).toBe('#123456');
116
116
  });
117
+
118
+ it('participates in FormData when name is set as a JS property', async () => {
119
+ document.body.innerHTML = `
120
+ <form id="testForm">
121
+ <dile-color-picker value="#123456"></dile-color-picker>
122
+ </form>
123
+ `;
124
+ const el = document.body.querySelector('dile-color-picker');
125
+ el.name = 'color';
126
+ await el.updateComplete;
127
+
128
+ expect(el.getAttribute('name')).toBe('color');
129
+
130
+ const data = new FormData(document.getElementById('testForm'));
131
+ expect(data.get('color')).toBe('#123456');
132
+ });
117
133
  });
@@ -26,7 +26,7 @@ export class DileColorPicker extends DileOverlay(
26
26
  label: { type: String },
27
27
 
28
28
  /** Name for this input field */
29
- name: { type: String },
29
+ name: { type: String, reflect: true },
30
30
 
31
31
  /** Set initial value to the input. Must be a 6-digit hex color, e.g. "#3366ff" */
32
32
  value: { type: String },
@@ -60,7 +60,7 @@ export class DileDropFile extends DileEmmitChange(LitElement) {
60
60
  selectedFileLabel: { type: String },
61
61
  allowedExtensions: { type: Array },
62
62
  extensionErrorMessage: { type: String },
63
- name: { type: String },
63
+ name: { type: String, reflect: true },
64
64
  };
65
65
  }
66
66
 
@@ -35,7 +35,7 @@ export class DileInput extends DileEmmitChange(LitElement) {
35
35
  value: { type: String },
36
36
 
37
37
  /** Name for this input field */
38
- name: { type: String },
38
+ name: { type: String, reflect: true },
39
39
 
40
40
  /** Has error on this input field */
41
41
  errored: { type: Boolean },
@@ -37,7 +37,7 @@ export class DileNumberPicker extends DileEmmitChange(LitElement) {
37
37
  disabled: { type: Boolean },
38
38
 
39
39
  /** Name for this input field */
40
- name: { type: String },
40
+ name: { type: String, reflect: true },
41
41
 
42
42
  /** Message Displayed */
43
43
  message: { type: String },
@@ -35,7 +35,7 @@ export class DileOtpInput extends DileEmmitChange(LitElement) {
35
35
  value: { type: String },
36
36
 
37
37
  /** Name for this input field */
38
- name: { type: String },
38
+ name: { type: String, reflect: true },
39
39
 
40
40
  /** Disable the input field */
41
41
  disabled: { type: Boolean },
@@ -26,7 +26,7 @@ export class DileRadioGroup extends DileEmmitChange(LitElement) {
26
26
  return {
27
27
  label: { type: String },
28
28
  value: { type: String },
29
- name: { type: String },
29
+ name: { type: String, reflect: true },
30
30
  message: { type: String },
31
31
  errored: { type: Boolean },
32
32
  hideErrorOnInput: { type: Boolean },
@@ -96,7 +96,7 @@ export class DileRange extends DileEmmitChange(LitElement) {
96
96
  min: { type: Number },
97
97
  step: { type: Number },
98
98
  value: { type: Number },
99
- name: { type: String },
99
+ name: { type: String, reflect: true },
100
100
  label: { type: String },
101
101
  message: { type: String },
102
102
  errored: { type: Boolean, reflect: true },
@@ -531,5 +531,44 @@ describe('dile-select', () => {
531
531
 
532
532
  expect(el.internals.form).toBeDefined();
533
533
  });
534
+
535
+ it('participates in FormData when name is set as an attribute', async () => {
536
+ document.body.innerHTML = `
537
+ <form id="testForm">
538
+ <dile-select name="fruit" value="1">
539
+ <select slot="select">
540
+ <option value="1">Option 1</option>
541
+ <option value="2">Option 2</option>
542
+ </select>
543
+ </dile-select>
544
+ </form>
545
+ `;
546
+ const el = document.body.querySelector('dile-select');
547
+ await el.updateComplete;
548
+
549
+ const data = new FormData(document.getElementById('testForm'));
550
+ expect(data.get('fruit')).toBe('1');
551
+ });
552
+
553
+ it('participates in FormData when name is set as a JS property', async () => {
554
+ document.body.innerHTML = `
555
+ <form id="testForm">
556
+ <dile-select value="1">
557
+ <select slot="select">
558
+ <option value="1">Option 1</option>
559
+ <option value="2">Option 2</option>
560
+ </select>
561
+ </dile-select>
562
+ </form>
563
+ `;
564
+ const el = document.body.querySelector('dile-select');
565
+ el.name = 'fruit';
566
+ await el.updateComplete;
567
+
568
+ expect(el.getAttribute('name')).toBe('fruit');
569
+
570
+ const data = new FormData(document.getElementById('testForm'));
571
+ expect(data.get('fruit')).toBe('1');
572
+ });
534
573
  });
535
574
  });
@@ -98,7 +98,7 @@ export class DileSelect extends DileEmmitChange(LitElement) {
98
98
  return {
99
99
  label: { type: String },
100
100
  value: { type: String },
101
- name: { type: String },
101
+ name: { type: String, reflect: true },
102
102
  disabled: { type: Boolean },
103
103
  errored: { type: Boolean },
104
104
  message: { type: String },
@@ -66,7 +66,7 @@ export class DileSelectAjax extends DileEmmitChange(LitElement) {
66
66
  endpoint: { type: String },
67
67
  label: { type: String },
68
68
  value: { type: String },
69
- name: { type: String },
69
+ name: { type: String, reflect: true },
70
70
  disabled: { type: Boolean },
71
71
  errored: { type: Boolean },
72
72
  data: { type: Array },
@@ -19,7 +19,7 @@ export class DileSelectBoolean extends DileEmmitChange(LitElement) {
19
19
  return {
20
20
  label: { type: String },
21
21
  value: {},
22
- name: { type: String },
22
+ name: { type: String, reflect: true },
23
23
  disabled: { type: Boolean },
24
24
  errored: { type: Boolean },
25
25
  message: { type: String },
@@ -58,7 +58,7 @@ export class DileTextarea extends DileEmmitChange(LitElement) {
58
58
 
59
59
  static get properties() {
60
60
  return {
61
- name: { type: String },
61
+ name: { type: String, reflect: true },
62
62
  label: { type: String },
63
63
  errored: {
64
64
  type: Boolean,
@@ -37,7 +37,7 @@ export class DileTimePicker extends DileEmmitChange(LitElement) {
37
37
  errored: { type: Boolean },
38
38
  hideErrorOnInput: { type: Boolean },
39
39
  focusOnStart: { type: Boolean },
40
- name: { type: String },
40
+ name: { type: String, reflect: true },
41
41
  };
42
42
  }
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dile/ui",
3
- "version": "3.4.4",
3
+ "version": "3.4.5",
4
4
  "description": "UI Core components from dile-components.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -28,5 +28,5 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
- "gitHead": "addd7ce4fc5edbd5145fbe1202e61894b42e0c0a"
31
+ "gitHead": "31136286663cc7482a95a8813d845e73c3f6c57d"
32
32
  }