From c113faaff177cd3a6150ad8724c8f3e449bbef01 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 29 Dec 2025 18:41:02 -0500 Subject: [PATCH] fix: #219 preserve JSX literal attribute expressions --- sandbox/components/counter-dsd.tsx | 2 -- src/jsx-loader.js | 27 +++++++++++++++++---------- test/cases/tsx/src/badge.tsx | 9 ++++++--- test/cases/tsx/tsx.spec.js | 13 ++++++++++++- 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/sandbox/components/counter-dsd.tsx b/sandbox/components/counter-dsd.tsx index 789f7e4e..5f9b7385 100644 --- a/sandbox/components/counter-dsd.tsx +++ b/sandbox/components/counter-dsd.tsx @@ -22,8 +22,6 @@ export default class CounterDsdTsx extends HTMLElement { return (
- {/* ` tag here to make sure types work */} - You have clicked {count} times diff --git a/src/jsx-loader.js b/src/jsx-loader.js index 621aaf45..32e397b3 100644 --- a/src/jsx-loader.js +++ b/src/jsx-loader.js @@ -141,17 +141,24 @@ function parseJsxElement(element, moduleContents = '') { // xxx={allTodos.length} > const { value } = attribute; const { expression } = value; + const expressionType = expression.type; - if (expression.type === 'Identifier') { - string += ` ${name}=$\{${expression.name}}`; - } - - if (expression.type === 'MemberExpression') { - if (expression.object.type === 'Identifier') { - if (expression.property.type === 'Identifier') { - string += ` ${name}=$\{${expression.object.name}.${expression.property.name}}`; + switch (expressionType) { + case 'Literal': + string += ` ${name}=${expression.raw}`; + break; + case 'Identifier': + string += ` ${name}=$\{${expression.name}}`; + break; + case 'MemberExpression': + if (expression.object.type === 'Identifier') { + if (expression.property.type === 'Identifier') { + string += ` ${name}=$\{${expression.object.name}.${expression.property.name}}`; + } } - } + break; + default: + break; } } } else { @@ -161,7 +168,7 @@ function parseJsxElement(element, moduleContents = '') { } } - string += openingElement.selfClosing ? '/>' : '>'; + string += openingElement.selfClosing ? ' />' : '>'; if (element.children.length > 0) { element.children.forEach((child) => parseJsxElement(child, moduleContents)); diff --git a/test/cases/tsx/src/badge.tsx b/test/cases/tsx/src/badge.tsx index 443c5c90..ece33070 100644 --- a/test/cases/tsx/src/badge.tsx +++ b/test/cases/tsx/src/badge.tsx @@ -35,9 +35,12 @@ export default class BadgeComponent extends HTMLElement { const conditionalText = predicate ? ' 🥳' : ''; return ( - - {count} - {conditionalText} + + Badge Icon + + {count} + {conditionalText} + ); } diff --git a/test/cases/tsx/tsx.spec.js b/test/cases/tsx/tsx.spec.js index 91af90ea..cf8a8f0c 100644 --- a/test/cases/tsx/tsx.spec.js +++ b/test/cases/tsx/tsx.spec.js @@ -48,12 +48,23 @@ describe('Run WCC For ', function () { // it('should handle a member expression', () => { const badge = dom.window.document.querySelectorAll('wcc-badge')[0]; - const span = badge.querySelectorAll('span')[0]; + const span = badge.querySelectorAll('span span')[0]; expect(badge.getAttribute('count')).to.be.equal('0'); expect(span.getAttribute('class')).to.be.equal('unmet'); expect(span.textContent.trim()).to.be.equal('0'); }); + + // Badge Icon + it('should preserve literal attribute expressions when using TS', () => { + const badge = dom.window.document.querySelectorAll('wcc-badge')[0]; + const img = badge.querySelectorAll('span img')[0]; + + expect(img.getAttribute('src')).to.be.equal('badge-icon.png'); + expect(img.getAttribute('alt')).to.be.equal('Badge Icon'); + expect(img.getAttribute('width')).to.be.equal('16'); + expect(img.getAttribute('height')).to.be.equal('16'); + }); }); describe('Event Handling', () => {