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

720 lines (574 loc) 18.4 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"> <body> <script> HTMLImports.whenReady(function() { window.LifeCycleBehavior1 = { properties: { foo: { type: String } }, created() { this.__createdList = this.__createdList || []; this.__createdList.push('1'); }, attached() { this.__attachedList = this.__attachedList || []; this.__attachedList.push('1'); }, attributeChanged() { this.__attributeChangedList = this.__attributeChangedList || []; this.__attributeChangedList.push('1'); } }; window.LifeCycleBehavior2 = { created() { this.__createdList = this.__createdList || []; this.__createdList.push('2'); }, attached() { this.__attachedList = this.__attachedList || []; this.__attachedList.push('2'); } }; window.LifeCycleBehavior3 = { created() { this.__createdList = this.__createdList || []; this.__createdList.push('3'); } }; window.LifeCycleBehavior4 = { created() { this.__createdList = this.__createdList || []; this.__createdList.push('4'); } }; window.BehaviorA = { properties: { label: { type: String, observer: '_labelChanged' }, hasOptionsA: { readOnly: true, notify: true }, overridableProperty: { value: false }, overridablePropertyB: { value: false }, hasBehaviorA: { value: true }, computeADependency: { value: true }, computeA: { computed: '_computeProp(computeADependency)' } }, _simpleProperty: 'A', hostAttributes: { behavior: 'A', element: 'A', user: 'A' }, listeners: { change: '_changeHandler' }, ready: function() { this.__readyA = true; }, _labelChanged: function(label) { this.__label = label; }, _changeHandler: function(e) { this.__change = e.detail.value; }, _computeProp: function(a) { return a; } }; window.BehaviorB = { properties: { disabled: { type: Boolean, value: false, observer: '_disabledChanged' }, hasOptionsB: { readOnly: true, notify: true }, hasBehaviorB: { value: true }, overridablePropertyB: { value: true }, computeADependencyDependency: { value: 'hi' }, computeADependency: { computed: '_computeProp(computeADependencyDependency)' } }, hostAttributes: { behavior: 'B', element: 'B', user: 'B' }, _simpleProperty: 'B', _disabledChanged: function(disabled) { this.__disabled = disabled; }, ready: function() { this.__readyB = true; }, }; window.BehaviorC = { properties: { hasBehaviorC: { value: true } }, _simpleProperty: 'C' }; window.BehaviorD = { properties: { hasBehaviorD: { value: true } }, _simpleProperty: 'D' }; }); </script> <dom-module id="single-behavior"> <script> HTMLImports.whenReady(function() { customElements.define('single-behavior', Polymer.mixinBehaviors(window.BehaviorA, Polymer.Element)); }); </script> </dom-module> <dom-module id="lifecycle-behavior"> <script> HTMLImports.whenReady(function() { customElements.define('lifecycle-behavior', Polymer.mixinBehaviors([window.LifeCycleBehavior1, window.LifeCycleBehavior2], Polymer.Element)); }); </script> </dom-module> <dom-module id="multi-behaviors"> <script> HTMLImports.whenReady(function() { customElements.define('multi-behaviors', class extends Polymer.mixinBehaviors( [window.BehaviorA, window.BehaviorB], Polymer.Element) { static get properties() { return { foo: { type: String, reflectToAttribute: true, readOnly: true, observer: '_fooChanged' }, overridableProperty: { value: true } }; } constructor() { super(); } _fooChanged(foo) { this.__foo = foo; } _ensureAttributes() { this._ensureAttribute('element', 'element'); super._ensureAttributes(); } }); }); </script> </dom-module> <script> HTMLImports.whenReady(function() { customElements.define('nested-behaviors', class extends Polymer.mixinBehaviors([window.BehaviorD, window.LifeCycleBehavior1], Polymer.mixinBehaviors( [ [window.BehaviorB, [window.BehaviorC, window.BehaviorB], window.BehaviorA, window.LifeCycleBehavior2], ], Polymer.Element)) { }); var base = Polymer({ is: 'sup-element', behaviors: [ window.LifeCycleBehavior1, window.LifeCycleBehavior2 ], created: function() { this.__createdList.push('sup'); } }); class extended extends Polymer.mixinBehaviors([window.LifeCycleBehavior3, window.LifeCycleBehavior4], base) { created() { super.created(); this.__createdList.push('sub'); } } customElements.define('extended-behaviors', extended); }); </script> <dom-module id="behavior-registered"> <template> <div id="content"></div> </template> <script> HTMLImports.whenReady(function() { window.registerBehavior1 ={ registeredCount: 0, registered: function() { this._createPropertyObserver('prop', 'propChanged1'); this._createMethodObserver('propChanged2(prop)'); this.registeredCount++; this.registeredProps = [this.prop1, this.prop2, this.prop3]; this.registeredBehaviors = this.behaviors; }, prop1: true, ready: function() { this._ensureAttribute('attr', true); }, propChanged1: function() { this.propChanged1Called = true; }, propChanged2: function() { this.propChanged2Called = true; } }; window.registerBehavior2 ={ prop2: true, registered: function() { this.registeredCount++; } }; window.registerBehavior3 ={ prop3: true, registered: function() { this.registeredCount++; } }; class BehaviorRegistered extends Polymer.mixinBehaviors([ window.registerBehavior1, window.registerBehavior2, window.registerBehavior3 ], Polymer.Element) { static get is() { return 'behavior-registered';} _initializeProperties() { super._initializeProperties(); this.registeredCount++; } } customElements.define(BehaviorRegistered.is, BehaviorRegistered); class BehaviorRegisteredExt extends BehaviorRegistered { static get is() { return 'behavior-registered-ext';} } customElements.define(BehaviorRegisteredExt.is, BehaviorRegisteredExt); }); </script> </dom-module> <script> HTMLImports.whenReady(function() { var klass = Polymer.mixinBehaviors([ { __fooChangedCalled: 0, beforeRegister: function() { this.constructor.generatedFrom.observers = [ '_fooChanged(foo)' ]; }, _fooChanged: function() { this.__fooChangedCalled++; } } ], Polymer.Element); customElements.define('before-register-observers', klass); const Base = Polymer.mixinBehaviors([{ registered() { this.usedExtendedProto = this.canUseExtendedProto; } }], Polymer.Element); customElements.define('registered-proto', Polymer.mixinBehaviors([ {canUseExtendedProto: true} ], Base)); class Base2 extends Polymer.mixinBehaviors([{ properties: {b1: String} }], Polymer.Element) { static get properties() { return { e1: String}; } } class El extends Polymer.mixinBehaviors([{ properties: {b2: String}}], Base2) { static get properties() { return { e2: String}; } } customElements.define('extended-observed-attributes', El); }); </script> <test-fixture id="single"> <template> <single-behavior></single-behavior> </template> </test-fixture> <test-fixture id="lifecycle"> <template> <lifecycle-behavior foo="foo"></lifecycle-behavior> </template> </test-fixture> <test-fixture id="multi"> <template> <multi-behaviors user="user"></multi-behaviors> </template> </test-fixture> <test-fixture id="nested"> <template> <nested-behaviors foo="foo"></nested-behaviors> </template> </test-fixture> <test-fixture id="extended-behaviors"> <template> <extended-behaviors></extended-behaviors> </template> </test-fixture> <test-fixture id="registered"> <template> <behavior-registered></behavior-registered> </template> </test-fixture> <test-fixture id="before-register-observers"> <template> <before-register-observers></before-register-observers> </template> </test-fixture> <test-fixture id="registered-ext"> <template> <behavior-registered-ext></behavior-registered-ext> </template> </test-fixture> <test-fixture id="registered-proto"> <template> <registered-proto></registered-proto> </template> </test-fixture> <test-fixture id="extended-observed-attributes"> <template> <extended-observed-attributes b1="b1" e1="e1" b2="b2" e2="e2"></extended-observed-attributes> </template> </test-fixture> <script> suite('single behavior element', function() { var el; setup(function() { el = fixture('single'); }); test('ready from behavior', function() { assert.equal(el.__readyA, true); }); test('properties from behavior', function() { el.label = 'foo'; assert.equal(el.__label, 'foo'); }); test('instance behaviors', function() { assert.equal(el.behaviors.length, 1); }); test('listener from behavior', function() { el.fire('change', {value: 'bar'}); assert.equal(el.__change, 'bar'); }); test('property info from behavior', function() { assert.equal(el._hasNotifyEffect('hasOptionsA'), true); assert.equal(el._hasReadOnlyEffect('hasOptionsA'), true); assert.equal(typeof el._setHasOptionsA, 'function'); }); test('compute property from behavior', function() { assert.equal(el.computeA, true); }); }); suite('behavior.registered/beforeRegister', function() { test('can install dynamic properties', function() { var el = fixture('registered'); assert.ok(el.$.content); el.prop = 42; assert.isTrue(el.propChanged1Called); assert.isTrue(el.propChanged2Called); assert.isTrue(el.hasAttribute('attr')); }); test('called once for each behavior with access to element prototype', function() { var el = fixture('registered'); assert.equal(el.registeredCount, 4); assert.equal(el.registeredBehaviors.length, 3); assert.equal(el.registeredBehaviors, el.behaviors); assert.deepEqual(el.registeredProps, [true, true, true]); }); test('extending element with behaviors with registered properly registers', function() { var el = fixture('registered-ext'); assert.equal(el.registeredCount, 4); assert.equal(el.registeredBehaviors.length, 3); assert.equal(el.registeredBehaviors, el.behaviors); assert.deepEqual(el.registeredProps, [true, true, true]); }); test('add observers via behavior in beforeRegister', function() { var el = fixture('before-register-observers'); el.foo = 1; assert.equal(el.__fooChangedCalled, 1); }); test('registered called on class prototype when extended', function() { var el = fixture('registered-proto'); assert.isTrue(el.usedExtendedProto); }); }); suite('behavior lifecycle', function() { var el; setup(function() { el = fixture('lifecycle'); }); test('lifecycle', function() { assert.deepEqual(el.__createdList, ['1', '2'], 'created list wrong'); assert.deepEqual(el.__attachedList, ['1', '2'], 'attached list wrong'); assert.deepEqual(el.__attributeChangedList, ['1'], 'attributeChanged list wrong'); }); }); suite('multi-behaviors element', function() { var el; setup(function() { el = fixture('multi'); }); test('ready from behaviors', function() { assert.equal(el.__readyA, true); assert.equal(el.__readyB, true); }); test('instance behaviors', function() { assert.equal(el.behaviors.length, 2); }); test('properties from behaviors', function() { el.label = 'foo'; assert.equal(el.__label, 'foo'); el.disabled = true; assert.equal(el.__disabled, true); }); test('properties from itself', function() { assert.isDefined(el._setFoo, 'readOnly setter not available'); el._setFoo('bar'); assert.equal(el.__foo, 'bar', 'observer not getting called'); assert.equal(el.getAttribute('foo'), 'bar', 'not getting reflected'); }); test('listener from behaviors', function() { el.fire('change', {value: 'bar'}); assert.equal(el.__change, 'bar'); }); test('property info from behavior A', function() { assert.equal(el._hasNotifyEffect('hasOptionsA'), true); assert.equal(el._hasReadOnlyEffect('hasOptionsA'), true); assert.equal(typeof el._setHasOptionsA, 'function'); }); test('property info from behavior B', function() { assert.equal(el._hasReadOnlyEffect('hasOptionsB'), true); assert.equal(el._hasNotifyEffect('hasOptionsB'), true); assert.equal(typeof el._setHasOptionsB, 'function'); }); test('computed property dependency can become a computed property', function() { assert.equal(el.computeA, 'hi'); }); test('multi-behavior overrides ordering', function() { assert(el.overridableProperty, 'Behavior property was not overridden by prototype'); assert(el.overridablePropertyB, 'Behavior config-property was not overridden by sub-behavior'); }); test('hostAttributes ordering', function() { assert.equal(el.attributes.behavior.value, 'B', 'Behavior hostAttribute not overridden by younger behavior'); assert.equal(el.attributes.element.value, 'element', 'Behavior hostAttribute overridden by behavior'); assert.equal(el.attributes.user.value, 'user', 'Behavior hostAttribute overrode user attribute'); }); test('behavior is null generates warning', function() { sinon.spy(console, 'warn'); Polymer({ is: 'behavior-null', behaviors: [ null ] }); document.createElement('behavior-null'); assert.equal(console.warn.callCount, 1, 'Null behaviour should generate warning'); console.warn.restore(); }); test('behavior array is unique', function() { customElements.define('behavior-unique', Polymer.mixinBehaviors( [window.BehaviorA, window.BehaviorA], Polymer.Element)); assert.equal(document.createElement('behavior-unique').behaviors.length, 1); }); test('duplicate behaviors keep first behavior', function() { customElements.define('behavior-unique-last-behavior', Polymer.mixinBehaviors( [window.BehaviorA, window.BehaviorB, window.BehaviorC, window.BehaviorA, window.BehaviorB], Polymer.Element)); var behaviors = document.createElement('behavior-unique-last-behavior').behaviors; assert.deepEqual(behaviors, [window.BehaviorC, window.BehaviorA, window.BehaviorB]); }); }); suite('nested-behaviors element', function() { var el; setup(function() { el = fixture('nested'); }); test('nested-behavior dedups', function() { assert.equal(el.behaviors.length, 6); }); test('nested-behavior lifecycle', function() { assert.deepEqual(el.__createdList, ['2', '1'], 'created list wrong'); assert.deepEqual(el.__attachedList, ['2', '1'], 'attached list wrong'); assert.deepEqual(el.__attributeChangedList, ['1'], 'attributeChanged list wrong'); }); test('nested-behavior overrides ordering', function() { assert.ok(el.hasBehaviorA, "missing BehaviorA"); assert.ok(el.hasBehaviorB, "missing BehaviorB"); assert.ok(el.hasBehaviorC, "missing BehaviorC"); assert.ok(el.hasBehaviorD, "missing BehaviorD"); assert.equal(el._simpleProperty, 'D', 'Behavior simple property was not overridden by sub-behavior'); }); }); suite('extended-behaviors', function() { var el; setup(function() { el = fixture('extended-behaviors'); }); test('lifecycle', function() { assert.deepEqual(el.__createdList, ['1', '2', 'sup', '3', '4', 'sub'], 'created list wrong'); }); test('observedAttributes when extended', function() { const el = fixture('extended-observed-attributes'); assert.equal(el.b1, 'b1'); assert.equal(el.e1, 'e1'); assert.equal(el.b2, 'b2'); assert.equal(el.e2, 'e2'); }); }); </script> </body> </html>