Skip to content

Commit

Permalink
fix(html): Support for external custom elements with shadow dom set i…
Browse files Browse the repository at this point in the history
…n constructor

The reason is that ShadyDOM has buggy support for custom elements, which create shadowRoot in constructor and are imported in the render process.
  • Loading branch information
smalluban committed Jun 6, 2018
1 parent fcb3590 commit fd16d8a
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 35 deletions.
23 changes: 12 additions & 11 deletions src/html/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import { compile, createSignature } from './template';
import { compile } from './template';
import * as methods from './methods';
import resolve from './resolve';

const templates = new Map();
const updates = new Map();

function create(parts, args, isSVG) {
let signature = createSignature(parts);
if (isSVG) signature = `<svg>${signature}</svg>`;
const update = (host, target = host) => {
const id = `${isSVG ? 'svg:' : ''}${parts.join('')}`;
let render = updates.get(id);

let render = templates.get(signature);
if (!render) {
render = compile(parts, isSVG);
updates.set(id, render);
}

if (!render) {
render = compile(signature, parts, isSVG);
templates.set(signature, render);
}
render(host, target, args);
};

const fn = (host, target = host) => render(host, target, args);
return Object.assign(fn, methods);
return Object.assign(update, methods);
}

export function html(parts, ...args) {
Expand Down
50 changes: 34 additions & 16 deletions src/html/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ function applyShadyCSS(template, tagName) {

if (!clone) {
clone = document.createElement('template');
clone.content.appendChild(document.importNode(template.content, true));
clone.content.appendChild(template.content.cloneNode(true));

map.set(tagName, clone);

Expand Down Expand Up @@ -232,16 +232,6 @@ function getPropertyName(string) {
return string.replace(/\s*=\s*['"]*$/g, '').split(' ').pop();
}

function createWalker(node) {
return document.createTreeWalker(
node,
// eslint-disable-next-line no-bitwise
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
null,
false,
);
}

function replaceComments(fragment) {
const iterator = document.createNodeIterator(fragment, NodeFilter.SHOW_COMMENT, null, false);
let node;
Expand All @@ -254,11 +244,42 @@ function replaceComments(fragment) {
}
}

const createWalker = typeof window.ShadyDOM === 'object' && window.ShadyDOM.inUse ?
(context) => {
let node;

return {
get currentNode() { return node; },
nextNode() {
if (node === undefined) {
node = context.childNodes[0];
} else if (node.childNodes.length) {
node = node.childNodes[0];
} else if (node.nextSibling) {
node = node.nextSibling;
} else {
node = node.parentNode.nextSibling;
}

return !!node;
},
};
} : node => document.createTreeWalker(
node,
// eslint-disable-next-line no-bitwise
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
null,
false,
);

const container = document.createElement('div');
export function compile(signature, rawParts, isSVG) {
export function compile(rawParts, isSVG) {
const template = document.createElement('template');
const parts = [];

let signature = createSignature(rawParts);
if (isSVG) signature = `<svg>${signature}</svg>`;

if (IS_IE) {
template.innerHTML = signature;
} else {
Expand Down Expand Up @@ -384,11 +405,8 @@ export function compile(signature, rawParts, isSVG) {
}
}

let index = 0;

data.markers.forEach(([node, fn]) => {
data.markers.forEach(([node, fn], index) => {
fn(host, node, args[index], data);
index += 1;
});
};
}
9 changes: 3 additions & 6 deletions test/runner.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'core-js/shim';
import '../shim';

// Set dynamic env variable
Expand All @@ -12,14 +11,12 @@ window.test = (html) => {
const wrapper = document.createElement('div');
document.body.appendChild(wrapper);

wrapper.appendChild(document.importNode(template.content, true));
wrapper.appendChild(template.content.cloneNode(true));
const promise = spec(wrapper.children[0]);

if (promise) {
promise.then(() => document.body.removeChild(wrapper));
} else {
Promise.resolve(promise).then(() => {
document.body.removeChild(wrapper);
}
});
};
};

Expand Down
4 changes: 2 additions & 2 deletions test/spec/children.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ describe('children:', () => {
<test-dynamic-parent>
<test-dynamic-child name="one"></test-dynamic-child>
${items.map(name => html`
<test-dynamic-child name="${name}">${name}</test-dynamic-child>
`)}
<test-dynamic-child name="${name}"></test-dynamic-child>
`.key(name))}
</test-dynamic-parent>
`.define({ TestDynamicParent, TestDynamicChild }),
});
Expand Down
36 changes: 36 additions & 0 deletions test/spec/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ describe('html:', () => {

beforeEach(() => {
fragment = document.createElement('div');
document.body.appendChild(fragment);
});

afterEach(() => {
document.body.removeChild(fragment);
});

const getArrayValues = f => Array.from(f.children)
Expand Down Expand Up @@ -583,4 +588,35 @@ describe('html:', () => {
document.body.removeChild(globalElement);
});
});

describe('use external element with shadowRoot', () => {
class TestExternalElement extends HTMLBridge {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<div>test</div>
<slot></slot>
`;
}
}

const render = value => html`<test-external-element>${value}</test-external-element>`
.define({ TestExternalElement });

it('renders external element with slotted value', (done) => {
const el = document.createElement('div');
el.attachShadow({ mode: 'open' });

document.body.appendChild(el);

render(0)(el, el.shadowRoot);

setTimeout(() => {
expect(el.shadowRoot.children[0].innerHTML).toBe('0');
document.body.removeChild(el);
done();
}, 100);
});
});
});

0 comments on commit fd16d8a

Please # to comment.