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,357 @@
|
|
|
1
|
+
import { expect } from '@esm-bundle/chai'
|
|
2
|
+
import '../src/input-tag.js'
|
|
3
|
+
import {
|
|
4
|
+
setupGlobalTestHooks,
|
|
5
|
+
setupInputTag,
|
|
6
|
+
waitForUpdate,
|
|
7
|
+
waitForBasicInitialization,
|
|
8
|
+
simulateInput,
|
|
9
|
+
simulateUserAddTag,
|
|
10
|
+
getTagElements,
|
|
11
|
+
getTagValues,
|
|
12
|
+
} from './lib/test-utils.js'
|
|
13
|
+
|
|
14
|
+
describe('Value/Label Separation', () => {
|
|
15
|
+
setupGlobalTestHooks()
|
|
16
|
+
|
|
17
|
+
describe('Basic Value/Label Behavior (Zero API Changes)', () => {
|
|
18
|
+
it('should work with value attribute different from display text', async () => {
|
|
19
|
+
const inputTag = await setupInputTag(`
|
|
20
|
+
<input-tag name="languages" multiple>
|
|
21
|
+
<tag-option value="js">JavaScript</tag-option>
|
|
22
|
+
<tag-option value="py">Python</tag-option>
|
|
23
|
+
</input-tag>
|
|
24
|
+
`)
|
|
25
|
+
|
|
26
|
+
// API works with values (unchanged behavior)
|
|
27
|
+
expect(inputTag.tags).to.deep.equal(['js', 'py'])
|
|
28
|
+
expect(getTagValues(inputTag)).to.deep.equal(['js', 'py'])
|
|
29
|
+
|
|
30
|
+
// But display shows labels
|
|
31
|
+
const tagElements = getTagElements(inputTag)
|
|
32
|
+
expect(tagElements[0].textContent.trim()).to.equal('JavaScript')
|
|
33
|
+
expect(tagElements[1].textContent.trim()).to.equal('Python')
|
|
34
|
+
|
|
35
|
+
// Values are still accessible
|
|
36
|
+
expect(tagElements[0].value).to.equal('js')
|
|
37
|
+
expect(tagElements[1].value).to.equal('py')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should fall back to text content when no value attribute', async () => {
|
|
41
|
+
const inputTag = await setupInputTag(`
|
|
42
|
+
<input-tag name="categories" multiple>
|
|
43
|
+
<tag-option>Frontend</tag-option>
|
|
44
|
+
<tag-option>Backend</tag-option>
|
|
45
|
+
</input-tag>
|
|
46
|
+
`)
|
|
47
|
+
|
|
48
|
+
// Both value and display are the same
|
|
49
|
+
expect(inputTag.tags).to.deep.equal(['Frontend', 'Backend'])
|
|
50
|
+
expect(getTagValues(inputTag)).to.deep.equal(['Frontend', 'Backend'])
|
|
51
|
+
|
|
52
|
+
const tagElements = getTagElements(inputTag)
|
|
53
|
+
expect(tagElements[0].textContent.trim()).to.equal('Frontend')
|
|
54
|
+
expect(tagElements[1].textContent.trim()).to.equal('Backend')
|
|
55
|
+
expect(tagElements[0].value).to.equal('Frontend')
|
|
56
|
+
expect(tagElements[1].value).to.equal('Backend')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should support mixed value/label and text-only tags', async () => {
|
|
60
|
+
const inputTag = await setupInputTag(`
|
|
61
|
+
<input-tag name="mixed" multiple>
|
|
62
|
+
<tag-option value="1">Label A</tag-option>
|
|
63
|
+
<tag-option>Text Only</tag-option>
|
|
64
|
+
<tag-option value="3">Label C</tag-option>
|
|
65
|
+
</input-tag>
|
|
66
|
+
`)
|
|
67
|
+
|
|
68
|
+
// Values array contains mix of explicit values and text
|
|
69
|
+
expect(inputTag.tags).to.deep.equal(['1', 'Text Only', '3'])
|
|
70
|
+
|
|
71
|
+
// Display shows appropriate text
|
|
72
|
+
const tagElements = getTagElements(inputTag)
|
|
73
|
+
expect(tagElements[0].textContent.trim()).to.equal('Label A')
|
|
74
|
+
expect(tagElements[1].textContent.trim()).to.equal('Text Only')
|
|
75
|
+
expect(tagElements[2].textContent.trim()).to.equal('Label C')
|
|
76
|
+
|
|
77
|
+
// Values are correct
|
|
78
|
+
expect(tagElements[0].value).to.equal('1')
|
|
79
|
+
expect(tagElements[1].value).to.equal('Text Only')
|
|
80
|
+
expect(tagElements[2].value).to.equal('3')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('API Methods Work with Values (Backward Compatible)', () => {
|
|
85
|
+
it('should add tags by value, display by label', async () => {
|
|
86
|
+
const inputTag = await setupInputTag(`
|
|
87
|
+
<input-tag name="languages" multiple>
|
|
88
|
+
<tag-option value="js">JavaScript</tag-option>
|
|
89
|
+
</input-tag>
|
|
90
|
+
`)
|
|
91
|
+
|
|
92
|
+
// Add by value (existing API)
|
|
93
|
+
inputTag.add('js')
|
|
94
|
+
await waitForUpdate()
|
|
95
|
+
|
|
96
|
+
// Should not create duplicate since value already exists
|
|
97
|
+
expect(inputTag.tags).to.deep.equal(['js'])
|
|
98
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
99
|
+
|
|
100
|
+
// Display still shows label
|
|
101
|
+
expect(getTagElements(inputTag)[0].textContent.trim()).to.equal('JavaScript')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should remove tags by value', async () => {
|
|
105
|
+
const inputTag = await setupInputTag(`
|
|
106
|
+
<input-tag name="languages" multiple>
|
|
107
|
+
<tag-option value="js">JavaScript</tag-option>
|
|
108
|
+
<tag-option value="py">Python</tag-option>
|
|
109
|
+
</input-tag>
|
|
110
|
+
`)
|
|
111
|
+
|
|
112
|
+
// Remove by value (existing API)
|
|
113
|
+
inputTag.remove('js')
|
|
114
|
+
await waitForUpdate()
|
|
115
|
+
|
|
116
|
+
expect(inputTag.tags).to.deep.equal(['py'])
|
|
117
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
118
|
+
expect(getTagElements(inputTag)[0].textContent.trim()).to.equal('Python')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should check tag existence by value', async () => {
|
|
122
|
+
const inputTag = await setupInputTag(`
|
|
123
|
+
<input-tag name="languages" multiple>
|
|
124
|
+
<tag-option value="js">JavaScript</tag-option>
|
|
125
|
+
</input-tag>
|
|
126
|
+
`)
|
|
127
|
+
|
|
128
|
+
// Check by value (existing API)
|
|
129
|
+
expect(inputTag.has('js')).to.be.true
|
|
130
|
+
expect(inputTag.has('JavaScript')).to.be.false // Label doesn't work for has()
|
|
131
|
+
expect(inputTag.has('nonexistent')).to.be.false
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe('User-Entered Tags (Mixed Values)', () => {
|
|
136
|
+
it('should handle user-typed tags alongside predefined value/label tags', async () => {
|
|
137
|
+
const inputTag = await setupInputTag(`
|
|
138
|
+
<input-tag name="mixed" multiple>
|
|
139
|
+
<tag-option value="1">Predefined A</tag-option>
|
|
140
|
+
<tag-option value="2">Predefined B</tag-option>
|
|
141
|
+
</input-tag>
|
|
142
|
+
`)
|
|
143
|
+
|
|
144
|
+
// User types custom tag
|
|
145
|
+
await simulateUserAddTag(inputTag, 'CustomTag')
|
|
146
|
+
|
|
147
|
+
// Values array contains mix of predefined values and user text
|
|
148
|
+
expect(inputTag.tags).to.deep.equal(['1', '2', 'CustomTag'])
|
|
149
|
+
|
|
150
|
+
// Display shows appropriate text
|
|
151
|
+
const tagElements = getTagElements(inputTag)
|
|
152
|
+
expect(tagElements[0].textContent.trim()).to.equal('Predefined A')
|
|
153
|
+
expect(tagElements[1].textContent.trim()).to.equal('Predefined B')
|
|
154
|
+
expect(tagElements[2].textContent.trim()).to.equal('CustomTag')
|
|
155
|
+
|
|
156
|
+
// User-entered tag has same value and label
|
|
157
|
+
expect(tagElements[2].value).to.equal('CustomTag')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('should allow removing user-entered tags by their text value', async () => {
|
|
161
|
+
const inputTag = await setupInputTag(`
|
|
162
|
+
<input-tag name="mixed" multiple>
|
|
163
|
+
<tag-option value="1">Predefined</tag-option>
|
|
164
|
+
</input-tag>
|
|
165
|
+
`)
|
|
166
|
+
|
|
167
|
+
await simulateUserAddTag(inputTag, 'UserTag')
|
|
168
|
+
expect(inputTag.tags).to.deep.equal(['1', 'UserTag'])
|
|
169
|
+
|
|
170
|
+
// Remove user-entered tag by its value (which is the text)
|
|
171
|
+
inputTag.remove('UserTag')
|
|
172
|
+
await waitForUpdate()
|
|
173
|
+
|
|
174
|
+
expect(inputTag.tags).to.deep.equal(['1'])
|
|
175
|
+
expect(getTagElements(inputTag)).to.have.length(1)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
describe('Form Integration', () => {
|
|
180
|
+
it('should submit values array to form, not labels', async () => {
|
|
181
|
+
const form = document.createElement('form')
|
|
182
|
+
form.innerHTML = `
|
|
183
|
+
<input-tag name="test" multiple>
|
|
184
|
+
<tag-option value="val1">Label 1</tag-option>
|
|
185
|
+
<tag-option value="val2">Label 2</tag-option>
|
|
186
|
+
</input-tag>
|
|
187
|
+
`
|
|
188
|
+
document.body.appendChild(form)
|
|
189
|
+
|
|
190
|
+
const inputTag = form.querySelector('input-tag')
|
|
191
|
+
await waitForUpdate()
|
|
192
|
+
|
|
193
|
+
// Wait for component initialization
|
|
194
|
+
while (!inputTag._taggle) {
|
|
195
|
+
await waitForUpdate()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Add user-entered tag
|
|
199
|
+
await simulateUserAddTag(inputTag, 'UserInput')
|
|
200
|
+
|
|
201
|
+
// Form data should contain values, not labels
|
|
202
|
+
const formData = new FormData(form)
|
|
203
|
+
const values = formData.getAll('test')
|
|
204
|
+
expect(values).to.deep.equal(['val1', 'val2', 'UserInput'])
|
|
205
|
+
|
|
206
|
+
document.body.removeChild(form)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe('TagOption Label Getter', () => {
|
|
211
|
+
it('should provide label getter that returns innerText', async () => {
|
|
212
|
+
const inputTag = await setupInputTag(`
|
|
213
|
+
<input-tag name="test" multiple>
|
|
214
|
+
<tag-option value="short">Long Display Name</tag-option>
|
|
215
|
+
<tag-option>Text Only</tag-option>
|
|
216
|
+
</input-tag>
|
|
217
|
+
`)
|
|
218
|
+
|
|
219
|
+
const tagElements = getTagElements(inputTag)
|
|
220
|
+
|
|
221
|
+
// First tag: value !== label
|
|
222
|
+
expect(tagElements[0].value).to.equal('short')
|
|
223
|
+
expect(tagElements[0].label).to.equal('Long Display Name')
|
|
224
|
+
|
|
225
|
+
// Second tag: value === label
|
|
226
|
+
expect(tagElements[1].value).to.equal('Text Only')
|
|
227
|
+
expect(tagElements[1].label).to.equal('Text Only')
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('Backward Compatibility', () => {
|
|
232
|
+
it('should maintain exact same behavior for tags without value attributes', async () => {
|
|
233
|
+
const inputTag = await setupInputTag(`
|
|
234
|
+
<input-tag name="legacy" multiple>
|
|
235
|
+
<tag-option>JavaScript</tag-option>
|
|
236
|
+
<tag-option>Python</tag-option>
|
|
237
|
+
</input-tag>
|
|
238
|
+
`)
|
|
239
|
+
|
|
240
|
+
// Existing behavior unchanged
|
|
241
|
+
expect(inputTag.tags).to.deep.equal(['JavaScript', 'Python'])
|
|
242
|
+
expect(getTagValues(inputTag)).to.deep.equal(['JavaScript', 'Python'])
|
|
243
|
+
|
|
244
|
+
const tagElements = getTagElements(inputTag)
|
|
245
|
+
expect(tagElements[0].value).to.equal('JavaScript')
|
|
246
|
+
expect(tagElements[1].value).to.equal('Python')
|
|
247
|
+
expect(tagElements[0].textContent.trim()).to.equal('JavaScript')
|
|
248
|
+
expect(tagElements[1].textContent.trim()).to.equal('Python')
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should work with existing add/remove/has API exactly as before', async () => {
|
|
252
|
+
const inputTag = await setupInputTag('<input-tag name="test" multiple></input-tag>')
|
|
253
|
+
|
|
254
|
+
// All existing API behavior unchanged
|
|
255
|
+
inputTag.add('test1')
|
|
256
|
+
inputTag.add(['test2', 'test3'])
|
|
257
|
+
expect(inputTag.tags).to.deep.equal(['test1', 'test2', 'test3'])
|
|
258
|
+
|
|
259
|
+
expect(inputTag.has('test1')).to.be.true
|
|
260
|
+
expect(inputTag.has('nonexistent')).to.be.false
|
|
261
|
+
|
|
262
|
+
inputTag.remove('test2')
|
|
263
|
+
expect(inputTag.tags).to.deep.equal(['test1', 'test3'])
|
|
264
|
+
|
|
265
|
+
inputTag.removeAll()
|
|
266
|
+
expect(inputTag.tags).to.deep.equal([])
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('Autocomplete with Value/Label Separation', () => {
|
|
271
|
+
it('should create tag-option with correct value and label when selecting from autocomplete', async () => {
|
|
272
|
+
// Set up input-tag with datalist that has value/label separation
|
|
273
|
+
document.body.innerHTML = `
|
|
274
|
+
<input-tag name="frameworks" list="suggestions" multiple></input-tag>
|
|
275
|
+
<datalist id="suggestions">
|
|
276
|
+
<option value="vue">Vue.js Framework</option>
|
|
277
|
+
<option value="react">React Library</option>
|
|
278
|
+
</datalist>
|
|
279
|
+
`
|
|
280
|
+
const inputTag = document.querySelector('input-tag')
|
|
281
|
+
await waitForBasicInitialization(inputTag)
|
|
282
|
+
|
|
283
|
+
// Simulate typing "vue" to trigger autocomplete
|
|
284
|
+
const input = inputTag._taggleInputTarget
|
|
285
|
+
await simulateInput(input, 'vue')
|
|
286
|
+
|
|
287
|
+
// Verify autocomplete shows the label
|
|
288
|
+
expect(inputTag._autocompleteSuggestions).to.deep.equal(['Vue.js Framework'])
|
|
289
|
+
|
|
290
|
+
// Simulate clicking on the autocomplete suggestion
|
|
291
|
+
// This should add a tag with value="vue" but display "Vue.js Framework"
|
|
292
|
+
const autocompleteItems = inputTag.autocompleteContainerTarget.querySelectorAll('.ui-menu-item')
|
|
293
|
+
expect(autocompleteItems.length).to.equal(1)
|
|
294
|
+
|
|
295
|
+
// Click the autocomplete item
|
|
296
|
+
autocompleteItems[0].click()
|
|
297
|
+
|
|
298
|
+
// Wait for tag to be added
|
|
299
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
300
|
+
|
|
301
|
+
// The bug: Currently this creates <tag-option value="vue">vue</tag-option>
|
|
302
|
+
// But it should create <tag-option value="vue">Vue.js Framework</tag-option>
|
|
303
|
+
|
|
304
|
+
const tagElements = getTagElements(inputTag)
|
|
305
|
+
expect(tagElements.length).to.equal(1)
|
|
306
|
+
|
|
307
|
+
// Value should be the short form
|
|
308
|
+
expect(tagElements[0].value).to.equal('vue')
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
// BUT display text should be the full label
|
|
312
|
+
expect(tagElements[0].textContent.trim()).to.equal('Vue.js Framework')
|
|
313
|
+
|
|
314
|
+
// Form submission should use the value
|
|
315
|
+
expect(inputTag.tags).to.deep.equal(['vue'])
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
it('should work with mixed value/label and simple options', async () => {
|
|
319
|
+
document.body.innerHTML = `
|
|
320
|
+
<input-tag name="frameworks" list="suggestions" multiple></input-tag>
|
|
321
|
+
<datalist id="suggestions">
|
|
322
|
+
<option value="vue">Vue.js Framework</option>
|
|
323
|
+
<option value="simple">simple</option>
|
|
324
|
+
</datalist>
|
|
325
|
+
`
|
|
326
|
+
const inputTag = document.querySelector('input-tag')
|
|
327
|
+
await waitForBasicInitialization(inputTag)
|
|
328
|
+
|
|
329
|
+
const input = inputTag._taggleInputTarget
|
|
330
|
+
|
|
331
|
+
// Add Vue.js via autocomplete
|
|
332
|
+
await simulateInput(input, 'vue')
|
|
333
|
+
const autocompleteItems1 = inputTag.autocompleteContainerTarget.querySelectorAll('.ui-menu-item')
|
|
334
|
+
autocompleteItems1[0].click()
|
|
335
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
336
|
+
|
|
337
|
+
// Add simple via autocomplete
|
|
338
|
+
await simulateInput(input, 'simple')
|
|
339
|
+
const autocompleteItems2 = inputTag.autocompleteContainerTarget.querySelectorAll('.ui-menu-item')
|
|
340
|
+
autocompleteItems2[0].click()
|
|
341
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
342
|
+
|
|
343
|
+
const tagElements = getTagElements(inputTag)
|
|
344
|
+
expect(tagElements.length).to.equal(2)
|
|
345
|
+
|
|
346
|
+
// First tag: value/label separation
|
|
347
|
+
expect(tagElements[0].value).to.equal('vue')
|
|
348
|
+
expect(tagElements[0].textContent.trim()).to.equal('Vue.js Framework')
|
|
349
|
+
|
|
350
|
+
// Second tag: simple case where value equals label
|
|
351
|
+
expect(tagElements[1].value).to.equal('simple')
|
|
352
|
+
expect(tagElements[1].textContent.trim()).to.equal('simple')
|
|
353
|
+
|
|
354
|
+
expect(inputTag.tags).to.deep.equal(['vue', 'simple'])
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import failOnly from './test/lib/fail-only.mjs'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
files: 'test/**/*.test.js',
|
|
5
|
+
nodeResolve: true,
|
|
6
|
+
coverage: true,
|
|
7
|
+
coverageConfig: {
|
|
8
|
+
include: ['src/**/*']
|
|
9
|
+
},
|
|
10
|
+
testFramework: {
|
|
11
|
+
config: {
|
|
12
|
+
timeout: 10000 // Increased timeout for slower browsers
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
plugins: [failOnly],
|
|
16
|
+
// Browser timeout configurations for cross-browser compatibility
|
|
17
|
+
browserStartTimeout: 60000,
|
|
18
|
+
testsStartTimeout: 20000,
|
|
19
|
+
testsFinishTimeout: 120000
|
|
20
|
+
};
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bard-tag_field
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -160,10 +160,29 @@ files:
|
|
|
160
160
|
- gemfiles/rails_8.0.gemfile
|
|
161
161
|
- gemfiles/rails_8.1.gemfile
|
|
162
162
|
- input-tag/.gitignore
|
|
163
|
-
- input-tag/
|
|
164
|
-
- input-tag/
|
|
163
|
+
- input-tag/CLAUDE.md
|
|
164
|
+
- input-tag/LICENSE
|
|
165
|
+
- input-tag/README.md
|
|
166
|
+
- input-tag/TESTING.md
|
|
167
|
+
- input-tag/bun.lock
|
|
168
|
+
- input-tag/index.html
|
|
165
169
|
- input-tag/package.json
|
|
166
170
|
- input-tag/rollup.config.js
|
|
171
|
+
- input-tag/src/input-tag.js
|
|
172
|
+
- input-tag/src/taggle.js
|
|
173
|
+
- input-tag/test/api-methods.test.js
|
|
174
|
+
- input-tag/test/autocomplete.test.js
|
|
175
|
+
- input-tag/test/basic-functionality.test.js
|
|
176
|
+
- input-tag/test/dom-mutation.test.js
|
|
177
|
+
- input-tag/test/edge-cases.test.js
|
|
178
|
+
- input-tag/test/events.test.js
|
|
179
|
+
- input-tag/test/form-integration.test.js
|
|
180
|
+
- input-tag/test/input-tag.test.js
|
|
181
|
+
- input-tag/test/lib/fail-only.mjs
|
|
182
|
+
- input-tag/test/lib/test-utils.js
|
|
183
|
+
- input-tag/test/nested-datalist.test.js
|
|
184
|
+
- input-tag/test/value-label-separation.test.js
|
|
185
|
+
- input-tag/web-test-runner.config.mjs
|
|
167
186
|
- lib/bard/tag_field.rb
|
|
168
187
|
- lib/bard/tag_field/cucumber.rb
|
|
169
188
|
- lib/bard/tag_field/field.rb
|
data/input-tag/bun.lockb
DELETED
|
Binary file
|
data/input-tag/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import "@botandrose/input-tag"
|