Skip to content

Commit bfb858e

Browse files
authored
Merge pull request #30 from helen/add/checked-support
Add support for checkboxes and radio buttons
2 parents 30de742 + 0147534 commit bfb858e

File tree

4 files changed

+143
-12
lines changed

4 files changed

+143
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ The `restoreResumableFields(id: string, options)` function supports optional con
6161
The `persistResumableFields(id: string, options)` function supports optional configurations:
6262

6363
* `storage:` - [`Storage`][] instance (defaults to [`window.sessionStorage`][])
64-
* `storageFilter:` - `(field: HTMLInputElement | HTMLTextAreaElement) => boolean` predicate to determine whether or not to store a field (defaults to `(field) => field.value !== field.defaultValue`)
64+
* `storageFilter:` - `(field: HTMLInputElement | HTMLTextAreaElement) => boolean` predicate to determine whether or not to store a field (defaults to `(field) => field.checked !== field.defaultChecked)` for checkbox and radio buttons, `(field) => field.value !== field.defaultValue` otherwise)
6565
* `keyPrefix:` - `string` prepended onto the storage key (defaults to `"session-resume"`)
6666
* `scope:` - `ParentNode` used to query field elements (defaults to `document`)
6767
* `selector:` - `string` used to query field elements (defaults to `".js-session-resumable"`)

examples/index.html

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>session-resume demo</title>
6+
<style>
7+
fieldset {
8+
margin-bottom: 1em;
9+
}
10+
</style>
11+
</head>
12+
<body>
13+
<h1>session-resume</h1>
14+
15+
<h2>Test by filling out the form and then refreshing the page or <a href="https://github.com">navigating away</a> and back.</h2>
16+
17+
<form>
18+
<p>
19+
<label>Text input<br>
20+
<input type="text" id="new-title" class="js-session-resumable"/>
21+
</label>
22+
</p>
23+
<p>
24+
<label>Email<br>
25+
<input type="email" id="new-email" class="js-session-resumable"/>
26+
</label>
27+
</p>
28+
<p>
29+
<label>Number<br>
30+
<input type="number" id="new-phone" class="js-session-resumable"/>
31+
</label>
32+
</p>
33+
<p>
34+
<label>Textarea<br>
35+
<textarea id="new-comment" class="js-session-resumable"></textarea>
36+
</label>
37+
</p>
38+
<fieldset>
39+
<legend>Checkboxes</legend>
40+
<div>
41+
<input type="checkbox" id="ice-cream-vanilla" name="ice-cream" value="vanilla" class="js-session-resumable" checked />
42+
<label for="ice-cream-vanilla">Vanilla (checked)</label><br>
43+
<input type="checkbox" id="ice-cream-chocolate" name="ice-cream" value="chocolate" class="js-session-resumable" />
44+
<label for="ice-cream-chocolate">Chocolate</label><br>
45+
<input type="checkbox" id="ice-cream-strawberry" name="ice-cream" value="strawberry" class="js-session-resumable" />
46+
<label for="ice-cream-strawberry">Strawberry</label>
47+
</div>
48+
</fieldset>
49+
<fieldset>
50+
<legend>Radio</legend>
51+
<div>
52+
<input type="radio" id="cake" name="cake-pie" value="cake" class="js-session-resumable" />
53+
<label for="cake">Cake</label><br>
54+
<input type="radio" id="pie" name="cake-pie" value="pie" class="js-session-resumable" />
55+
<label for="pie">Pie</label>
56+
</div>
57+
</fieldset>
58+
59+
<button id="save-data" type="button">Set sessionStorage</button>
60+
</form>
61+
62+
<script type="module">
63+
// import {persistResumableFields, restoreResumableFields, setForm} from '../dist/index.js'
64+
import {persistResumableFields, restoreResumableFields, setForm} from 'https://unpkg.com/@github/session-resume/dist/index.js'
65+
66+
// Listen for all form submit events and to see if their default submission
67+
// behavior is invoked.
68+
window.addEventListener('submit', setForm, {capture: true})
69+
70+
// Resume field content on regular page loads.
71+
window.addEventListener('pageshow', function() {
72+
restoreResumableFields('session-resume-demo')
73+
})
74+
75+
// Persist resumable fields when page is unloaded
76+
window.addEventListener('pagehide', function() {
77+
persistResumableFields('session-resume-demo')
78+
})
79+
80+
document.addEventListener('click', function(event) {
81+
if (event.target.id === 'save-data') {
82+
persistResumableFields('session-resume-demo')
83+
}
84+
})
85+
</script>
86+
</body>
87+
</html>

src/index.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ function shouldResumeField(field: HTMLInputElement | HTMLTextAreaElement, filter
66
}
77

88
function valueIsUnchanged(field: HTMLInputElement | HTMLTextAreaElement): boolean {
9-
return field.value !== field.defaultValue
9+
if (isHTMLCheckableInputElement(field)) {
10+
return field.checked !== field.defaultChecked
11+
} else {
12+
return field.value !== field.defaultValue
13+
}
1014
}
1115

1216
type StorageFilter = (field: HTMLInputElement | HTMLTextAreaElement) => boolean
@@ -29,6 +33,16 @@ type PersistOptions = (PersistOptionsWithSelector | PersistOptionsWithFields) &
2933
storageFilter?: StorageFilter
3034
}
3135

