UNPKG

@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
<!doctype 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>