Skip to content

Commit 368aee7

Browse files
committed
fix(vdom): render falsy attributes properly
fixes #1780
1 parent 2ef43d1 commit 368aee7

File tree

7 files changed

+52
-11
lines changed

7 files changed

+52
-11
lines changed

src/compiler/docs/readme/markdown-usage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as d from '../../../declarations';
2-
import { captializeFirstLetter } from '@utils';
2+
import { toTitleCase } from '@utils';
33

44
export function usageToMarkdown(usages: d.JsonDocsUsage) {
55
const content: string[] = [];
@@ -12,7 +12,7 @@ export function usageToMarkdown(usages: d.JsonDocsUsage) {
1212

1313
merged.forEach(({name, text}) => {
1414
content.push('');
15-
content.push(`### ${captializeFirstLetter(name)}`);
15+
content.push(`### ${toTitleCase(name)}`);
1616
content.push('');
1717
content.push(text);
1818
content.push('');

src/compiler/types/generate-event-types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import * as d from '../../declarations';
2-
import { captializeFirstLetter, getTextDocs, isDocsPublic } from '@utils';
2+
import { getTextDocs, isDocsPublic, toTitleCase } from '@utils';
33

44

55
export function generateEventTypes(cmpEvents: d.ComponentCompilerEvent[]): d.TypeInfo {
66
return cmpEvents.map(cmpEvent => {
7-
const name = `on${captializeFirstLetter(cmpEvent.name)}`;
7+
const name = `on${toTitleCase(cmpEvent.name)}`;
88
const type = (cmpEvent.complexType.original) ? `(event: CustomEvent<${cmpEvent.complexType.original}>) => void` : `CustomEvent`;
99
return {
1010
name,

src/mock-doc/node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ export class MockElement extends MockNode {
479479
if (attributes.caseInsensitive) {
480480
attrName = attrName.toLowerCase();
481481
}
482-
attr = new MockAttr(attrName, value);
482+
attr = new MockAttr(attrName, String(value));
483483
attributes.items.push(attr);
484484

485485
if (checkAttrChanged === true) {

src/mock-doc/test/element.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,25 @@ describe('element', () => {
214214
testNsAttributes(element);
215215
});
216216

217+
it('should cast attribute values to string', () => {
218+
const element = new MockHTMLElement(doc, 'div');
219+
element.setAttribute('prop1', null);
220+
element.setAttribute('prop2', undefined);
221+
element.setAttribute('prop3', 0);
222+
element.setAttribute('prop4', 1);
223+
element.setAttribute('prop5', 'hola');
224+
element.setAttribute('prop6', '');
225+
226+
expect(element.getAttribute('prop1')).toBe('null');
227+
expect(element.getAttribute('prop2')).toBe('undefined');
228+
expect(element.getAttribute('prop3')).toBe('0');
229+
expect(element.getAttribute('prop4')).toBe('1');
230+
expect(element.getAttribute('prop5')).toBe('hola');
231+
expect(element.getAttribute('prop6')).toBe('');
232+
233+
expect(element).toEqualHtml(`<div prop1=\"null\" prop2=\"undefined\" prop3=\"0\" prop4=\"1\" prop5=\"hola\" prop6></div>`);
234+
});
235+
217236
it('attributes are case insensible in HTMLElement', () => {
218237
const element = new MockHTMLElement(doc, 'div');
219238

src/runtime/test/render-vdom.spec.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,27 @@ describe('render-vdom', () => {
364364
});
365365
});
366366

367+
describe('native elements', () => {
368+
it('should render <input> correctly', async () => {
369+
@Component({ tag: 'cmp-a'})
370+
class CmpA {
371+
render() {
372+
return <input min={0} max={10} value={5}/>;
373+
}
374+
}
375+
376+
const { root } = await newSpecPage({
377+
components: [CmpA],
378+
html: `<cmp-a></cmp-a>`,
379+
});
380+
381+
expect(root).toEqualHtml(`
382+
<cmp-a>
383+
<input max=\"10\" min=\"0\" value=\"5\">
384+
</cmp-a>`);
385+
});
386+
});
387+
367388
describe('ref property', () => {
368389
it('should set on Host', async () => {
369390
@Component({ tag: 'cmp-a'})

src/runtime/vdom/set-accessor.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,13 @@ export const setAccessor = (elm: HTMLElement, memberName: string, oldValue: any,
8787
const isCustomElement = elm.tagName.includes('-');
8888
if ((isProp || (isComplex && newValue !== null)) && !isSvg) {
8989
try {
90-
if (isCustomElement) {
90+
if (!isCustomElement) {
91+
newValue = newValue == null ? '' : newValue;
92+
if ((elm as any)[memberName] !== newValue) {
93+
(elm as any)[memberName] = newValue;
94+
}
95+
} else {
9196
(elm as any)[memberName] = newValue;
92-
} else if ((elm as any)[memberName] !== newValue || '') {
93-
(elm as any)[memberName] = newValue || '';
9497
}
9598
} catch (e) {}
9699
}

src/utils/helpers.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ export const toDashCase = (str: string) => toLowerCase(str.replace(/([A-Z0-9])/g
99

1010
export const dashToPascalCase = (str: string) => toLowerCase(str).split('-').map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)).join('');
1111

12-
export const toTitleCase = (str: string) => str.charAt(0).toUpperCase() + str.substr(1);
13-
14-
export const captializeFirstLetter = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
12+
export const toTitleCase = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
1513

1614
export const noop = (): any => { /* noop*/ };
1715

0 commit comments

Comments
 (0)