@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
535 lines (474 loc) • 15.9 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>
<script>
window.configureBehavior = {
changeHandlerCount: 0,
objectChangeHandlerCount: 0,
contentChanged: function() {
this.changeHandlerCount++;
this.stomp = 10;
},
objectChanged: function() {
this.objectChangeHandlerCount++;
}
};
</script>
<dom-module id="x-configure-value">
<template>
<span id="content">{{content}}</span>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-configure-value',
behaviors: [window.configureBehavior],
properties: {
content: {
type: String,
notify: true,
observer: 'contentChanged',
value: 'default'
},
object: {
type: Object,
notify: true,
value: function() { return {foo: 'obj-default'}; },
observer: 'objectChanged'
},
readOnly: {
readOnly: true,
value: 'default'
},
stomp: {
value: 5
}
}
});
});
</script>
</dom-module>
<dom-module id="x-configure-gchild">
<template>
<span id="content">{{content}}</span>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-configure-gchild',
behaviors: [window.configureBehavior],
properties: {
content: {
type: String,
notify: true,
observer: 'contentChanged',
value: 'gchild'
},
object: {
type: Object,
notify: true,
value: function() { return {foo: 'obj-default'}; },
observer: 'objectChanged'
},
readOnly: {
readOnly: true,
value: 'default'
},
stomp: {
value: 5
}
}
});
});
</script>
</dom-module>
<dom-module id="x-configure-child">
<template>
<x-configure-gchild id="gchild" content="{{content}}" object="{{object}}"></x-configure-gchild>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-configure-child',
behaviors: [window.configureBehavior],
properties: {
content: {
type: String,
notify: true,
observer: 'contentChanged',
value: 'child'
},
negatedContent: {
type: Boolean,
observer: 'negatedContentChanged',
value: true
},
compoundInput: {
type: String,
observer: 'compoundInputChanged',
value: 'default'
},
object: {
type: Object,
notify: true,
value: function() { return {foo: 'obj-default'}; },
observer: 'objectChanged'
},
readOnly: {
readOnly: true,
value: 'default'
},
stomp: {
value: 5
},
attrDash: {
observer: 'attrDashChanged',
value: 'default'
},
attrNumber: {
type: Number,
observer: 'attrNumberChanged',
value: 0
},
attrBoolean: {
type: Boolean,
observer: 'attrBooleanChanged',
value: false
}
},
created: function() {
this.attrDashChanged = sinon.spy();
this.attrNumberChanged = sinon.spy();
this.attrBooleanChanged = sinon.spy();
this.negatedContentChanged = sinon.spy();
this.compoundInputChanged = sinon.spy();
}
});
});
</script>
</dom-module>
<dom-module id="x-configure-simple-child">
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-configure-simple-child',
properties: {
noeffect: String
},
ready: function() {
this.hasPropertyAtReadyTime = (this.noeffect !== undefined);
}
});
});
</script>
</dom-module>
<dom-module id="x-configure-host">
<template>
<x-configure-child id="child"
content="{{content}}"
negated-content="[[!content]]"
compound-input="a [[simple]] [[content]]"
object="{{object.goo}}"
attr$="{{attrValue}}"
attr-dash$="{{attrValue}}"
attr-number$="{{attrNumber}}"
attr-boolean$="{{attrBoolean}}"
></x-configure-child>
<x-configure-simple-child id="simple" noeffect="{{simple}}"></x-configure-simple-child>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-configure-host',
behaviors: [window.configureBehavior],
properties: {
content: {
type: String,
notify: true,
observer: 'contentChanged',
value: 'host'
},
object: {
type: Object,
notify: true,
value: function() { return {goo: {foo: 'obj-host'}}; },
observer: 'objectChanged'
},
readOnly: {
readOnly: true,
value: 'default'
},
stomp: {
value: 5
},
attrValue: {
value: 'attrValue'
},
attrNumber: {
value: 42
},
attrBoolean: {
value: false
},
simple: {
value: 'simple'
}
}
});
});
</script>
</dom-module>
<script>
window.XConfigLazy = {
is: 'x-config-lazy',
properties: {
noEffectProp: Number,
defaultUsesNoEffectProp: {
type: Number
},
boundNoEffectProp: {
type: Number,
value: 5
},
prop: {
value: 'lazy',
observer: 'propChanged'
},
readOnlyProp: {
readOnly: true,
value: 'readOnly'
},
hadAttrProp: {
value: 'hadAttrProp',
observer: 'hadAttrPropChanged'
}
},
created: function() {
this.noEffectProp = 1;
this.defaultUsesNoEffectProp = this.noEffectProp * 2;
this.propChanged = sinon.spy();
this.hadAttrPropChanged = sinon.spy();
}
};
</script>
<dom-module id="x-config-lazy-nodefaults">
<template>
<div>x-config-lazy-nodefaults</div>
</template>
<script>
window.XConfigLazyNoDefaults = {
is: 'x-config-lazy-nodefaults',
properties: {
prop: {
observer: 'propChanged'
}
},
created: function() {
this.propChanged = sinon.spy();
}
};
</script>
</dom-module>
<dom-module id="x-config-lazy-host">
<template>
<x-config-lazy id="lazy" prop="{{foo}}" read-only-prop="{{foo}}" had-attr-prop="attrValue" bound-no-effect-prop="{{foo}}"></x-config-lazy>
<x-config-lazy-nodefaults prop="[[foo]]"></x-config-lazy-nodefaults>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-config-lazy-host',
properties: {
foo: {
value: 'foo',
observer: 'fooChanged'
}
},
fooChanged: function(foo) {
this.$.lazy.hadAttrProp = foo;
}
});
});
</script>
</dom-module>
<x-configure-value></x-configure-value>
<x-configure-value content="attr" object='{"foo": "obj-attr"}'></x-configure-value>
<x-configure-host></x-configure-host>
<x-configure-host content="attr"></x-configure-host>
<script>
function testValueAndChangeHandler(e, value) {
assert.equal(e.content, value, 'Property does not equal configured value');
assert.equal(e.changeHandlerCount, 1, 'property `change` Change handler not run when default value set');
assert.equal(e.objectChangeHandlerCount, 1, 'property `object` Change handler not run when default value set');
}
function testConfigure(e, value, objectValue) {
testValueAndChangeHandler(e, value);
assert.equal(e.object.foo, objectValue);
assert.equal(e.$.content.textContent, value, 'Bound value not propagated to dom');
}
function testConfigureHost(e, value) {
testValueAndChangeHandler(e, value);
e = e.$.child;
testValueAndChangeHandler(e, value);
e = e.$.gchild;
testValueAndChangeHandler(e, value);
assert.equal(e.$.content.textContent, value, 'Bound value not propagated to dom');
}
suite('configure', function() {
test('value set in properties initializes correctly', function() {
var e = document.querySelector('x-configure-value');
testConfigure(e, 'default', 'obj-default');
});
test('attribute overrides value set in properties', function() {
var e = document.querySelector('x-configure-value[content]');
testConfigure(e, 'attr', 'obj-attr');
});
test('configured values initialize and propagates', function() {
var e = document.querySelector('x-configure-host');
testConfigureHost(e, 'host');
});
test('negated value configured correctly', function() {
var e = document.querySelector('x-configure-host');
assert.equal(e.$.child.negatedContent, false);
assert.isTrue(e.$.child.negatedContentChanged.calledOnce, 'negated content not changed exactly once');
});
test('compound effect resulting value set once', function() {
var e = document.querySelector('x-configure-host');
assert.equal(e.$.child.compoundInput, 'a simple host');
assert.isTrue(e.$.child.compoundInputChanged.calledOnce, 'compound content not changed exactly once');
});
test('attribute overrides configured values and propagates', function() {
var e = document.querySelector('x-configure-host[content]');
testConfigureHost(e, 'attr');
});
test('property changed in change handler of another not stomped by default', function() {
var e = document.querySelector('x-configure-value');
assert.equal(e.stomp, 10);
});
test('read-only property initialized to default value', function() {
var e = document.querySelector('x-configure-value');
assert.equal(e.readOnly, 'default');
});
test('attribute bindings to properties without effects not configured', function() {
var e = document.querySelector('x-configure-host');
assert.equal(e.$.child.getAttribute('attr'), 'attrValue');
assert.equal(e.$.child.attr, undefined);
});
test('attribute bindings to properties with effects configured', function() {
var e = document.createElement('x-configure-host');
document.body.appendChild(e);
assert.equal(e.$.child.getAttribute('attr-dash'), 'attrValue');
assert.notProperty(e.$.child, 'attr-dash');
assert.equal(e.$.child.attrDash, 'attrValue');
assert.isTrue(e.$.child.attrDashChanged.calledOnce);
assert.equal(e.$.child.attrDashChanged.getCall(0).args[0], 'attrValue');
assert.equal(e.$.child.getAttribute('attr-number'), '42');
assert.notProperty(e.$.child, 'attr-number');
assert.strictEqual(e.$.child.attrNumber, 42);
assert.isTrue(e.$.child.attrNumberChanged.calledOnce);
assert.strictEqual(e.$.child.attrNumberChanged.getCall(0).args[0], 42);
assert.equal(e.$.child.hasAttribute('attr-boolean'), false);
assert.notProperty(e.$.child, 'attr-boolean');
// Attribute bindings are no longer specially
// configured to properties as they were with 1.0. This should
// only affect bindings that set false (removing a non-existing attribute)
// to override a default of true (which is an odd default for a boolean attr).
assert.strictEqual(e.$.child.attrBoolean, false);
assert.isTrue(e.$.child.attrBooleanChanged.calledOnce);
assert.strictEqual(e.$.child.attrBooleanChanged.getCall(0).args[0], false);
});
test('bindings to properties without effects configured', function() {
var e = document.createElement('x-configure-host');
document.body.appendChild(e);
assert.isTrue(e.$.simple.hasPropertyAtReadyTime, 'property value not configured and therefore not set at ready time');
});
test('pre-register property assignment does not break getters and setters', function() {
var x = document.createElement('x-late-register');
document.body.appendChild(x);
// set property
x.shouldChange = '1';
// now register element
Polymer({
is: 'x-late-register',
properties: {
shouldChange : {
observer: 'shouldChangeCallback',
type: String
}
},
shouldChangeCallback: function() {
this.textContent = this.shouldChange;
}
});
assert.equal(x.shouldChange, '1');
assert.equal(x.shouldChange, x.textContent);
x.shouldChange = '2';
assert.equal(x.shouldChange, '2');
assert.equal(x.shouldChange, x.textContent);
document.body.removeChild(x);
});
// Created is no longer called before
// default properties are set. This means values set in created cannot
// be used to calculate defaults. However, defaults can now be set for
// any property (including those with effects) in created.
test('setting properties in created works with configuration', function() {
var x = document.createElement('x-late-register2');
document.body.appendChild(x);
// now register element
Polymer({
is: 'x-late-register2',
properties: {
a: {
type: Number,
value: 0
},
b: {
type: Number
}
},
created: function() {
this.a = 1;
this.b = this.a * 2;
}
});
assert.equal(x.b, 2);
document.body.removeChild(x);
});
test('lazy upgrade binding use cases', function() {
var el = document.createElement('x-config-lazy-host');
document.body.appendChild(el);
Polymer(window.XConfigLazy);
// The purpose of this test is to test lazy upgrading an element that
// has a property pre-bound to it (that has an accessor) but that has
// no defaults for it (stresses a prior bug in ready() code that called
// _attachDom twice, which throws in Safari's native SD
Polymer(window.XConfigLazyNoDefaults);
assert.equal(el.$.lazy.noEffectProp, 1);
assert.equal(el.$.lazy.defaultUsesNoEffectProp, 2);
assert.equal(el.$.lazy.boundNoEffectProp, 'foo');
assert.equal(el.$.lazy.prop, 'foo');
assert.isTrue(el.$.lazy.propChanged.calledOnce);
assert.equal(el.$.lazy.readOnlyProp, 'readOnly');
assert.equal(el.$.lazy.hadAttrProp, 'foo');
assert.isTrue(el.$.lazy.hadAttrPropChanged.calledOnce);
document.body.removeChild(el);
});
});
</script>
</body>
</html>