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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b7ac94a1fb550eb7e58297a8cc81307d54586f0fbbd078330b8b956707d341f
4
- data.tar.gz: fd32175f829a10338b99c87e14357bfa7093dd37a44cbb62eac3d601748579cb
3
+ metadata.gz: 5af78ac6af58f7abb2ec0f83972de2d3265bdf2e7fe2590d3325d450954fdc3f
4
+ data.tar.gz: 32aaedf19954e5e7abefc0866aa1b33e738dbb2f5d64778adfc04dddc838c039
5
5
  SHA512:
6
- metadata.gz: 9641455d386d85398760439770086aa56b9cdb3f6d3c7dd5529fdd729e106bcdd1b99376fcde56ed4283f43038f0de2c74a9e8cb72f84e14647ef9004ce38ec4
7
- data.tar.gz: d7aa23769d039fbbd4dd105f2c3f813d583be752c39d714f92b571f14f81c7a7aa7a8911c5c7cd09f00c0bc81cc25efa99fc4be5b18906b851e6c562abf219bb
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
- this._internals.setFormValue(formData);
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();
@@ -288,7 +288,9 @@ class InputTag extends HTMLElement {
288
288
  if (values.length === 0) {
289
289
  formData.append(this.name, "");
290
290
  }
291
- this._internals.setFormValue(formData);
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
+ })
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bard
4
4
  module TagField
5
- VERSION = "0.7.3"
5
+ VERSION = "0.7.4"
6
6
  end
7
7
  end
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.3
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-08-13 00:00:00.000000000 Z
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