@polymer/polymer
Version:
The Polymer library makes it easy to create your own web components. Give your element some markup and properties, and then use it on a site. Polymer provides features like dynamic templates and data binding to reduce the amount of boilerplate you need to
681 lines (600 loc) • 17.1 kB
HTML
<!--
@license
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<meta charset="utf-8">
<script src="../../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer.html">
</head>
<body>
<x-scope></x-scope>
<dom-module id="story-card">
<template>
<style>
:host {
display: block;
}
#story-card .story-content {
font-family: 'Roboto', sans-serif;
font-size: 1.2em;
@apply --story-content;
}
</style>
<div id="story-card">
<div class="story-content" id="content">Content</div>
</div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'story-card'});
});
</script>
</dom-module>
<dom-module id="x-child-scope">
<template>
<style>
:host {
display: block;
--mixin2: {
border: 3px solid seagreen;
background: url(http://www.google.com/icon.png);
};
}
#mixin1 {
@apply --mixin1;
}
#mixin2 {
@apply --mixin2;
}
#mixin3 {
padding: 3px;
@apply --mixin3;
}
#mixin4 {
@apply --mixin4;
}
#mixin6 {
@apply --mixin6
}
#mixin7 {@apply --mixin7}
#mixin8 {
@apply --mixin8
}
#mixin9 {
@apply --mixin9
}
</style>
<div id="mixin1">mixin1</div>
<div id="mixin2">mixin2</div>
<div id="mixin3">mixin3</div>
<div id="mixin4">mixin4</div>
<div id="mixin6">mixin6</div>
<div id="mixin7">mixin7</div>
<div id="mixin8">mixin8</div>
<div id="mixin9">mixin9</div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'x-child-scope'});
});
</script>
</dom-module>
<dom-module id="x-keyframes">
<template>
<style>
:host {
display: block;
position: relative;
border: 10px solid blue;
left: 0px;
/* Prefix required by Safari <= 8 */
-webkit-animation-duration: 0.3s;
animation-duration: 0.3s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
:host([animated]) {
/* Prefix required by Safari <= 8 */
-webkit-animation-name: x-keyframes-animation;
animation-name: x-keyframes-animation;
}
/* Prefix required by Safari <= 8 */
@-webkit-keyframes x-keyframes-animation {
0% {
left: var(--c1);
}
100% {
left: var(--c2);
@apply --keyframe-finish;
}
}
@keyframes x-keyframes-animation {
0% {
left: var(--c1);
}
100% {
left: var(--c2);
@apply --keyframe-finish;
}
}
</style>
x-keyframes
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-keyframes',
properties: {
animated: {
type: Boolean,
value: false,
reflectToAttribute: true
}
}
});
});
</script>
</dom-module>
<dom-module id="x-scope">
<template>
<style>
:host {
display: block;
padding: 8px;
--override-me: {
border: 11px solid black;
};
--mixin1: {
border: 1px solid black;
};
--b: 2px solid orange;
--m1: 1px;
--m2: 2px;
--c1: 5px;
--c2: 10px;
--mixin2: {
border: var(--b);
};
--mixin3: {
@apply --mixin2;
};
--mixin4: {
padding: 2px;
@apply --mixin3;
margin: var(--m2);
};
--mixin5: {
border: calc(var(--c1) + var(--c2)) solid orange;
};
--mixin6: {
border: 16px solid orange;
};
--mixin7: {
border: 17px solid navy;
};
--mixin8: {
border: 17px dotted navy;
};
--mixin9: var(--mixin8);
}
#mixin1 {
@apply --mixin1;
}
#mixin2 {
@apply --mixin2;
}
#mixin3 {
padding: 1px;
margin: var(--m1);
@apply --mixin3;
}
#mixin4 {
@apply --mixin4;
}
#mixin5 {
@apply --mixin5;
}
#keyframes2 {
--keyframe-finish: {
left: 20px;
};
}
x-child-scope {
padding: 10px;
}
#card {
--story-content: {
border: 11px solid orange;
};
}
#override {
@apply --override-me;
border: 19px solid steelblue;
}
</style>
<div id="mixin1">mixin1</div>
<div id="mixin2">mixin2</div>
<div id="mixin3">mixin3</div>
<div id="mixin4">mixin4</div>
<div id="mixin5">mixin5</div>
<hr>
<x-keyframes id="keyframes1"></x-keyframes>
<x-keyframes id="keyframes2"></x-keyframes>
<x-child-scope id="child"></x-child-scope>
<story-card id="card"></story-card>
<div id="override">override</div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'x-scope'});
});
</script>
</dom-module>
<dom-module id="x-var-produce-via-consume">
<template>
<style>
:host {
display: block;
border: 10px solid orange;
--foo: {
color: var(--bar);
}
}
</style>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-var-produce-via-consume'
});
});
</script>
</dom-module>
<dom-module id="x-cache-child">
<template>
<style>
:host {
display: block;
color: var(--foo);
@apply --bar;
}
</style>
X
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-cache-child'
});
});
</script>
</dom-module>
<dom-module id="x-cache-host-1">
<template>
<style>
:host {
display: block;
--foo: blue;
--bar: {
border: 10px solid black;
};
}
</style>
<x-cache-child id="child"></x-bad-child>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-cache-host-1'
});
});
</script>
</dom-module>
<dom-module id="x-cache-host-2">
<template>
<style>
:host {
display: block;
--foo: blue;
--bar: {
border: 1px dotted green;
};
}
</style>
<x-cache-child id="child"></x-bad-child>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-cache-host-2'
});
});
</script>
</dom-module>
<dom-module id="x-no-paren-apply-only">
<template>
<style>
:host {
--bar: {
border: 2px solid green;
}
}
#child {
@apply --bar;
}
</style>
<div id="child">X</div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-no-paren-apply-only'
});
});
</script>
</dom-module>
<dom-module id="x-redefine-three">
<template>
<style>
div {
@apply --redefine;
}
</style>
<div id="div"></div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'x-redefine-three'});
});
</script>
</dom-module>
<dom-module id="x-redefine-two">
<template>
<style>
x-redefine-three {
--redefine: {
border: 2px solid gray;
};
}
</style>
<x-redefine-three id="three"></x-redefine-three>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'x-redefine-two'});
});
</script>
</dom-module>
<dom-module id="x-redefine-one">
<template>
<style>
x-redefine-two {
--redefine: {
height: 100px;
width: 100px;
};
}
</style>
<x-redefine-two id="two"></x-redefine-two>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'x-redefine-one'});
});
</script>
</dom-module>
<dom-module id="order-child">
<template>
<style>
div {
@apply --order-mixin;
}
</style>
<div id="target"></div>
</template>
</dom-module>
<dom-module id="order-parent">
<template>
<style>
order-child {
--order-mixin: {
border: 10px solid black;
};
}
</style>
<order-child id="child"></order-child>
</template>
</dom-module>
<dom-module id="order-gp">
<template>
<style>
order-child {
--order-mixin: {
};
}
</style>
<order-child id="child"></order-child>
<order-parent id="parent"></order-parent>
</template>
</dom-module>
<script>
HTMLImports.whenReady(function() {
Polymer({is: 'order-child'});
Polymer({is: 'order-parent'});
Polymer({is: 'order-gp'});
});
</script>
<script>
suite('scoped-styling-apply', function() {
function assertComputed(element, value, property) {
var computed = getComputedStyle(element);
property = property || 'border-top-width';
assert.equal(computed[property], value, 'computed style incorrect for ' + property);
}
var styled = document.querySelector('x-scope');
test('variable mixins calculated correctly and inherit', function() {
if (!window.ShadyCSS || ShadyCSS.nativeCss) {
this.skip();
}
var suffix = window.ShadyCSS.ApplyShim._separator + 'border';
var e = styled;
let mixin1 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin1' + suffix);
let mixin2 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin2' + suffix);
let mixin3 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin3' + suffix);
let mixin4 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin4' + suffix);
e = styled.$.child;
let cmixin1 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin1' + suffix);
let cmixin2 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin2' + suffix);
let cmixin3 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin3' + suffix);
let cmixin4 = ShadyCSS.ScopingShim.getComputedStyleValue(e, '--mixin4' + suffix);
assert.equal(mixin1, cmixin1);
assert.equal(mixin2, cmixin2);
assert.equal(mixin3, cmixin3);
assert.equal(mixin4, cmixin4);
});
test('variable mixins apply', function() {
assertComputed(styled.$.mixin1, '1px');
assertComputed(styled.$.mixin2, '2px');
assertComputed(styled.$.mixin3, '2px');
assertComputed(styled.$.mixin3, '1px', 'padding-top');
assertComputed(styled.$.mixin3, '1px', 'margin-top');
assertComputed(styled.$.mixin4, '2px');
assertComputed(styled.$.mixin4, '2px', 'padding-top');
assertComputed(styled.$.mixin4, '2px', 'margin-top');
});
test('mixins apply with url values', function() {
var url = 'http://www.google.com/icon.png';
var e = styled.$.child;
var actual, unescaped, propertyName;
if (window.ShadyCSS && ShadyCSS.nativeCssApply) {
actual = getComputedStyle(e).getPropertyValue('--mixin2');
// if strings aren't used in the url, getPropertyValue will escape the string, which breaks assert.include
unescaped = actual.replace(/\\/g, '');
assert.include(unescaped, url);
} else if (!window.ShadyCSS || ShadyCSS.nativeCss) {
propertyName = '--mixin2' + window.ShadyCSS.ApplyShim._separator + 'background';
actual = getComputedStyle(e).getPropertyValue(propertyName);
// if strings aren't used in the url, getPropertyValue will escape the string, which breaks assert.include
unescaped = actual.replace(/\\/g, '');
assert.include(unescaped, url);
} else {
propertyName = '--mixin2';
actual = ShadyCSS.ScopingShim.getComputedStyleValue(e, propertyName);
unescaped = actual.replace(/\\/g, '');
assert.include(unescaped, url);
}
});
test('variable mixins inherit and override', function() {
var e = styled.$.child;
assertComputed(e.$.mixin1, '1px');
assertComputed(e.$.mixin2, '3px');
assertComputed(e.$.mixin3, '2px');
assertComputed(e.$.mixin3, '3px', 'padding-top');
assertComputed(e.$.mixin4, '2px');
assertComputed(e.$.mixin4, '2px', 'padding-top');
assertComputed(e.$.mixin4, '2px', 'margin-top');
});
test('calc can be used in mixins', function() {
assertComputed(styled.$.mixin5, '15px');
});
test('mixins work with selectors that contain element name', function() {
assertComputed(styled.$.card.$.content, '11px');
});
test('mixins with trailing new line or } apply', function() {
assertComputed(styled.$.child.$.mixin6, '16px');
assertComputed(styled.$.child.$.mixin7, '17px');
});
test('mixins with new `@apply --foo` syntax', function() {
assertComputed(styled.$.child.$.mixin8, '17px');
});
test('mixins can be realiased with var()', function() {
assertComputed(styled.$.child.$.mixin9, '17px');
});
test('mixins apply to @keyframe rules', function(done) {
if (navigator.userAgent.match('Edge') && (!window.ShadyCSS || window.ShadyCSS.nativeCss)) {
// skip test due to missing variable support in keyframes
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12084341/
this.skip();
}
var xKeyframes1 = styled.$.keyframes1;
var xKeyframes2 = styled.$.keyframes2;
var completed = 0;
[xKeyframes1, xKeyframes2].forEach(function(xKeyframes, index) {
var target = index === 0 ? '10px' : '20px';
var onAnimationEnd = function() {
assert.include(xKeyframes.getComputedStyleValue('left'), target, xKeyframes.id);
xKeyframes.removeEventListener('animationend', onAnimationEnd);
xKeyframes.removeEventListener('webkitAnimationEnd', onAnimationEnd);
xKeyframes.animated = false;
if (++completed > 1) {
done();
}
};
xKeyframes.addEventListener('animationend', onAnimationEnd);
xKeyframes.addEventListener('webkitAnimationEnd', onAnimationEnd);
xKeyframes.animated = true;
});
});
test('producing a var that consumes another var preserves static styling', function() {
var d = document.createElement('x-var-produce-via-consume');
document.body.appendChild(d);
assertComputed(d, '10px');
});
test('producing a var that consumes results in static and not dynamic stylesheet', function() {
var d = document.createElement('x-var-produce-via-consume');
document.body.appendChild(d);
var styleRoot = (!window.ShadyDOM || !ShadyDOM.isShadyRoot(d.shadowRoot)) ? d.shadowRoot : document.head;
var selector = window.ShadyDOM ? 'style[scope~=x-var-produce-via-consume]' : 'style';
var staticStyle = styleRoot.querySelector(selector);
assert.ok(staticStyle);
assert.match(staticStyle.textContent, /display/, 'static style does not contain style content');
assert.equal(styleRoot.querySelectorAll(selector).length, 1);
});
test('mixin values can be overridden by subsequent concrete properties', function() {
assertComputed(styled.$.override, '19px');
});
test('@apply without parens is included in style cache behavior', function() {
var e1 = document.createElement('x-cache-host-1');
var e2 = document.createElement('x-cache-host-2');
document.body.appendChild(e1);
document.body.appendChild(e2);
Polymer.flush();
assertComputed(e1.$.child, '10px');
assertComputed(e2.$.child, '1px');
});
test('@apply without parens triggers property shim', function() {
var e = document.createElement('x-no-paren-apply-only');
document.body.appendChild(e);
Polymer.flush();
assertComputed(e.$.child, '2px');
});
test('mixin redefinition resets old properties', function() {
var e = document.createElement('x-redefine-one');
document.body.appendChild(e);
Polymer.flush();
var div = e.$.two.$.three.$.div;
assertComputed(div, '2px');
assertComputed(div, '0px', 'height');
});
test('mixin redefinition applies to complicated hierarchies', function() {
var e = document.createElement('order-gp');
document.body.appendChild(e);
Polymer.flush();
var childDiv = e.$.child.$.target;
var parentDiv = e.$.parent.$.child.$.target;
assertComputed(childDiv, '0px');
assertComputed(parentDiv, '10px');
});
});
</script>
</body>
</html>