diff --git a/README.md b/README.md index 9bee76e..7f8b587 100644 --- a/README.md +++ b/README.md @@ -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"`) diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..bfa0126 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,87 @@ + + + + + session-resume demo + + + +

session-resume

+ +

Test by filling out the form and then refreshing the page or navigating away and back.

+ +
+

+ +

+

+ +

+

+ +

+

+ +

+
+ Checkboxes +
+ +
+ +
+ + +
+
+
+ Radio +
+ +
+ + +
+
+ + +
+ + + + diff --git a/src/index.ts b/src/index.ts index 2852d45..4cab310 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 @@ -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 @@ -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) } diff --git a/test/test.js b/test/test.js index adcf236..cd91d95 100644 --- a/test/test.js +++ b/test/test.js @@ -6,8 +6,11 @@ describe('session-resume', function () { // eslint-disable-next-line github/no-inner-html document.body.innerHTML = `
- - + + + + +
` window.addEventListener('submit', sessionStorage.setForm, {capture: true}) @@ -15,11 +18,21 @@ describe('session-resume', function () { 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 () { @@ -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')) }) @@ -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 () { @@ -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'] ]) @@ -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'] ]) @@ -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'] @@ -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'] ])