bard-tag_field 0.7.3 → 0.7.4
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/app/assets/javascripts/input-tag.js +33 -2
- data/input-tag/src/input-tag.js +33 -2
- data/input-tag/test/form-state-restore.test.js +54 -0
- data/lib/bard/tag_field/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5af78ac6af58f7abb2ec0f83972de2d3265bdf2e7fe2590d3325d450954fdc3f
|
|
4
|
+
data.tar.gz: 32aaedf19954e5e7abefc0866aa1b33e738dbb2f5d64778adfc04dddc838c039
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 705a9da9dd9164c92dc74cdb9080312f9e2c76e713f6677d0c3d8845e4ea25c0d9e58f57509731c947914aacf26d2392fdf35876dd33b527f934b6b57969f53a
|
|
7
|
+
data.tar.gz: c9936f93229bcee51aaf934248caab4ffd8b644df3330b19b04ed828c1909207c5205bb926c5ab97177afafe0f0831be1588f8672cda615796172596d8705136
|
|
@@ -1284,7 +1284,9 @@ class InputTag extends HTMLElement {
|
|
|
1284
1284
|
if (values.length === 0) {
|
|
1285
1285
|
formData.append(this.name, "");
|
|
1286
1286
|
}
|
|
1287
|
-
|
|
1287
|
+
// State is a JSON string, not the FormData: Firefox flattens a FormData to {} in the
|
|
1288
|
+
// session store and restores it as "[object Object]", but a string survives intact.
|
|
1289
|
+
this._internals.setFormValue(formData, JSON.stringify(values));
|
|
1288
1290
|
}
|
|
1289
1291
|
|
|
1290
1292
|
get name() {
|
|
@@ -1342,6 +1344,29 @@ class InputTag extends HTMLElement {
|
|
|
1342
1344
|
this._updateButtonContent();
|
|
1343
1345
|
}
|
|
1344
1346
|
|
|
1347
|
+
// On session restore Firefox hands back the state we saved (a JSON string of tag values).
|
|
1348
|
+
// Rebuild the tags from it so they survive a browser restart; before taggle is ready, stash
|
|
1349
|
+
// them for connectedCallback, and fall back to the current tags if the state is malformed.
|
|
1350
|
+
formStateRestoreCallback(state) {
|
|
1351
|
+
const values = this._restoredValues(state);
|
|
1352
|
+
if (values) {
|
|
1353
|
+
if (this._taggle && this.initialized) this.value = values;
|
|
1354
|
+
else this._pendingRestoredValues = values;
|
|
1355
|
+
} else {
|
|
1356
|
+
this._setFormValue(this._taggle ? this._taggle.getTagValues() : []);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
_restoredValues(state) {
|
|
1361
|
+
if (typeof state !== "string") return null
|
|
1362
|
+
try {
|
|
1363
|
+
const values = JSON.parse(state);
|
|
1364
|
+
return Array.isArray(values) ? values : null
|
|
1365
|
+
} catch {
|
|
1366
|
+
return null
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1345
1370
|
get options() {
|
|
1346
1371
|
const datalistId = this.getAttribute("list");
|
|
1347
1372
|
if(datalistId) {
|
|
@@ -1434,7 +1459,13 @@ class InputTag extends HTMLElement {
|
|
|
1434
1459
|
this._taggleInputTarget.setAttribute("data-turbo-permanent", true);
|
|
1435
1460
|
this._taggleInputTarget.addEventListener("keyup", e => this.keyup(e));
|
|
1436
1461
|
|
|
1437
|
-
// Set initial value after taggle is initialized
|
|
1462
|
+
// Set initial value after taggle is initialized. A session restore that arrived before
|
|
1463
|
+
// taggle was ready is applied here, so its tags and submission value both come back.
|
|
1464
|
+
if (this._pendingRestoredValues) {
|
|
1465
|
+
const restored = this._pendingRestoredValues;
|
|
1466
|
+
this._pendingRestoredValues = null;
|
|
1467
|
+
if (restored.length > 0) this._taggle.add(restored);
|
|
1468
|
+
}
|
|
1438
1469
|
this.value = this._taggle.getTagValues();
|
|
1439
1470
|
|
|
1440
1471
|
this.checkRequired();
|
data/input-tag/src/input-tag.js
CHANGED
|
@@ -288,7 +288,9 @@ class InputTag extends HTMLElement {
|
|
|
288
288
|
if (values.length === 0) {
|
|
289
289
|
formData.append(this.name, "");
|
|
290
290
|
}
|
|
291
|
-
|
|
291
|
+
// State is a JSON string, not the FormData: Firefox flattens a FormData to {} in the
|
|
292
|
+
// session store and restores it as "[object Object]", but a string survives intact.
|
|
293
|
+
this._internals.setFormValue(formData, JSON.stringify(values));
|
|
292
294
|
}
|
|
293
295
|
|
|
294
296
|
get name() {
|
|
@@ -346,6 +348,29 @@ class InputTag extends HTMLElement {
|
|
|
346
348
|
this._updateButtonContent()
|
|
347
349
|
}
|
|
348
350
|
|
|
351
|
+
// On session restore Firefox hands back the state we saved (a JSON string of tag values).
|
|
352
|
+
// Rebuild the tags from it so they survive a browser restart; before taggle is ready, stash
|
|
353
|
+
// them for connectedCallback, and fall back to the current tags if the state is malformed.
|
|
354
|
+
formStateRestoreCallback(state) {
|
|
355
|
+
const values = this._restoredValues(state)
|
|
356
|
+
if (values) {
|
|
357
|
+
if (this._taggle && this.initialized) this.value = values
|
|
358
|
+
else this._pendingRestoredValues = values
|
|
359
|
+
} else {
|
|
360
|
+
this._setFormValue(this._taggle ? this._taggle.getTagValues() : [])
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
_restoredValues(state) {
|
|
365
|
+
if (typeof state !== "string") return null
|
|
366
|
+
try {
|
|
367
|
+
const values = JSON.parse(state)
|
|
368
|
+
return Array.isArray(values) ? values : null
|
|
369
|
+
} catch {
|
|
370
|
+
return null
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
349
374
|
get options() {
|
|
350
375
|
const datalistId = this.getAttribute("list")
|
|
351
376
|
if(datalistId) {
|
|
@@ -438,7 +463,13 @@ class InputTag extends HTMLElement {
|
|
|
438
463
|
this._taggleInputTarget.setAttribute("data-turbo-permanent", true)
|
|
439
464
|
this._taggleInputTarget.addEventListener("keyup", e => this.keyup(e))
|
|
440
465
|
|
|
441
|
-
// Set initial value after taggle is initialized
|
|
466
|
+
// Set initial value after taggle is initialized. A session restore that arrived before
|
|
467
|
+
// taggle was ready is applied here, so its tags and submission value both come back.
|
|
468
|
+
if (this._pendingRestoredValues) {
|
|
469
|
+
const restored = this._pendingRestoredValues
|
|
470
|
+
this._pendingRestoredValues = null
|
|
471
|
+
if (restored.length > 0) this._taggle.add(restored)
|
|
472
|
+
}
|
|
442
473
|
this.value = this._taggle.getTagValues()
|
|
443
474
|
|
|
444
475
|
this.checkRequired()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { expect } from '@esm-bundle/chai'
|
|
2
|
+
import '../src/input-tag.js'
|
|
3
|
+
import { setupGlobalTestHooks, setupInputTag, waitForUpdate } from './lib/test-utils.js'
|
|
4
|
+
|
|
5
|
+
describe('Form state restore (Firefox session restore)', () => {
|
|
6
|
+
setupGlobalTestHooks()
|
|
7
|
+
|
|
8
|
+
it('saves the tag values as a JSON string form state, not the FormData Firefox flattens', async () => {
|
|
9
|
+
const inputTag = await setupInputTag(`
|
|
10
|
+
<form><input-tag name="labels[]" multiple>
|
|
11
|
+
<tag-option value="urgent">urgent</tag-option>
|
|
12
|
+
<tag-option value="backend">backend</tag-option>
|
|
13
|
+
</input-tag></form>
|
|
14
|
+
`)
|
|
15
|
+
await waitForUpdate()
|
|
16
|
+
|
|
17
|
+
const states = []
|
|
18
|
+
const internals = inputTag._internals
|
|
19
|
+
const real = internals.setFormValue.bind(internals)
|
|
20
|
+
internals.setFormValue = (value, state) => { states.push(state); real(value, state) }
|
|
21
|
+
|
|
22
|
+
inputTag.add('frontend')
|
|
23
|
+
await waitForUpdate()
|
|
24
|
+
|
|
25
|
+
const lastState = states[states.length - 1]
|
|
26
|
+
expect(lastState).to.be.a('string')
|
|
27
|
+
expect(JSON.parse(lastState)).to.eql(['urgent', 'backend', 'frontend'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('rebuilds the tags from a restored state string', async () => {
|
|
31
|
+
const inputTag = await setupInputTag(`<form><input-tag name="labels[]" multiple></input-tag></form>`)
|
|
32
|
+
await waitForUpdate()
|
|
33
|
+
expect(inputTag.tags).to.eql([])
|
|
34
|
+
|
|
35
|
+
inputTag.formStateRestoreCallback(JSON.stringify(['urgent', 'backend']))
|
|
36
|
+
await waitForUpdate()
|
|
37
|
+
|
|
38
|
+
expect(inputTag.tags).to.eql(['urgent', 'backend'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('falls back to the current tags when the restored state is the coerced "[object Object]"', async () => {
|
|
42
|
+
const inputTag = await setupInputTag(`
|
|
43
|
+
<form><input-tag name="labels[]" multiple>
|
|
44
|
+
<tag-option value="keep">keep</tag-option>
|
|
45
|
+
</input-tag></form>
|
|
46
|
+
`)
|
|
47
|
+
await waitForUpdate()
|
|
48
|
+
|
|
49
|
+
inputTag.formStateRestoreCallback('[object Object]')
|
|
50
|
+
await waitForUpdate()
|
|
51
|
+
|
|
52
|
+
expect(inputTag.tags).to.eql(['keep'])
|
|
53
|
+
})
|
|
54
|
+
})
|
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.7.
|
|
4
|
+
version: 0.7.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -177,6 +177,7 @@ files:
|
|
|
177
177
|
- input-tag/test/edge-cases.test.js
|
|
178
178
|
- input-tag/test/events.test.js
|
|
179
179
|
- input-tag/test/form-integration.test.js
|
|
180
|
+
- input-tag/test/form-state-restore.test.js
|
|
180
181
|
- input-tag/test/input-tag.test.js
|
|
181
182
|
- input-tag/test/lib/fail-only.mjs
|
|
182
183
|
- input-tag/test/lib/test-utils.js
|