Render void elements as self-closing tags (#2064)
* Render void elements as self-closing tags * changeset * nit: only check void element names if there is no child content * nit: only check void element names if there is no child content * add tests
This commit is contained in:
parent
b348ca6c9f
commit
5bda895fcb
5 changed files with 64 additions and 1 deletions
5
.changeset/rare-jars-dance.md
Normal file
5
.changeset/rare-jars-dance.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes an issue where void elements are rendered with opening and closing tags.
|
|
@ -7,6 +7,8 @@ import { serializeListValue } from './util.js';
|
|||
export { createMetadata } from './metadata.js';
|
||||
export type { Metadata } from './metadata';
|
||||
|
||||
const voidElementNames = /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i;
|
||||
|
||||
// INVESTIGATE:
|
||||
// 2. Less anys when possible and make it well known when they are needed.
|
||||
|
||||
|
@ -223,7 +225,9 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
|
|||
// This is a custom element without a renderer. Because of that, render it
|
||||
// as a string and the user is responsible for adding a script tag for the component definition.
|
||||
if (!html && typeof Component === 'string') {
|
||||
html = await renderAstroComponent(await render`<${Component}${spreadAttributes(props)}>${children}</${Component}>`);
|
||||
html = await renderAstroComponent(
|
||||
await render`<${Component}${spreadAttributes(props)}${(children == null || children == '') && voidElementNames.test(Component) ? `/>` : `>${children}</${Component}>`}`
|
||||
);
|
||||
}
|
||||
|
||||
// This is used to add polyfill scripts to the page, if the renderer needs them.
|
||||
|
|
|
@ -83,6 +83,26 @@ describe('Astro basics', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('Supports void elements whose name is a string (#2062)', async () => {
|
||||
const html = await fixture.readFile('/input/index.html');
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
// <Input />
|
||||
expect($('body > :nth-child(1)').prop('outerHTML')).to.equal('<input>');
|
||||
|
||||
// <Input type="password" />
|
||||
expect($('body > :nth-child(2)').prop('outerHTML')).to.equal('<input type="password">');
|
||||
|
||||
// <Input type="text" />
|
||||
expect($('body > :nth-child(3)').prop('outerHTML')).to.equal('<input type="text">');
|
||||
|
||||
// <Input type="select"><option>option</option></Input>
|
||||
expect($('body > :nth-child(4)').prop('outerHTML')).to.equal('<select><option>option</option></select>');
|
||||
|
||||
// <Input type="textarea">textarea</Input>
|
||||
expect($('body > :nth-child(5)').prop('outerHTML')).to.equal('<textarea>textarea</textarea>');
|
||||
});
|
||||
|
||||
describe('preview', () => {
|
||||
it('returns 200 for valid URLs', async () => {
|
||||
const result = await fixture.fetch('/');
|
||||
|
|
18
packages/astro/test/fixtures/astro-basic/src/components/Input.astro
vendored
Normal file
18
packages/astro/test/fixtures/astro-basic/src/components/Input.astro
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
const {
|
||||
type: initialType,
|
||||
...props
|
||||
} = {
|
||||
...Astro.props
|
||||
} as {
|
||||
[K: string]: any;
|
||||
};
|
||||
|
||||
const isSelect = /^select$/i.test(initialType);
|
||||
const isTextarea = /^textarea$/i.test(initialType);
|
||||
|
||||
const Control = isSelect ? 'select' : isTextarea ? 'textarea' : 'input';
|
||||
|
||||
if (Control === 'input' && initialType) props.type = initialType;
|
||||
---
|
||||
<Control {...props}>{'default' in Astro.slots ? <slot /> : null}</Control>
|
16
packages/astro/test/fixtures/astro-basic/src/pages/input.astro
vendored
Normal file
16
packages/astro/test/fixtures/astro-basic/src/pages/input.astro
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import Input from '../components/Input.astro';
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<!-- Head Stuff -->
|
||||
</head>
|
||||
<body>
|
||||
<Input />
|
||||
<Input type="password" />
|
||||
<Input type="text" />
|
||||
<Input type="select"><option>option</option></Input>
|
||||
<Input type="textarea">textarea</Input>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue