Skip to content
This repository was archived by the owner on Apr 4, 2019. It is now read-only.

Implement special is attribute case. #415

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions packages/dom-helper/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ var canClone = doc && (function(document){

// This is not the namespace of the element, but of
// the elements inside that elements.
function interiorNamespace(element){
function interiorNamespace(element) {
if (
element &&
element.namespaceURI === svgNamespace &&
!svgHTMLIntegrationPoints[element.tagName]
) {
return svgNamespace;
} else {
return null;
}

return null;
}

// The HTML spec allows for "omitted start tags". These tags are optional
Expand Down Expand Up @@ -351,19 +351,18 @@ if (doc && doc.createElementNS) {
// Only opt into namespace detection if a contextualElement
// is passed.
prototype.createElement = function(tagName, contextualElement) {
var namespace = this.namespace;
let namespace = this.namespace;
if (contextualElement) {
if (tagName === 'svg') {
namespace = svgNamespace;
} else {
namespace = interiorNamespace(contextualElement);
namespace = typeof contextualElement === 'string' ? contextualElement : interiorNamespace(contextualElement);
}
}
if (namespace) {
return this.document.createElementNS(namespace, tagName);
} else {
return this.document.createElement(tagName);
}
return this.document.createElement(tagName);
};
prototype.setAttributeNS = function(element, namespace, name, value) {
element.setAttributeNS(namespace, name, String(value));
Expand Down
21 changes: 14 additions & 7 deletions packages/htmlbars-compiler/lib/fragment-javascript-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,21 @@ FragmentJavaScriptCompiler.prototype.createFragment = function() {
this.source.push(this.indent+' var '+el+' = dom.createDocumentFragment();\n');
};

FragmentJavaScriptCompiler.prototype.createElement = function(tagName) {
var el = 'el'+(++this.depth);
FragmentJavaScriptCompiler.prototype.createElement = function(tagName, contextualElement) {
let el = 'el'+(++this.depth);
if (tagName === 'svg') {
this.pushNamespaceFrame({namespace: svgNamespace, depth: this.depth});
}
this.ensureNamespace();
this.source.push(this.indent+' var '+el+' = dom.createElement('+string(tagName)+');\n');

let toPush = this.indent+' var '+el+' = dom.createElement('+string(tagName);
if (contextualElement) {
toPush += ', ' + string(contextualElement);
}
toPush += ');\n';

this.source.push(toPush);

if (svgHTMLIntegrationPoints[tagName]) {
this.pushNamespaceFrame({namespace: null, depth: this.depth});
}
Expand All @@ -60,12 +68,11 @@ FragmentJavaScriptCompiler.prototype.returnNode = function() {
};

FragmentJavaScriptCompiler.prototype.setAttribute = function(name, value, namespace) {
var el = 'el'+this.depth;
let el = 'el'+this.depth;
if (namespace) {
this.source.push(this.indent+' dom.setAttributeNS('+el+','+string(namespace)+','+string(name)+','+string(value)+');\n');
} else {
this.source.push(this.indent+' dom.setAttribute('+el+','+string(name)+','+string(value)+');\n');
return this.source.push(this.indent+' dom.setAttributeNS('+el+','+string(namespace)+','+string(name)+','+string(value)+');\n');
}
this.source.push(this.indent+' dom.setAttribute('+el+','+string(name)+','+string(value)+');\n');
};

FragmentJavaScriptCompiler.prototype.appendChild = function() {
Expand Down
12 changes: 11 additions & 1 deletion packages/htmlbars-compiler/lib/fragment-opcode-compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,17 @@ FragmentOpcodeCompiler.prototype.comment = function(comment) {
};

FragmentOpcodeCompiler.prototype.openElement = function(element) {
this.opcode('createElement', [element.tag]);
let contextualElement;

for (let i = 0; i < element.attributes.length; i++) {
let attr = element.attributes[i];
if (attr.name === 'is') {
contextualElement = attr.value.chars;
break;
}
}

this.opcode('createElement', [element.tag, contextualElement]);
forEach(element.attributes, this.attribute, this);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import FragmentJavaScriptCompiler from "../htmlbars-compiler/fragment-javascript-compiler";

QUnit.module('FragmentJavaScriptCompiler');

test('createElement correctly generates code for tagName with contextualElement', assert => {
assert.expect(1);
let fragmentCompiler = new FragmentJavaScriptCompiler();
fragmentCompiler.source.length = 0;
fragmentCompiler.depth = -1;
fragmentCompiler.indent = "";
fragmentCompiler.namespaceFrameStack = [{namespace: null, depth: null}];

const tagName = 'button';
const contextualElement = 'my-button';

fragmentCompiler.createElement(tagName, contextualElement);

const pattern = new RegExp(`dom\\.createElement\\("${tagName}", "${contextualElement}"\\)`);

assert.equal(pattern.test(fragmentCompiler.source[1]), true);
});

test('createElement correctly generates code for tagName without contextualElement', assert => {
assert.expect(1);
let fragmentCompiler = new FragmentJavaScriptCompiler();
fragmentCompiler.source.length = 0;
fragmentCompiler.depth = -1;
fragmentCompiler.indent = "";
fragmentCompiler.namespaceFrameStack = [{namespace: null, depth: null}];

const tagName = 'button';

fragmentCompiler.createElement(tagName);

const pattern = new RegExp(`dom\\.createElement\\("${tagName}"\\)`);

assert.equal(pattern.test(fragmentCompiler.source[1]), true);
});
32 changes: 32 additions & 0 deletions packages/htmlbars-compiler/tests/fragment-opcode-compiler-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import FragmentOpcodeCompiler from "../htmlbars-compiler/fragment-opcode-compiler";

QUnit.module('FragmentOpcodeCompiler');

test('openElement calls opcode with correct arguments when element has `is` attribute', assert => {
assert.expect(2);
let opcodeCompiler = new FragmentOpcodeCompiler();

const tagName = 'button';
const typeExtension = 'my-button';
const attributeName = 'is';

const element = {
tag: tagName,
attributes: [
{
name: attributeName,
value: {
chars: typeExtension
}
}
]
};

// monkey patch
opcodeCompiler.opcode = (opname, args) => {
assert.equal(opname, 'createElement');
assert.deepEqual(args, [ tagName, typeExtension ]);
};

opcodeCompiler.openElement(element);
});
16 changes: 16 additions & 0 deletions packages/htmlbars-compiler/tests/html-compiler-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,22 @@ test("Simple elements can have dashed attributes", function() {
equalTokens(fragment, '<div aria-label="foo">content</div>');
});

test("<button is=\"my-button\"> should generate a createElement('button', 'my-button') call", assert => {
assert.expect(2);

const extensionName = 'my-button';
const tagName = 'button';
const template = compile(`<${tagName} is="${extensionName}"></${tagName}>`);

let fragment = template.render({ isTrue: true }, env);
assert.equal( fragment.fragment.childNodes[0].namespaceURI, extensionName,
'button has namespace matching extension name');

const pattern = new RegExp(`dom\\.createElement\\("${tagName}", "${extensionName}"\\)`);

assert.equal(pattern.test(fragment.template.buildFragment), true);
});

QUnit.skip("Block params", function() {
registerHelper('a', function() {
this.yieldIn(compile("A({{yield 'W' 'X1'}})"));
Expand Down