From c6ef52084e7bbd591f89392acabafa42f7266f6d Mon Sep 17 00:00:00 2001 From: Stefan Schweiger Date: Mon, 10 Jan 2022 16:04:17 +0100 Subject: [PATCH] Added correct void element parsing --- .../ComputesTemplateFromComponent.test.ts | 32 +++++++++++++++++++ .../ComputesTemplateFromComponent.ts | 29 ++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts index 62786ade02b3..3ab84b295072 100644 --- a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts +++ b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.test.ts @@ -77,6 +77,22 @@ describe('angular source decorator', () => { }); }); + describe('with component with void element and attribute selector', () => { + @Component({ + selector: 'input[foo]', + template: '', + }) + class VoidElementWithAttributeComponent {} + + it('should create without separate closing tag', async () => { + const component = VoidElementWithAttributeComponent; + const props = {}; + const argTypes: ArgTypes = {}; + const source = computesTemplateSourceFromComponent(component, props, argTypes); + expect(source).toEqual(``); + }); + }); + describe('with component with attribute and value only selector', () => { @Component({ selector: '[foo="bar"]', @@ -93,6 +109,22 @@ describe('angular source decorator', () => { }); }); + describe('with component with void element, attribute and value only selector', () => { + @Component({ + selector: 'input[foo="bar"]', + template: '', + }) + class VoidElementWithAttributeComponent {} + + it('should create and add attribute to template without separate closing tag', async () => { + const component = VoidElementWithAttributeComponent; + const props = {}; + const argTypes: ArgTypes = {}; + const source = computesTemplateSourceFromComponent(component, props, argTypes); + expect(source).toEqual(``); + }); + }); + describe('with component with class selector', () => { @Component({ selector: 'doc-button.foo', diff --git a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts index 297b63c57af5..c6703bf9e3c5 100644 --- a/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts +++ b/app/angular/src/client/preview/angular-beta/ComputesTemplateFromComponent.ts @@ -141,6 +141,26 @@ const buildTemplate = ( inputs: string, outputs: string ) => { + // https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#syntax-elements + const voidElements = [ + 'area', + 'base', + 'br', + 'col', + 'command', + 'embed', + 'hr', + 'img', + 'input', + 'keygen', + 'link', + 'meta', + 'param', + 'source', + 'track', + 'wbr', + ]; + const firstSelector = selector.split(',')[0]; const templateReplacers: [ string | RegExp, @@ -153,7 +173,14 @@ const buildTemplate = ( [/#([\w-]+)/, ` id="$1"`], [/((\.[\w-]+)+)/, (_, c) => ` class="${c.split`.`.join` `.trim()}"`], [/(\[.+?])/g, (_, a) => ` ${a.slice(1, -1)}`], - [/([\S]+)(.*)/, `<$1$2${inputs}${outputs}>${innerTemplate}`], + [ + /([\S]+)(.*)/, + (template, elementSelector) => { + return voidElements.some((element) => elementSelector === element) + ? template.replace(/([\S]+)(.*)/, `<$1$2${inputs}${outputs} />`) + : template.replace(/([\S]+)(.*)/, `<$1$2${inputs}${outputs}>${innerTemplate}`); + }, + ], ]; return templateReplacers.reduce(