Skip to content

Commit

Permalink
Make element filtering configurable
Browse files Browse the repository at this point in the history
Introduce support for a `{ storageFilter: (field) => boolean }`
configuration override for calls to `persistResumableFields`. By
default, preserve the existing behavior of rejecting fields where
[value][] is equivalent to [defaultValue][].

This change is motivated by situations where an element's value is
impacted by events or packages that change its `[value]` attribute
directly.

For example, consider the following sample code:

```html
<input id="input" type="hidden" value="the default value">
<output id="output"></output>

<button>Change</button>

<script>
  const button = document.querySelector("button")
  const input = document.getElementById("input")
  const output = document.getElementById("output")

  output.textContent = input.defaultValue

  button.addEventListener("click", () => {
    input.setAttribute("value", "a new default value")
    output.textContent = input.defaultValue
  })
</script>
```

You can experiment with the code on [JSFiddle][].

Clicking the button changes the `<input>` element's `[value]` directly,
which has a side-effect of change its `.defaultValue` as well.

Within the context of `session-resume`, this means that any elements
impacted by side-effects of other code on the page will always be
omitted from being stored.

This is especially incompatible with `<trix-editor>` elements provided
by [Trix][] and [Action Text][].

[Trix]: https://trix-editor.org
[Action Text]: https://edgeguides.rubyonrails.org/action_text_overview.html

[value]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#value
[defaultValue]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#defaultvalue
[JSFiddle]: https://jsfiddle.net/1nwb4o23/
  • Loading branch information
seanpdoyle committed Oct 31, 2022
1 parent 1236700 commit 5f198c2
Showing 1 changed file with 11 additions and 3 deletions.
14 changes: 11 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
// Last submitted HTMLFormElement that will cause a browser navigation.
let submittedForm: HTMLFormElement | null = null

function shouldResumeField(field: HTMLInputElement | HTMLTextAreaElement): boolean {
return !!field.id && field.value !== field.defaultValue && field.form !== submittedForm
function shouldResumeField(field: HTMLInputElement | HTMLTextAreaElement, filter: StorageFilter): boolean {
return !!field.id && filter(field) && field.form !== submittedForm
}

function valueIsUnchanged(field: HTMLInputElement | HTMLTextAreaElement): boolean {
return field.value !== field.defaultValue
}

type StorageFilter = (field: HTMLInputElement | HTMLTextAreaElement) => boolean

type PersistOptions = {
selector?: string
keyPrefix?: string
storage?: Pick<Storage, 'getItem' | 'setItem'>
storageFilter?: StorageFilter
}

// Write all ids and values of the selected fields on the page into sessionStorage.
export function persistResumableFields(id: string, options?: PersistOptions): void {
const selector = options?.selector ?? '.js-session-resumable'
const keyPrefix = options?.keyPrefix ?? 'session-resume:'
const storageFilter = options?.storageFilter ?? valueIsUnchanged

let storage
try {
Expand All @@ -33,7 +41,7 @@ export function persistResumableFields(id: string, options?: PersistOptions): vo
}
}

let fields = resumables.filter(field => shouldResumeField(field)).map(field => [field.id, field.value])
let fields = resumables.filter(field => shouldResumeField(field, storageFilter)).map(field => [field.id, field.value])

if (fields.length) {
try {
Expand Down

0 comments on commit 5f198c2

Please sign in to comment.