ciscoheat / sveltekit-superforms

Making SvelteKit forms a pleasure to use!
https://superforms.rocks
MIT License
2.25k stars 65 forks source link

Inconsistent `length` in textarea prevents superforms constraints as source of truth #253

Open fcrozatier opened 1 year ago

fcrozatier commented 1 year ago

Description As per HTML spec a textarea normalizes new lines to \n in the browser but to \r\n when the form is submitted. So if you insert new lines and use a naive max in your schema you can pass browser validation but fail server validation.

This is unwanted and can be confusing for users if you have a character counter.

As an alternative one can refine the string schema, but then the constraint doesn't know about the maxlength which has to be added manually?

Refs:

For historical reasons, the element's value is normalized in three different ways for three different purposes. The raw value is the value as it was originally set. It is not normalized. The API value is the value used in the value IDL attribute, textLength IDL attribute, and by the maxlength and minlength content attributes. It is normalized so that line breaks use U+000A LINE FEED (LF) characters. Finally, there is the value, as used in form submission and other processing models in this specification. It is normalized as for the API value, and in addition, if necessary given the element's wrap attribute, additional line breaks are inserted to wrap the text at the given width. https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element

As with all MIME transmissions, "CR LF" (i.e., `%0D%0A') is used to separate lines of data. https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

If applicable, a MRE

repro repo

stackblitz https://stackblitz.com/edit/sveltekit-superforms-1-testing-xxfpiy?file=src%2Froutes%2F%2Bpage.server.ts,src%2Froutes%2F%2Bpage.svelte

ciscoheat commented 1 year ago

What do you suggest as a fix or workaround?

fcrozatier commented 1 year ago

Well one workaround is to not use max for textarea and use a refinement as in the repro here https://github.com/fcrozatier/superform-textarea/blob/main/src/routes/%2Bpage.server.ts#L9

But then you also have to add a maxlength manually.

So it's not really satisfying but at least there no browser/server validation mismatch that could confuse the end user.

Maybe there's a better approach?

ciscoheat commented 1 year ago

I don't know, since it works in general for input fields, which are the most common case, it's probably enough to make a note in the documentation about this.

fcrozatier commented 1 year ago

I don't know, since it works in general for input fields

Because we can't have new lines in an input field

Since this inconsistency is inevitable (html spec) and only concerns texteara because of new lines maybe it would make sense to have a .text validation in zod? I'll ask them

But for now yeah maybe a doc note is all we can do.

ciscoheat commented 1 year ago

Good idea, Zod is a better place for this. Thank you for making the effort! :)

mxdvl commented 1 year ago

I was able to get a consistent behaviour by adding a text normalisation function, like so:

/** One has to take into account the \r characters added by the new line normalization, as per html specs */
const normalise = (text: string) => text.replaceAll('\r\n', '\n');

const schema = z.object({
  textarea: z.string().transform(normalise).pipe(z.string().max(5))
});

Except for Safari which correctly reports the textarea's length with LF for new lines, but incorrectly checks against maxLength using CRLF. Fixed by https://github.com/WebKit/WebKit/commit/2252898e2468994720be25176170e00bc7b26ca3 in Safari Technology Preview