diff --git a/src/index.ts b/src/index.ts index ede2c4d..4163a69 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,10 +20,20 @@ export function persistResumableFields(id: string, options?: PersistOptions): vo } } - const fields = resumables.filter(field => shouldResumeField(field)).map(field => [field.id, field.value]) + let fields = resumables.filter(field => shouldResumeField(field)).map(field => [field.id, field.value]) if (fields.length) { try { + const previouslyStoredFieldsJson = sessionStorage.getItem(key) + + if (previouslyStoredFieldsJson !== null) { + const previouslyStoredFields: string[][] = JSON.parse(previouslyStoredFieldsJson) + const fieldsNotReplaced: string[][] = previouslyStoredFields.filter(function (oldField) { + return !fields.some(field => field[0] === oldField[0]) + }) + fields = fields.concat(fieldsNotReplaced) + } + sessionStorage.setItem(key, JSON.stringify(fields)) } catch { // Ignore browser private mode error. @@ -46,13 +56,8 @@ export function restoreResumableFields(id: string, options?: RestoreOptions): vo if (!fields) return - try { - sessionStorage.removeItem(key) - } catch { - // Ignore browser private mode error. - } - const changedFields: Array = [] + const storedFieldsNotFound: string[][] = [] for (const [fieldId, value] of JSON.parse(fields)) { const resumeEvent = new CustomEvent('session:resume', { @@ -63,17 +68,30 @@ 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) && - field.value === field.defaultValue - ) { - field.value = value - changedFields.push(field) + if (field && (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement)) { + if (field.value === field.defaultValue) { + field.value = value + changedFields.push(field) + } + } else { + storedFieldsNotFound.push([fieldId, value]) } } } + // Some fields we want to restore are not always immediately present in the + // DOM and may be added later. This holds onto the values until + // they're needed. + if (storedFieldsNotFound.length === 0) { + try { + sessionStorage.removeItem(key) + } catch { + // Ignore browser private mode error. + } + } else { + sessionStorage.setItem(key, JSON.stringify(storedFieldsNotFound)) + } + setTimeout(function () { for (const el of changedFields) { el.dispatchEvent(new CustomEvent('change', {bubbles: true, cancelable: true})) diff --git a/test/test.js b/test/test.js index 9e39ce8..150ff5c 100644 --- a/test/test.js +++ b/test/test.js @@ -20,6 +20,38 @@ describe('session-resume', function () { assert.equal(document.querySelector('#my-second-field').value, 'second-field-value') }) + it('leaves unrestored values in session storage', function () { + sessionStorage.setItem( + 'session-resume:test-persist', + JSON.stringify([ + ['my-first-field', 'test2'], + ['non-existant-field', 'test3'] + ]) + ) + document.querySelector('#my-first-field').value = 'first-field-value' + document.querySelector('#my-second-field').value = 'second-field-value' + + restoreResumableFields('test-persist') + + assert.equal(document.querySelector('#my-first-field').value, 'test2') + assert.equal(document.querySelector('#my-second-field').value, 'second-field-value') + + // Some fields we want to restore are not always present in the DOM + // and may be added later. We hold onto the values until they're needed. + assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [ + ['non-existant-field', 'test3'] + ]) + }) + + it('removes the sessionStore key when all the fields were found', function () { + sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'test2']])) + restoreResumableFields('test-persist') + + // Some fields we want to restore are not always present in the DOM + // and may be added later. We hold onto the values until they're needed. + assert.equal(sessionStorage.getItem('session-resume:test-persist'), null) + }) + it('fires off session:resume events for changed fields', function () { const fieldsRestored = {} document.addEventListener('session:resume', function (event) { @@ -55,5 +87,32 @@ describe('session-resume', function () { ['my-second-field', 'test2'] ]) }) + + it('holds onto existing values in the store', function () { + sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['non-existant-field', 'test3']])) + document.querySelector('#my-first-field').value = 'test1' + document.querySelector('#my-second-field').value = 'test2' + + persistResumableFields('test-persist') + + assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [ + ['my-first-field', 'test1'], + ['my-second-field', 'test2'], + ['non-existant-field', 'test3'] + ]) + }) + + it('replaces old values with the latest field values', function () { + sessionStorage.setItem('session-resume:test-persist', JSON.stringify([['my-first-field', 'old data']])) + document.querySelector('#my-first-field').value = 'test1' + document.querySelector('#my-second-field').value = 'test2' + + persistResumableFields('test-persist') + + assert.deepEqual(JSON.parse(sessionStorage.getItem('session-resume:test-persist')), [ + ['my-first-field', 'test1'], + ['my-second-field', 'test2'] + ]) + }) }) })