Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ns/add/checked support #56

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The `restoreResumableFields(id: string, options)` function supports optional con
The `persistResumableFields(id: string, options)` function supports optional configurations:

* `storage:` - [`Storage`][] instance (defaults to [`window.sessionStorage`][])
* `storageFilter:` - `(field: HTMLInputElement | HTMLTextAreaElement) => boolean` predicate to determine whether or not to store a field (defaults to `(field) => field.value !== field.defaultValue`)
* `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)
* `keyPrefix:` - `string` prepended onto the storage key (defaults to `"session-resume"`)
* `scope:` - `ParentNode` used to query field elements (defaults to `document`)
* `selector:` - `string` used to query field elements (defaults to `".js-session-resumable"`)
Expand Down
87 changes: 87 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>session-resume demo</title>
<style>
fieldset {
margin-bottom: 1em;
}
</style>
</head>
<body>
<h1>session-resume</h1>

<h2>Test by filling out the form and then refreshing the page or <a href="https://github.com">navigating away</a> and back.</h2>

<form>
<p>
<label>Text input<br>
<input type="text" id="new-title" class="js-session-resumable"/>
</label>
</p>
<p>
<label>Email<br>
<input type="email" id="new-email" class="js-session-resumable"/>
</label>
</p>
<p>
<label>Number<br>
<input type="number" id="new-phone" class="js-session-resumable"/>
</label>
</p>
<p>
<label>Textarea<br>
<textarea id="new-comment" class="js-session-resumable"></textarea>
</label>
</p>
<fieldset>
<legend>Checkboxes</legend>
<div>
<input type="checkbox" id="ice-cream-vanilla" name="ice-cream" value="vanilla" class="js-session-resumable" checked />
<label for="ice-cream-vanilla">Vanilla (checked)</label><br>
<input type="checkbox" id="ice-cream-chocolate" name="ice-cream" value="chocolate" class="js-session-resumable" />
<label for="ice-cream-chocolate">Chocolate</label><br>
<input type="checkbox" id="ice-cream-strawberry" name="ice-cream" value="strawberry" class="js-session-resumable" />
<label for="ice-cream-strawberry">Strawberry</label>
</div>
</fieldset>
<fieldset>
<legend>Radio</legend>
<div>
<input type="radio" id="cake" name="cake-pie" value="cake" class="js-session-resumable" />
<label for="cake">Cake</label><br>
<input type="radio" id="pie" name="cake-pie" value="pie" class="js-session-resumable" />
<label for="pie">Pie</label>
</div>
</fieldset>

<button id="save-data" type="button">Set sessionStorage</button>
</form>

<script type="module">
// import {persistResumableFields, restoreResumableFields, setForm} from '../dist/index.js'
import {persistResumableFields, restoreResumableFields, setForm} from 'https://unpkg.com/@github/session-resume/dist/index.js'

// Listen for all form submit events and to see if their default submission
// behavior is invoked.
window.addEventListener('submit', setForm, {capture: true})

// Resume field content on regular page loads.
window.addEventListener('pageshow', function() {
restoreResumableFields('session-resume-demo')
})

// Persist resumable fields when page is unloaded
window.addEventListener('pagehide', function() {
persistResumableFields('session-resume-demo')
})

document.addEventListener('click', function(event) {
if (event.target.id === 'save-data') {
persistResumableFields('session-resume-demo')
}
})
</script>
</body>
</html>
21 changes: 19 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ function shouldResumeField(field: HTMLInputElement | HTMLTextAreaElement, filter
}

function valueIsUnchanged(field: HTMLInputElement | HTMLTextAreaElement): boolean {
return field.value !== field.defaultValue
if (isHTMLCheckableInputElement(field)) {
return field.checked !== field.defaultChecked
} else {
return field.value !== field.defaultValue
}
}

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

type HTMLCheckableInputElement = HTMLInputElement & {
type: 'checkbox' | 'radio'
}

function isHTMLCheckableInputElement(
field: HTMLInputElement | HTMLTextAreaElement
): field is HTMLCheckableInputElement {
return field instanceof HTMLInputElement && /checkbox|radio/.test(field.type)
}

// Write all ids and values of the selected fields on the page into sessionStorage.
export function persistResumableFields(id: string, options?: PersistOptions): void {
const scope = options?.scope ?? document
Expand Down Expand Up @@ -112,7 +126,10 @@ export function restoreResumableFields(id: string, options?: RestoreOptions): vo
if (document.dispatchEvent(resumeEvent)) {
const field = document.getElementById(fieldId)
if (field && (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement)) {
if (field.value === field.defaultValue) {
if (isHTMLCheckableInputElement(field)) {
field.checked = !field.defaultChecked
changedFields.push(field)
} else if (field.value === field.defaultValue) {
field.value = value
changedFields.push(field)
}
Expand Down
45 changes: 36 additions & 9 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,33 @@ describe('session-resume', function () {
// eslint-disable-next-line github/no-inner-html
document.body.innerHTML = `
<form>
<input id="my-first-field" value="first-field-value" class="js-session-resumable" />
<input id="my-second-field" value="second-field-value" class="js-session-resumable" />
<input id="my-first-field" type="text" value="first-field-value" class="js-session-resumable" />
<input id="my-second-field" type="text" value="second-field-value" class="js-session-resumable" />
<input id="my-first-checkbox" type="checkbox" value="first-checkbox-value" class="js-session-resumable" />
<input id="my-second-checkbox" type="checkbox" value="second-checkbox-value" class="js-session-resumable" />
<input id="my-checked-checkbox" type="checkbox" value="checked-checkbox-value" class="js-session-resumable" checked />
</form>
`
window.addEventListener('submit', sessionStorage.setForm, {capture: true})
})

describe('restoreResumableFields', function () {
it('restores fields values from session storage by default', function () {
sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'test2']]))
sessionStorage.setItem(
'session-resume:test-persist',
JSON.stringify([
['my-first-field', 'test2'],
['my-first-checkbox', 'first-checkbox-value'],
['my-checked-checkbox', 'checked-checkbox-value']
])
)
restoreResumableFields('test-persist')

assert.equal(document.querySelector('#my-first-field').value, 'test2')
assert.equal(document.querySelector('#my-second-field').value, 'second-field-value')
assert.equal(document.querySelector('#my-first-checkbox').checked, true)
assert.equal(document.querySelector('#my-second-checkbox').checked, false)
assert.equal(document.querySelector('#my-checked-checkbox').checked, false)
})

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

it('fires off change for changed fields', function (done) {
for (const input of document.querySelectorAll('input')) {
it('fires off change for changed input[type=text] fields', function (done) {
for (const input of document.querySelectorAll('input[type=text]')) {
input.addEventListener('change', function (event) {
done(assert.equal(event.target.id, 'my-first-field'))
})
Expand All @@ -94,6 +107,20 @@ describe('session-resume', function () {
sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'test2']]))
restoreResumableFields('test-persist')
})

it('fires off change for changed input[type=checkbox] fields', function (done) {
for (const input of document.querySelectorAll('input[type=checkbox]')) {
input.addEventListener('change', function (event) {
done(assert.equal(event.target.id, 'my-first-checkbox'))
})
}

sessionStorage.setItem(
'session-resume:test-persist',
JSON.stringify([['my-first-checkbox', 'first-checkbox-value']])
)
restoreResumableFields('test-persist')
})
})

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

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

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

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

persistResumableFields('test-persist')

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

persistResumableFields('test-persist')

assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
assert.includeDeepMembers(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [
['my-first-field', 'test1'],
['my-second-field', 'test2']
])
Expand Down