Skip to content

Commit

Permalink
fix: Use token for the styling host element instead of tag name (#390)
Browse files Browse the repository at this point in the history
## Details

This PR changes the way the host element gets targetted in CSS. Instead of relying on the tag name, the compiler now reuses the token to style the host element.

Fix #383 

## Does this PR introduce a breaking change?

* [ ] Yes
* [X] No
  • Loading branch information
pmdartus authored Jun 12, 2018
1 parent 231e00d commit cccc63d
Show file tree
Hide file tree
Showing 28 changed files with 185 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-default_default';
tmpl.hostToken = 'x-default_default-host';
tmpl.shadowToken = 'x-default_default';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-class_and_template_class_and_template';
tmpl.hostToken = 'x-class_and_template_class_and_template-host';
tmpl.shadowToken = 'x-class_and_template_class_and_template';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-external_external';
tmpl.hostToken = 'x-external_external-host';
tmpl.shadowToken = 'x-external_external';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-cmp1_cmp1';
tmpl.hostToken = 'x-cmp1_cmp1-host';
tmpl.shadowToken = 'x-cmp1_cmp1';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
return [];
}
if (style) {
tmpl.token = 'x-node_env_node_env';
tmpl.hostToken = 'x-node_env_node_env-host';
tmpl.shadowToken = 'x-node_env_node_env';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-node_env_node_env';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
return [];
}
if (style) {
tmpl.token = 'x-node_env_node_env';
tmpl.hostToken = 'x-node_env_node_env-host';
tmpl.shadowToken = 'x-node_env_node_env';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-node_env_node_env';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'myns-relative_import_relative_import';
tmpl.hostToken = 'myns-relative_import_relative-host';
tmpl.shadowToken = 'myns-relative_import_relative';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}, [])];
}
if (style) {
tmpl.token = 'x-foo_foo';
tmpl.hostToken = 'x-foo_foo-host';
tmpl.shadowToken = 'x-foo_foo';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-foo_foo';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-foo_foo';
tmpl.hostToken = 'x-foo_foo-host';
tmpl.shadowToken = 'x-foo_foo';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-foo_foo';
tmpl.hostToken = 'x-foo_foo-host';
tmpl.shadowToken = 'x-foo_foo';

const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _xFoo from 'x-foo';
import { Element } from 'engine';

function style(tagName, token) {
return `${tagName}[${token}],[is="${tagName}"][${token}] {
return `[${token}-host] {
color: blue;
}
div[${token}] {
Expand All @@ -28,7 +28,8 @@ function tmpl($api, $cmp, $slotset, $ctx) {
}

if (style) {
tmpl.token = 'x-styled_styled';
tmpl.hostToken = 'x-styled_styled-host';
tmpl.shadowToken = 'x-styled_styled';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-styled_styled';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ describe("module resolver", () => {
}, [api_text(\"Manually Imported Template\")])];
}
if (style) {
tmpl.token = 'x-class_and_template_class_and_template';
tmpl.hostToken = 'x-class_and_template_class_and_template-host';
tmpl.shadowToken = 'x-class_and_template_class_and_template';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-class_and_template_class_and_template';
Expand Down Expand Up @@ -93,7 +94,8 @@ describe("module resolver", () => {
}, [api_text(\"Another Template\")])];
}
if (style) {
tmpl.token = 'x-class_and_template_anotherTemplate';
tmpl.hostToken = 'x-class_and_template_anotherTemplate-host';
tmpl.shadowToken = 'x-class_and_template_anotherTemplate';
const style$$1 = document.createElement('style');
style$$1.type = 'text/css';
style$$1.dataset.token = 'x-class_and_template_anotherTemplate';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ describe("transform", () => {
}, [api_text(\"Hello\")])];
}
if (stylesheet) {
tmpl.token = 'x-foo_foo';
tmpl.hostToken = 'x-foo_foo-host';
tmpl.shadowToken = 'x-foo_foo';
const style = document.createElement('style');
style.type = 'text/css';
style.dataset.token = 'x-foo_foo'
Expand Down
1 change: 0 additions & 1 deletion packages/lwc-compiler/src/transformers/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export default function transformStyle(
const plugins = [
postcssPluginRaptor({
token: TOKEN_PLACEHOLDER,
tagName: TAG_NAME_PLACEHOLDER
})
];

Expand Down
15 changes: 9 additions & 6 deletions packages/lwc-compiler/src/transformers/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ function attachStyleToTemplate(
// Use the component tagname and a unique style token to scope the compiled
// styles to the component.
const tagName = `${namespace}-${name}`;
const scopingToken = `${tagName}_${templateFilename}`;
const shadowToken = `${tagName}_${templateFilename}`;
const hostToken = `${shadowToken}-host`;

return [
`import stylesheet from './${templateFilename}.css'`,
Expand All @@ -35,16 +36,18 @@ function attachStyleToTemplate(
// doesn't exists.
`if (stylesheet) {`,

// The engine picks the style token from the template during rendering to
// add the token to all generated elements.
` tmpl.token = '${scopingToken}';`,
// The engine picks the tokens from the template during rendering:
// * `hostToken`: is applied only to the host element
// * `shadowToken`: is applied to all the element generated by the template
` tmpl.hostToken = '${hostToken}';`,
` tmpl.shadowToken = '${shadowToken}';`,
``,

// Inject the component style in a new style tag the document head.
` const style = document.createElement('style');`,
` style.type = 'text/css';`,
` style.dataset.token = '${scopingToken}'`,
` style.textContent = stylesheet('${tagName}', '${scopingToken}');`,
` style.dataset.token = '${shadowToken}'`,
` style.textContent = stylesheet('${tagName}', '${shadowToken}');`,
` document.head.appendChild(style);`,
`}`
].join("\n");
Expand Down
63 changes: 44 additions & 19 deletions packages/lwc-engine/src/framework/__tests__/template.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Element } from "../html-element";
import { createElement } from '../main';
import { ViewModelReflection } from '../utils';
import { Template } from '../template';
import { querySelector } from '../dom/element';

function createCustomComponent(html: Template, slotset?) {
class MyComponent extends Element {
Expand Down Expand Up @@ -295,9 +294,9 @@ describe('template', () => {
})

describe('token', () => {
it('adds token to the host element if template has a token', () => {
it('adds the host token to the host element if template has a token', () => {
const styledTmpl: Template = () => [];
styledTmpl.token = 'token';
styledTmpl.hostToken = 'token-host';

class Component extends Element {
render() {
Expand All @@ -307,14 +306,39 @@ describe('template', () => {

const cmp = createElement('x-cmp', { is: Component });

expect(cmp.hasAttribute('token')).toBe(false);
expect(cmp.hasAttribute('token-host')).toBe(false);
document.body.appendChild(cmp);
expect(cmp.hasAttribute('token')).toBe(true);
expect(cmp.hasAttribute('token-host')).toBe(true);
});

it('removes token from the host element when changing template', () => {
it('adds the token to all the rendered elements if the template has a token', () => {
const styledTmpl: Template = ($api) => [
$api.h('div', {
key: 1,
}, [
$api.h('div', {
key: 2,
}, [])
]),
];
styledTmpl.shadowToken = 'token';

class Component extends Element {
render() {
return styledTmpl;
}
}

const cmp = createElement('x-cmp', { is: Component });
document.body.appendChild(cmp);

const divs = cmp.shadowRoot.querySelectorAll('div[token]');
expect(divs.length).toBe(2);
});

it('removes the host token from the host element when changing template', () => {
const styledTmpl: Template = () => [];
styledTmpl.token = 'token';
styledTmpl.hostToken = 'token-host';

const unstyledTmpl: Template = () => [];

Expand All @@ -331,21 +355,21 @@ describe('template', () => {
const cmp = createElement('x-cmp', { is: Component });
document.body.appendChild(cmp);

expect(cmp.hasAttribute('token')).toBe(true);
expect(cmp.hasAttribute('token-host')).toBe(true);

cmp.tmpl = unstyledTmpl;

return Promise.resolve().then(() => {
expect(cmp.hasAttribute('token')).toBe(false);
expect(cmp.hasAttribute('token-host')).toBe(false);
});
});

it('swaps the token when replacing the template with a different token', () => {
it('swaps the host token when replacing the template with a different token', () => {
const styledTmplA: Template = () => [];
styledTmplA.token = 'tokenA';
styledTmplA.hostToken = 'tokenA-host';

const styledTmplB: Template = () => [];
styledTmplB.token = 'tokenB';
styledTmplB.hostToken = 'tokenB-host';

class Component extends Element {
tmpl = styledTmplA;
Expand All @@ -360,14 +384,14 @@ describe('template', () => {
const cmp = createElement('x-cmp', { is: Component });
document.body.appendChild(cmp);

expect(cmp.hasAttribute('tokenA')).toBe(true);
expect(cmp.hasAttribute('tokenB')).toBe(false);
expect(cmp.hasAttribute('tokenA-host')).toBe(true);
expect(cmp.hasAttribute('tokenB-host')).toBe(false);

cmp.tmpl = styledTmplB;

return Promise.resolve().then(() => {
expect(cmp.hasAttribute('tokenA')).toBe(false);
expect(cmp.hasAttribute('tokenB')).toBe(true);
expect(cmp.hasAttribute('tokenA-host')).toBe(false);
expect(cmp.hasAttribute('tokenB-host')).toBe(true);
});
});
});
Expand Down Expand Up @@ -461,7 +485,8 @@ describe('template', () => {
const element = createElement('x-attr-cmp', { is: MyComponent });
document.body.appendChild(element);

expect(querySelector.call(element, 'div').getAttribute('title')).toBe('foo');
const div = element.shadowRoot.querySelector('div');
expect(div.getAttribute('title')).toBe('foo');
});

it('should remove attribute when value is null', () => {
Expand Down Expand Up @@ -493,10 +518,10 @@ describe('template', () => {
const element = createElement('x-attr-cmp', { is: MyComponent });
document.body.appendChild(element);

expect(querySelector.call(element, 'div').getAttribute('title')).toBe('initial');
expect(element.shadowRoot.querySelector('div').getAttribute('title')).toBe('initial');
element.setInner(null);
return Promise.resolve().then(() => {
expect(querySelector.call(element, 'div').hasAttribute('title')).toBe(false);
expect(element.shadowRoot.querySelector('div').hasAttribute('title')).toBe(false);
});
});
});
Expand Down
8 changes: 4 additions & 4 deletions packages/lwc-engine/src/framework/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ function getCurrentFallback(): boolean {
return isNull(vmBeingRendered) || vmBeingRendered.fallback;
}

function getCurrentTplToken(): string | undefined {
function getCurrentShadowToken(): string | undefined {
// For root elements and other special cases the vm is not set.
if (isNull(vmBeingRendered)) {
return;
}
return vmBeingRendered.context.tplToken;
return vmBeingRendered.context.shadowToken;
}

function normalizeStyleString(value: any): string | undefined {
Expand Down Expand Up @@ -190,7 +190,7 @@ export function h(sel: string, data: VNodeData, children: VNodes): VElement {
const { classMap, className, style, styleMap, key } = data;
data.class = classMap || getMapFromClassName(normalizeStyleString(className));
data.style = styleMap || normalizeStyleString(style);
data.token = getCurrentTplToken();
data.token = getCurrentShadowToken();
data.uid = getCurrentOwnerId();
let text, elm; // tslint:disable-line
const vnode: VElement = {
Expand Down Expand Up @@ -261,7 +261,7 @@ export function c(sel: string, Ctor: ComponentConstructor, data: VNodeData, chil
data = { hook, key, attrs, on, props, ctor: Ctor };
data.class = classMap || getMapFromClassName(normalizeStyleString(className));
data.style = styleMap || normalizeStyleString(style);
data.token = getCurrentTplToken();
data.token = getCurrentShadowToken();
data.uid = getCurrentOwnerId();
data.fallback = getCurrentFallback();
data.mode = 'open'; // TODO: this should be defined in Ctor
Expand Down
5 changes: 3 additions & 2 deletions packages/lwc-engine/src/framework/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ export const TopLevelContextSymbol = Symbol();

export interface Context {
[TopLevelContextSymbol]?: boolean;
tplToken?: string;
tplCache?: Template | undefined;
hostToken?: string;
shadowToken?: string;
tplCache?: Template;
[key: string]: any;
}

Expand Down
Loading

0 comments on commit cccc63d

Please sign in to comment.