diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts index 879bc93a9552..6ab35a83f882 100644 --- a/src/compiler/compile/render_dom/index.ts +++ b/src/compiler/compile/render_dom/index.ts @@ -561,7 +561,7 @@ export default function dom( computed: false, key: { type: 'Identifier', name: 'observedAttributes' }, value: x`function() { - return [${props.map(prop => x`"${prop.export_name}"`)}]; + return [${props.map(prop => x`"${prop.export_name.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`)}"`)}]; }` as FunctionExpression }); } diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index a8a500b25b02..875c4da31f41 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -167,7 +167,8 @@ if (typeof HTMLElement === 'function') { } attributeChangedCallback(attr, _oldValue, newValue) { - this[attr] = newValue; + const camelCase = attr.replace(/-./g, c => c[1].toUpperCase()); + this[camelCase] = newValue; } disconnectedCallback() { diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index c025313c157d..bc9002515b6f 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -340,6 +340,10 @@ export function set_svg_attributes(node: Element & ElementCSSInlineStyle, attrib } } +function kebab_case_to_camel_case(str: string): string { + return str.replace(/-./g, c => c[1].toUpperCase()); +} + export function set_custom_element_data_map(node, data_map: Record) { Object.keys(data_map).forEach((key) => { set_custom_element_data(node, key, data_map[key]); @@ -347,8 +351,9 @@ export function set_custom_element_data_map(node, data_map: Record export let name; + export let hyphenatedAttr;

name: {name}

+

hyphenated attribute: {hyphenatedAttr}

$$props: {JSON.stringify($$props)}

$$restProps: {JSON.stringify($$restProps)}

diff --git a/test/custom-elements/samples/$$props/test.js b/test/custom-elements/samples/$$props/test.js index 94cad865778c..a408e4f92eab 100644 --- a/test/custom-elements/samples/$$props/test.js +++ b/test/custom-elements/samples/$$props/test.js @@ -2,12 +2,13 @@ import * as assert from 'assert'; import './main.svelte'; export default function (target) { - target.innerHTML = ''; + target.innerHTML = ''; const el = target.querySelector('custom-element'); assert.htmlEqual(el.shadowRoot.innerHTML, `

name: world

-

$$props: {"name":"world","answer":"42","test":"svelte"}

-

$$restProps: {"answer":"42","test":"svelte"}

+

hyphenated attribute: galaxy

+

$$props: {"name":"world","answer":"42","test":"svelte","hyphenatedAttr":"galaxy","restAttr":"universe"}

+

$$restProps: {"answer":"42","test":"svelte","restAttr":"universe"}

`); } diff --git a/test/custom-elements/samples/props/main.svelte b/test/custom-elements/samples/props/main.svelte index cf47b436b590..75f9503e04ed 100644 --- a/test/custom-elements/samples/props/main.svelte +++ b/test/custom-elements/samples/props/main.svelte @@ -5,6 +5,7 @@ export let items = ['a', 'b', 'c']; export let flagged = false; + export let flaggedWithHyphen = false; - + diff --git a/test/custom-elements/samples/props/my-widget.svelte b/test/custom-elements/samples/props/my-widget.svelte index 970acf84b21b..04b5df170349 100644 --- a/test/custom-elements/samples/props/my-widget.svelte +++ b/test/custom-elements/samples/props/my-widget.svelte @@ -4,9 +4,13 @@ export let items = []; export let flag1 = false; export let flag2 = false; + export let flagWithHyphen1 = false; + export let flagWithHyphen2 = false;

{items.length} items

{items.join(', ')}

{flag1 ? 'flagged (dynamic attribute)' : 'not flagged'}

{flag2 ? 'flagged (static attribute)' : 'not flagged'}

+

{flagWithHyphen1 ? 'flagged with hyphen (dynamic attribute)' : 'not flagged'}

+

{flagWithHyphen2 ? 'flagged with hyphen (static attribute)' : 'not flagged'}

diff --git a/test/custom-elements/samples/props/test.js b/test/custom-elements/samples/props/test.js index 41ca77d29d02..df261cb9244f 100644 --- a/test/custom-elements/samples/props/test.js +++ b/test/custom-elements/samples/props/test.js @@ -11,17 +11,21 @@ export default function (target) { const el = target.querySelector('custom-element'); const widget = el.shadowRoot.querySelector('my-widget'); - const [p1, p2, p3, p4] = widget.shadowRoot.querySelectorAll('p'); + const [p1, p2, p3, p4, p5, p6] = widget.shadowRoot.querySelectorAll('p'); assert.equal(p1.textContent, '3 items'); assert.equal(p2.textContent, 'a, b, c'); assert.equal(p3.textContent, 'not flagged'); assert.equal(p4.textContent, 'flagged (static attribute)'); + assert.equal(p5.textContent, 'not flagged'); + assert.equal(p6.textContent, 'flagged with hyphen (static attribute)'); el.items = ['d', 'e', 'f', 'g', 'h']; el.flagged = true; + el.flaggedWithHyphen = true; assert.equal(p1.textContent, '5 items'); assert.equal(p2.textContent, 'd, e, f, g, h'); assert.equal(p3.textContent, 'flagged (dynamic attribute)'); + assert.equal(p5.textContent, 'flagged with hyphen (dynamic attribute)'); }