36+
type HTMLCheckableInputElement = HTMLInputElement & {
37+
type: 'checkbox' | 'radio'
38+
}
39+
40+
function isHTMLCheckableInputElement(
41+
field: HTMLInputElement | HTMLTextAreaElement
42+
): field is HTMLCheckableInputElement {
43+
return field instanceof HTMLInputElement && /checkbox|radio/.test(field.type)
44+
}
45+
3246
// Write all ids and values of the selected fields on the page into sessionStorage.
3347
export function persistResumableFields(id: string, options?: PersistOptions): void {
3448
const scope = options?.scope ?? document
@@ -112,7 +126,10 @@ export function restoreResumableFields(id: string, options?: RestoreOptions): vo
112126
if (document.dispatchEvent(resumeEvent)) {
113127
const field = document.getElementById(fieldId)
114128
if (field && (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement)) {
115-
if (field.value === field.defaultValue) {
129+
if (isHTMLCheckableInputElement(field)) {
130+
field.checked = !field.defaultChecked
131+
changedFields.push(field)
132+
} else if (field.value === field.defaultValue) {
116133
field.value = value
117134
changedFields.push(field)
118135
}

test/test.js

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,33 @@ describe('session-resume', function () {
66
// eslint-disable-next-line github/no-inner-html
77
document.body.innerHTML = `
88
<form>
9-
<input id="my-first-field" value="first-field-value" class="js-session-resumable" />
10-
<input id="my-second-field" value="second-field-value" class="js-session-resumable" />
9+
<input id="my-first-field" type="text" value="first-field-value" class="js-session-resumable" />
10+
<input id="my-second-field" type="text" value="second-field-value" class="js-session-resumable" />
11+
<input id="my-first-checkbox" type="checkbox" value="first-checkbox-value" class="js-session-resumable" />
12+
<input id="my-second-checkbox" type="checkbox" value="second-checkbox-value" class="js-session-resumable" />
13+
<input id="my-checked-checkbox" type="checkbox" value="checked-checkbox-value" class="js-session-resumable" checked />
1114
</form>
1215
`
1316
window.addEventListener('submit', sessionStorage.setForm, {capture: true})
1417
})
1518

1619
describe('restoreResumableFields', function () {
1720
it('restores fields values from session storage by default', function () {
18-
sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'test2']]))
21+
sessionStorage.setItem(
22+
'session-resume:test-persist',
23+
JSON.stringify([
24+
['my-first-field', 'test2'],
25+
['my-first-checkbox', 'first-checkbox-value'],
26+
['my-checked-checkbox', 'checked-checkbox-value']
27+
])
28+
)
1929
restoreResumableFields('test-persist')
2030

2131
assert.equal(document.querySelector('#my-first-field').value, 'test2')
2232
assert.equal(document.querySelector('#my-second-field').value, 'second-field-value')
33+
assert.equal(document.querySelector('#my-first-checkbox').checked, true)
34+
assert.equal(document.querySelector('#my-second-checkbox').checked, false)
35+
assert.equal(document.querySelector('#my-checked-checkbox').checked, false)
2336
})
2437

2538
it('uses a Storage object when provided as an option', function () {
@@ -84,8 +97,8 @@ describe('session-resume', function () {
8497
assert.deepEqual(fieldsRestored, {'my-first-field': 'test2'})
8598
})
8699

87-
it('fires off change for changed fields', function (done) {
88-
for (const input of document.querySelectorAll('input')) {
100+
it('fires off change for changed input[type=text] fields', function (done) {
101+
for (const input of document.querySelectorAll('input[type=text]')) {
89102
input.addEventListener('change', function (event) {
90103
done(assert.equal(event.target.id, 'my-first-field'))
91104
})
@@ -94,6 +107,20 @@ describe('session-resume', function () {
94107
sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'test2']]))
95108
restoreResumableFields('test-persist')
96109
})
110+
111+
it('fires off change for changed input[type=checkbox] fields', function (done) {
112+
for (const input of document.querySelectorAll('input[type=checkbox]')) {
113+
input.addEventListener('change', function (event) {
114+
done(assert.equal(event.target.id, 'my-first-checkbox'))
115+
})
116+
}
117+
118+
sessionStorage.setItem(
119+
'session-resume:test-persist',
120+
JSON.stringify([['my-first-checkbox', 'first-checkbox-value']])
121+
)
122+
restoreResumableFields('test-persist')
123+
})
97124
})
98125

99126
describe('persistResumableFields', function () {
@@ -102,7 +129,7 @@ describe('session-resume', function () {
102129
document.querySelector('#my-second-field').value = 'test2'
103130
persistResumableFields('test-persist')
104131

105-
assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
132+
assert.includeDeepMembers(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
106133
['my-first-field', 'test1'],
107134
['my-second-field', 'test2']
108135
])
@@ -124,7 +151,7 @@ describe('session-resume', function () {
124151

125152
persistResumableFields('test-persist', {storage: fakeStorage})
126153

127-
assert.deepEqual(JSON.parse(fakeStorage.getItem('session-resume:test-persist')), [
154+
assert.includeDeepMembers(JSON.parse(fakeStorage.getItem('session-resume:test-persist')), [
128155
['my-first-field', 'test1'],
129156
['my-second-field', 'test2']
130157
])
@@ -137,7 +164,7 @@ describe('session-resume', function () {
137164

138165
persistResumableFields('test-persist')
139166

140-
assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
167+
assert.includeDeepMembers(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
141168
['my-first-field', 'test1'],
142169
['my-second-field', 'test2'],
143170
['non-existant-field', 'test3']
@@ -151,7 +178,7 @@ describe('session-resume', function () {
151178

152179
persistResumableFields('test-persist')
153180

154-
assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
181+
assert.includeDeepMembers(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
155182
['my-first-field', 'test1'],
156183
['my-second-field', 'test2']
157184
])

0 commit comments

Comments
 (0)