From ade8ef4ae42b9cf2e9165e7fd2fbb43c273e3116 Mon Sep 17 00:00:00 2001 From: Theodore Brown Date: Sat, 25 Feb 2023 21:27:00 -0600 Subject: [PATCH] fix: bind null option and input values consistently Null and undefined `value` bindings should always be set to an empty string. This allows native browser validation of `required` fields to work as expected with placeholder options. Resolves #8312 --- .../render_dom/wrappers/Element/Attribute.ts | 2 +- .../samples/select-dynamic-value/expected.js | 7 +++-- .../_config.js | 28 +++++++++++++++++++ .../main.svelte | 11 ++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/runtime/samples/binding-select-null-placeholder/_config.js create mode 100644 test/runtime/samples/binding-select-null-placeholder/main.svelte diff --git a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts index 81a18983316a..1e651abd6aa6 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Attribute.ts @@ -171,7 +171,7 @@ export default class AttributeWrapper extends BaseAttributeWrapper { } if (is_indirectly_bound_value) { - const update_value = b`${element.var}.value = ${element.var}.__value;`; + const update_value = b`@set_input_value(${element.var}, ${element.var}.__value);`; block.chunks.hydrate.push(update_value); updater = b` diff --git a/test/js/samples/select-dynamic-value/expected.js b/test/js/samples/select-dynamic-value/expected.js index cd1ab1d81242..1d0896d6e822 100644 --- a/test/js/samples/select-dynamic-value/expected.js +++ b/test/js/samples/select-dynamic-value/expected.js @@ -8,7 +8,8 @@ import { insert, noop, safe_not_equal, - select_option + select_option, + set_input_value } from "svelte/internal"; function create_fragment(ctx) { @@ -24,9 +25,9 @@ function create_fragment(ctx) { option1 = element("option"); option1.textContent = "2"; option0.__value = "1"; - option0.value = option0.__value; + set_input_value(option0, option0.__value); option1.__value = "2"; - option1.value = option1.__value; + set_input_value(option1, option1.__value); }, m(target, anchor) { insert(target, select, anchor); diff --git a/test/runtime/samples/binding-select-null-placeholder/_config.js b/test/runtime/samples/binding-select-null-placeholder/_config.js new file mode 100644 index 000000000000..b453e6869a95 --- /dev/null +++ b/test/runtime/samples/binding-select-null-placeholder/_config.js @@ -0,0 +1,28 @@ +const items = [ { id: 'a' }, { id: 'b' } ]; + +export default { + props: { + foo: null, + items + }, + + test({ assert, component, target }) { + const select = target.querySelector( 'select' ); + const options = target.querySelectorAll( 'option' ); + + assert.equal( options[0].selected, true ); + assert.equal( options[0].disabled, true ); + assert.equal( options[1].selected, false ); + assert.equal( options[1].disabled, false ); + + // placeholder option value must be blank string for native required field validation + assert.equal( options[0].value, '' ); + assert.equal( select.checkValidity(), false ); + + component.foo = items[0]; + + assert.equal( options[0].selected, false ); + assert.equal( options[1].selected, true ); + assert.equal( select.checkValidity(), true ); + } +}; diff --git a/test/runtime/samples/binding-select-null-placeholder/main.svelte b/test/runtime/samples/binding-select-null-placeholder/main.svelte new file mode 100644 index 000000000000..65cab994957c --- /dev/null +++ b/test/runtime/samples/binding-select-null-placeholder/main.svelte @@ -0,0 +1,11 @@ + + +