bard-tag_field 0.5.1 → 0.5.3
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.
- checksums.yaml +4 -4
- data/CLAUDE.md +9 -8
- data/Rakefile +4 -1
- data/app/assets/javascripts/input-tag.js +16 -30
- data/input-tag/.gitignore +6 -2
- data/input-tag/CLAUDE.md +87 -0
- data/input-tag/LICENSE +21 -0
- data/input-tag/README.md +135 -0
- data/input-tag/TESTING.md +99 -0
- data/input-tag/bun.lock +821 -0
- data/input-tag/index.html +331 -0
- data/input-tag/package.json +52 -8
- data/input-tag/rollup.config.js +4 -4
- data/input-tag/src/input-tag.js +849 -0
- data/input-tag/src/taggle.js +546 -0
- data/input-tag/test/api-methods.test.js +684 -0
- data/input-tag/test/autocomplete.test.js +615 -0
- data/input-tag/test/basic-functionality.test.js +567 -0
- data/input-tag/test/dom-mutation.test.js +466 -0
- data/input-tag/test/edge-cases.test.js +524 -0
- data/input-tag/test/events.test.js +425 -0
- data/input-tag/test/form-integration.test.js +447 -0
- data/input-tag/test/input-tag.test.js +90 -0
- data/input-tag/test/lib/fail-only.mjs +24 -0
- data/input-tag/test/lib/test-utils.js +187 -0
- data/input-tag/test/nested-datalist.test.js +328 -0
- data/input-tag/test/value-label-separation.test.js +357 -0
- data/input-tag/web-test-runner.config.mjs +20 -0
- data/lib/bard/tag_field/version.rb +1 -1
- metadata +23 -4
- data/input-tag/bun.lockb +0 -0
- data/input-tag/index.js +0 -1
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { expect } from '@esm-bundle/chai'
|
|
2
|
+
import '../src/input-tag.js'
|
|
3
|
+
import {
|
|
4
|
+
setupGlobalTestHooks,
|
|
5
|
+
waitForElement,
|
|
6
|
+
waitForUpdate,
|
|
7
|
+
getTagElements,
|
|
8
|
+
getTagValues
|
|
9
|
+
} from './lib/test-utils.js'
|
|
10
|
+
|
|
11
|
+
describe('DOM Mutation Handling', () => {
|
|
12
|
+
setupGlobalTestHooks()
|
|
13
|
+
|
|
14
|
+
describe('Attribute Changes', () => {
|
|
15
|
+
it('should update when name attribute changes', async () => {
|
|
16
|
+
document.body.innerHTML = `
|
|
17
|
+
<input-tag name="original-name" multiple>
|
|
18
|
+
<tag-option value="test">Test</tag-option>
|
|
19
|
+
</input-tag>
|
|
20
|
+
`
|
|
21
|
+
const inputTag = document.querySelector('input-tag')
|
|
22
|
+
await waitForElement(inputTag, '_taggle')
|
|
23
|
+
|
|
24
|
+
expect(inputTag.name).to.equal('original-name')
|
|
25
|
+
|
|
26
|
+
// Simulate DOM morphing changing the name attribute
|
|
27
|
+
inputTag.setAttribute('name', 'morphed-name')
|
|
28
|
+
await waitForUpdate()
|
|
29
|
+
|
|
30
|
+
expect(inputTag.name).to.equal('morphed-name')
|
|
31
|
+
|
|
32
|
+
// Check that internal hidden input also updated
|
|
33
|
+
const hiddenInput = inputTag.shadowRoot.querySelector('input[type="hidden"]')
|
|
34
|
+
expect(hiddenInput.name).to.equal('morphed-name')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should update when multiple attribute is added', async () => {
|
|
38
|
+
document.body.innerHTML = `
|
|
39
|
+
<input-tag name="tags">
|
|
40
|
+
<tag-option value="single">Single</tag-option>
|
|
41
|
+
</input-tag>
|
|
42
|
+
`
|
|
43
|
+
const inputTag = document.querySelector('input-tag')
|
|
44
|
+
await waitForElement(inputTag, '_taggle')
|
|
45
|
+
|
|
46
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
47
|
+
|
|
48
|
+
// Add multiple attribute via DOM mutation
|
|
49
|
+
inputTag.setAttribute('multiple', '')
|
|
50
|
+
await waitForUpdate()
|
|
51
|
+
|
|
52
|
+
// Should be able to add more tags now
|
|
53
|
+
inputTag.add('second')
|
|
54
|
+
inputTag.add('third')
|
|
55
|
+
await waitForUpdate()
|
|
56
|
+
|
|
57
|
+
expect(getTagElements(inputTag)).to.have.length(3)
|
|
58
|
+
expect(getTagValues(inputTag)).to.deep.equal(['single', 'second', 'third'])
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should update when multiple attribute is removed', async () => {
|
|
62
|
+
document.body.innerHTML = `
|
|
63
|
+
<input-tag name="tags" multiple>
|
|
64
|
+
<tag-option value="first">First</tag-option>
|
|
65
|
+
<tag-option value="second">Second</tag-option>
|
|
66
|
+
</input-tag>
|
|
67
|
+
`
|
|
68
|
+
const inputTag = document.querySelector('input-tag')
|
|
69
|
+
await waitForElement(inputTag, '_taggle')
|
|
70
|
+
|
|
71
|
+
expect(getTagElements(inputTag)).to.have.length(2)
|
|
72
|
+
|
|
73
|
+
// Remove multiple attribute via DOM mutation
|
|
74
|
+
inputTag.removeAttribute('multiple')
|
|
75
|
+
await waitForUpdate()
|
|
76
|
+
|
|
77
|
+
// Should now be limited to single tag mode
|
|
78
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
79
|
+
|
|
80
|
+
// Trying to add another should not work
|
|
81
|
+
inputTag.add('third')
|
|
82
|
+
await waitForUpdate()
|
|
83
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should update when required attribute changes', async () => {
|
|
87
|
+
document.body.innerHTML = `
|
|
88
|
+
<input-tag name="tags" multiple></input-tag>
|
|
89
|
+
`
|
|
90
|
+
const inputTag = document.querySelector('input-tag')
|
|
91
|
+
await waitForElement(inputTag, '_taggle')
|
|
92
|
+
|
|
93
|
+
expect(inputTag.checkValidity()).to.be.true
|
|
94
|
+
|
|
95
|
+
// Add required attribute via DOM mutation
|
|
96
|
+
inputTag.setAttribute('required', '')
|
|
97
|
+
await waitForUpdate()
|
|
98
|
+
|
|
99
|
+
expect(inputTag.checkValidity()).to.be.false
|
|
100
|
+
|
|
101
|
+
// Add a tag to make it valid
|
|
102
|
+
inputTag.add('required-tag')
|
|
103
|
+
await waitForUpdate()
|
|
104
|
+
|
|
105
|
+
expect(inputTag.checkValidity()).to.be.true
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should update when list attribute changes', async () => {
|
|
109
|
+
document.body.innerHTML = `
|
|
110
|
+
<input-tag name="tags" multiple></input-tag>
|
|
111
|
+
<datalist id="options1">
|
|
112
|
+
<option value="option1">Option 1</option>
|
|
113
|
+
</datalist>
|
|
114
|
+
<datalist id="options2">
|
|
115
|
+
<option value="option2">Option 2</option>
|
|
116
|
+
<option value="option3">Option 3</option>
|
|
117
|
+
</datalist>
|
|
118
|
+
`
|
|
119
|
+
const inputTag = document.querySelector('input-tag')
|
|
120
|
+
await waitForElement(inputTag, '_taggle')
|
|
121
|
+
|
|
122
|
+
expect(inputTag.options).to.deep.equal([])
|
|
123
|
+
|
|
124
|
+
// Add list attribute via DOM mutation
|
|
125
|
+
inputTag.setAttribute('list', 'options1')
|
|
126
|
+
await waitForUpdate()
|
|
127
|
+
|
|
128
|
+
expect(inputTag.options).to.deep.equal(['option1'])
|
|
129
|
+
|
|
130
|
+
// Change list attribute to different datalist
|
|
131
|
+
inputTag.setAttribute('list', 'options2')
|
|
132
|
+
await waitForUpdate()
|
|
133
|
+
|
|
134
|
+
expect(inputTag.options).to.deep.equal(['option2', 'option3'])
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('Tag Option Changes', () => {
|
|
139
|
+
it('should update when tag-option elements are added', async () => {
|
|
140
|
+
document.body.innerHTML = `
|
|
141
|
+
<input-tag name="tags" multiple>
|
|
142
|
+
<tag-option value="initial">Initial</tag-option>
|
|
143
|
+
</input-tag>
|
|
144
|
+
`
|
|
145
|
+
const inputTag = document.querySelector('input-tag')
|
|
146
|
+
await waitForElement(inputTag, '_taggle')
|
|
147
|
+
|
|
148
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
149
|
+
expect(getTagValues(inputTag)).to.deep.equal(['initial'])
|
|
150
|
+
|
|
151
|
+
// Add new tag-option via DOM mutation
|
|
152
|
+
const newTagOption = document.createElement('tag-option')
|
|
153
|
+
newTagOption.setAttribute('value', 'added')
|
|
154
|
+
newTagOption.textContent = 'Added'
|
|
155
|
+
inputTag.appendChild(newTagOption)
|
|
156
|
+
await waitForUpdate()
|
|
157
|
+
|
|
158
|
+
expect(getTagElements(inputTag)).to.have.length(2)
|
|
159
|
+
expect(getTagValues(inputTag)).to.deep.equal(['initial', 'added'])
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('should update when tag-option elements are removed', async () => {
|
|
163
|
+
document.body.innerHTML = `
|
|
164
|
+
<input-tag name="tags" multiple>
|
|
165
|
+
<tag-option value="keep">Keep</tag-option>
|
|
166
|
+
<tag-option value="remove">Remove</tag-option>
|
|
167
|
+
<tag-option value="also-keep">Also Keep</tag-option>
|
|
168
|
+
</input-tag>
|
|
169
|
+
`
|
|
170
|
+
const inputTag = document.querySelector('input-tag')
|
|
171
|
+
await waitForElement(inputTag, '_taggle')
|
|
172
|
+
|
|
173
|
+
expect(getTagElements(inputTag)).to.have.length(3)
|
|
174
|
+
expect(getTagValues(inputTag)).to.deep.equal(['keep', 'remove', 'also-keep'])
|
|
175
|
+
|
|
176
|
+
// Remove tag-option via DOM mutation
|
|
177
|
+
const tagOptionToRemove = inputTag.querySelector('tag-option[value="remove"]')
|
|
178
|
+
inputTag.removeChild(tagOptionToRemove)
|
|
179
|
+
await waitForUpdate()
|
|
180
|
+
|
|
181
|
+
expect(getTagElements(inputTag)).to.have.length(2)
|
|
182
|
+
expect(getTagValues(inputTag)).to.deep.equal(['keep', 'also-keep'])
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should update when tag-option value attribute changes', async () => {
|
|
186
|
+
document.body.innerHTML = `
|
|
187
|
+
<input-tag name="tags" multiple>
|
|
188
|
+
<tag-option value="original">Original</tag-option>
|
|
189
|
+
</input-tag>
|
|
190
|
+
`
|
|
191
|
+
const inputTag = document.querySelector('input-tag')
|
|
192
|
+
await waitForElement(inputTag, '_taggle')
|
|
193
|
+
|
|
194
|
+
expect(getTagValues(inputTag)).to.deep.equal(['original'])
|
|
195
|
+
|
|
196
|
+
// Change tag-option value via DOM mutation
|
|
197
|
+
const tagOption = inputTag.querySelector('tag-option')
|
|
198
|
+
tagOption.setAttribute('value', 'modified')
|
|
199
|
+
await waitForUpdate()
|
|
200
|
+
|
|
201
|
+
expect(getTagValues(inputTag)).to.deep.equal(['modified'])
|
|
202
|
+
|
|
203
|
+
// Verify the tag visually updated too
|
|
204
|
+
const tagElements = getTagElements(inputTag)
|
|
205
|
+
expect(tagElements[0].getAttribute('data-value')).to.equal('modified')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should update when tag-option text content changes', async () => {
|
|
209
|
+
document.body.innerHTML = `
|
|
210
|
+
<input-tag name="tags" multiple>
|
|
211
|
+
<tag-option value="test">Original Text</tag-option>
|
|
212
|
+
</input-tag>
|
|
213
|
+
`
|
|
214
|
+
const inputTag = document.querySelector('input-tag')
|
|
215
|
+
await waitForElement(inputTag, '_taggle')
|
|
216
|
+
|
|
217
|
+
// Change tag-option text content via DOM mutation
|
|
218
|
+
const tagOption = inputTag.querySelector('tag-option')
|
|
219
|
+
tagOption.textContent = 'Modified Text'
|
|
220
|
+
await waitForUpdate()
|
|
221
|
+
|
|
222
|
+
// Verify the tag display updated
|
|
223
|
+
const tagElements = getTagElements(inputTag)
|
|
224
|
+
expect(tagElements[0].textContent.trim()).to.include('Modified Text')
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('Complex DOM Mutations', () => {
|
|
229
|
+
it('should handle multiple simultaneous attribute changes', async () => {
|
|
230
|
+
document.body.innerHTML = `
|
|
231
|
+
<input-tag name="original" multiple>
|
|
232
|
+
<tag-option value="test">Test</tag-option>
|
|
233
|
+
</input-tag>
|
|
234
|
+
<datalist id="new-options">
|
|
235
|
+
<option value="option1">Option 1</option>
|
|
236
|
+
</datalist>
|
|
237
|
+
`
|
|
238
|
+
const inputTag = document.querySelector('input-tag')
|
|
239
|
+
await waitForElement(inputTag, '_taggle')
|
|
240
|
+
|
|
241
|
+
// Simulate complex DOM morphing changing multiple attributes
|
|
242
|
+
inputTag.setAttribute('name', 'morphed')
|
|
243
|
+
inputTag.setAttribute('required', '')
|
|
244
|
+
inputTag.setAttribute('list', 'new-options')
|
|
245
|
+
await waitForUpdate()
|
|
246
|
+
|
|
247
|
+
expect(inputTag.name).to.equal('morphed')
|
|
248
|
+
expect(inputTag.hasAttribute('required')).to.be.true
|
|
249
|
+
expect(inputTag.options).to.deep.equal(['option1'])
|
|
250
|
+
|
|
251
|
+
// Should still be invalid due to having no valid tags for required field
|
|
252
|
+
expect(inputTag.checkValidity()).to.be.true // has existing tag
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('should handle tag-option replacement via innerHTML', async () => {
|
|
256
|
+
document.body.innerHTML = `
|
|
257
|
+
<input-tag name="tags" multiple>
|
|
258
|
+
<tag-option value="old1">Old 1</tag-option>
|
|
259
|
+
<tag-option value="old2">Old 2</tag-option>
|
|
260
|
+
</input-tag>
|
|
261
|
+
`
|
|
262
|
+
const inputTag = document.querySelector('input-tag')
|
|
263
|
+
await waitForElement(inputTag, '_taggle')
|
|
264
|
+
|
|
265
|
+
expect(getTagValues(inputTag)).to.deep.equal(['old1', 'old2'])
|
|
266
|
+
|
|
267
|
+
// Replace all content via innerHTML (simulating morphing)
|
|
268
|
+
inputTag.innerHTML = `
|
|
269
|
+
<tag-option value="new1">New 1</tag-option>
|
|
270
|
+
<tag-option value="new2">New 2</tag-option>
|
|
271
|
+
<tag-option value="new3">New 3</tag-option>
|
|
272
|
+
`
|
|
273
|
+
await waitForUpdate()
|
|
274
|
+
|
|
275
|
+
expect(getTagValues(inputTag)).to.deep.equal(['new1', 'new2', 'new3'])
|
|
276
|
+
expect(getTagElements(inputTag)).to.have.length(3)
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('should handle mixed tag-option additions and removals', async () => {
|
|
280
|
+
document.body.innerHTML = `
|
|
281
|
+
<input-tag name="tags" multiple>
|
|
282
|
+
<tag-option value="keep">Keep</tag-option>
|
|
283
|
+
<tag-option value="remove">Remove</tag-option>
|
|
284
|
+
</input-tag>
|
|
285
|
+
`
|
|
286
|
+
const inputTag = document.querySelector('input-tag')
|
|
287
|
+
await waitForElement(inputTag, '_taggle')
|
|
288
|
+
|
|
289
|
+
expect(getTagValues(inputTag)).to.deep.equal(['keep', 'remove'])
|
|
290
|
+
|
|
291
|
+
// Remove one and add two new ones
|
|
292
|
+
const removeOption = inputTag.querySelector('tag-option[value="remove"]')
|
|
293
|
+
inputTag.removeChild(removeOption)
|
|
294
|
+
|
|
295
|
+
const newOption1 = document.createElement('tag-option')
|
|
296
|
+
newOption1.setAttribute('value', 'new1')
|
|
297
|
+
newOption1.textContent = 'New 1'
|
|
298
|
+
inputTag.appendChild(newOption1)
|
|
299
|
+
|
|
300
|
+
const newOption2 = document.createElement('tag-option')
|
|
301
|
+
newOption2.setAttribute('value', 'new2')
|
|
302
|
+
newOption2.textContent = 'New 2'
|
|
303
|
+
inputTag.appendChild(newOption2)
|
|
304
|
+
|
|
305
|
+
await waitForUpdate()
|
|
306
|
+
|
|
307
|
+
expect(getTagValues(inputTag)).to.deep.equal(['keep', 'new1', 'new2'])
|
|
308
|
+
expect(getTagElements(inputTag)).to.have.length(3)
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
describe('MutationObserver Integration', () => {
|
|
313
|
+
it('should detect attribute mutations via MutationObserver', async () => {
|
|
314
|
+
document.body.innerHTML = `
|
|
315
|
+
<input-tag name="observer-test" multiple>
|
|
316
|
+
<tag-option value="test">Test</tag-option>
|
|
317
|
+
</input-tag>
|
|
318
|
+
`
|
|
319
|
+
const inputTag = document.querySelector('input-tag')
|
|
320
|
+
await waitForElement(inputTag, '_taggle')
|
|
321
|
+
|
|
322
|
+
// Verify MutationObserver is active
|
|
323
|
+
expect(inputTag.observer).to.not.be.null
|
|
324
|
+
|
|
325
|
+
// Make attribute change that should be observed
|
|
326
|
+
inputTag.setAttribute('name', 'observed-change')
|
|
327
|
+
await waitForUpdate()
|
|
328
|
+
|
|
329
|
+
expect(inputTag.name).to.equal('observed-change')
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('should detect child node mutations via MutationObserver', async () => {
|
|
333
|
+
document.body.innerHTML = `
|
|
334
|
+
<input-tag name="child-observer" multiple>
|
|
335
|
+
<tag-option value="original">Original</tag-option>
|
|
336
|
+
</input-tag>
|
|
337
|
+
`
|
|
338
|
+
const inputTag = document.querySelector('input-tag')
|
|
339
|
+
await waitForElement(inputTag, '_taggle')
|
|
340
|
+
|
|
341
|
+
expect(getTagValues(inputTag)).to.deep.equal(['original'])
|
|
342
|
+
|
|
343
|
+
// Add child that should be observed
|
|
344
|
+
const newChild = document.createElement('tag-option')
|
|
345
|
+
newChild.setAttribute('value', 'observed')
|
|
346
|
+
newChild.textContent = 'Observed'
|
|
347
|
+
inputTag.appendChild(newChild)
|
|
348
|
+
await waitForUpdate()
|
|
349
|
+
|
|
350
|
+
expect(getTagValues(inputTag)).to.deep.equal(['original', 'observed'])
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it('should clean up MutationObserver on disconnect', async () => {
|
|
354
|
+
document.body.innerHTML = `
|
|
355
|
+
<input-tag name="cleanup-test" multiple>
|
|
356
|
+
<tag-option value="test">Test</tag-option>
|
|
357
|
+
</input-tag>
|
|
358
|
+
`
|
|
359
|
+
const inputTag = document.querySelector('input-tag')
|
|
360
|
+
await waitForElement(inputTag, '_taggle')
|
|
361
|
+
|
|
362
|
+
expect(inputTag.observer).to.not.be.null
|
|
363
|
+
|
|
364
|
+
// Disconnect should clean up observer
|
|
365
|
+
inputTag.disconnectedCallback()
|
|
366
|
+
|
|
367
|
+
// Observer should be cleaned up (can't directly test this, but ensure no errors)
|
|
368
|
+
expect(() => {
|
|
369
|
+
inputTag.setAttribute('name', 'should-not-crash')
|
|
370
|
+
}).to.not.throw()
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
describe('Edge Cases', () => {
|
|
375
|
+
it('should handle malformed tag-option mutations gracefully', async () => {
|
|
376
|
+
document.body.innerHTML = `
|
|
377
|
+
<input-tag name="malformed" multiple></input-tag>
|
|
378
|
+
`
|
|
379
|
+
const inputTag = document.querySelector('input-tag')
|
|
380
|
+
await waitForElement(inputTag, '_taggle')
|
|
381
|
+
|
|
382
|
+
// Add malformed tag-option (no value attribute)
|
|
383
|
+
const malformedOption = document.createElement('tag-option')
|
|
384
|
+
malformedOption.textContent = 'No Value'
|
|
385
|
+
inputTag.appendChild(malformedOption)
|
|
386
|
+
await waitForUpdate()
|
|
387
|
+
|
|
388
|
+
// Should handle gracefully without crashing
|
|
389
|
+
expect(() => getTagValues(inputTag)).to.not.throw()
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
it('should handle rapid mutations without race conditions', async () => {
|
|
393
|
+
document.body.innerHTML = `
|
|
394
|
+
<input-tag name="rapid" multiple></input-tag>
|
|
395
|
+
`
|
|
396
|
+
const inputTag = document.querySelector('input-tag')
|
|
397
|
+
await waitForElement(inputTag, '_taggle')
|
|
398
|
+
|
|
399
|
+
// Rapid mutations
|
|
400
|
+
for (let i = 0; i < 10; i++) {
|
|
401
|
+
const option = document.createElement('tag-option')
|
|
402
|
+
option.setAttribute('value', `rapid-${i}`)
|
|
403
|
+
option.textContent = `Rapid ${i}`
|
|
404
|
+
inputTag.appendChild(option)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
await waitForUpdate(100) // Give extra time for processing
|
|
408
|
+
|
|
409
|
+
expect(getTagElements(inputTag)).to.have.length(10)
|
|
410
|
+
expect(getTagValues(inputTag)).to.include('rapid-0')
|
|
411
|
+
expect(getTagValues(inputTag)).to.include('rapid-9')
|
|
412
|
+
})
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
describe('Form Integration with Mutations', () => {
|
|
416
|
+
it('should update FormData when name attribute changes', async () => {
|
|
417
|
+
document.body.innerHTML = `
|
|
418
|
+
<form>
|
|
419
|
+
<input-tag name="original-form-name" multiple>
|
|
420
|
+
<tag-option value="form-test">Form Test</tag-option>
|
|
421
|
+
</input-tag>
|
|
422
|
+
</form>
|
|
423
|
+
`
|
|
424
|
+
const form = document.querySelector('form')
|
|
425
|
+
const inputTag = document.querySelector('input-tag')
|
|
426
|
+
await waitForElement(inputTag, '_taggle')
|
|
427
|
+
|
|
428
|
+
let formData = new FormData(form)
|
|
429
|
+
expect(formData.getAll('original-form-name')).to.deep.equal(['form-test'])
|
|
430
|
+
|
|
431
|
+
// Change name via mutation
|
|
432
|
+
inputTag.setAttribute('name', 'mutated-form-name')
|
|
433
|
+
await waitForUpdate()
|
|
434
|
+
|
|
435
|
+
formData = new FormData(form)
|
|
436
|
+
expect(formData.getAll('mutated-form-name')).to.deep.equal(['form-test'])
|
|
437
|
+
expect(formData.getAll('original-form-name')).to.deep.equal([])
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
it('should maintain form validity state through mutations', async () => {
|
|
441
|
+
document.body.innerHTML = `
|
|
442
|
+
<form>
|
|
443
|
+
<input-tag name="validity-test" multiple>
|
|
444
|
+
<tag-option value="valid">Valid</tag-option>
|
|
445
|
+
</input-tag>
|
|
446
|
+
</form>
|
|
447
|
+
`
|
|
448
|
+
const inputTag = document.querySelector('input-tag')
|
|
449
|
+
await waitForElement(inputTag, '_taggle')
|
|
450
|
+
|
|
451
|
+
expect(inputTag.checkValidity()).to.be.true
|
|
452
|
+
|
|
453
|
+
// Add required attribute
|
|
454
|
+
inputTag.setAttribute('required', '')
|
|
455
|
+
await waitForUpdate()
|
|
456
|
+
|
|
457
|
+
expect(inputTag.checkValidity()).to.be.true // Still valid due to existing tag
|
|
458
|
+
|
|
459
|
+
// Remove all tags
|
|
460
|
+
inputTag.innerHTML = ''
|
|
461
|
+
await waitForUpdate()
|
|
462
|
+
|
|
463
|
+
expect(inputTag.checkValidity()).to.be.false // Now invalid
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
})
|