From eaca195467ab92d81ce5bb837b3d4a5496ab9ad9 Mon Sep 17 00:00:00 2001 From: Steven Orvell Date: Wed, 29 Jan 2020 17:54:02 -0800 Subject: [PATCH] Introduce opt-out per class for `legacyNoObservedAttributes` This is necessary when a native property that sets an attribute should be observeable via attributeChangedCallback or Polymer property reflection (e.g. element.tabIndex = 5 setting the `tabindex` attribute). --- lib/legacy/legacy-element-mixin.js | 11 +++++++---- lib/legacy/polymer-fn.js | 4 ++++ test/unit/legacy-noattributes.html | 22 +++++++++++++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/legacy/legacy-element-mixin.js b/lib/legacy/legacy-element-mixin.js index 8939ecbf79..e06b371ae7 100644 --- a/lib/legacy/legacy-element-mixin.js +++ b/lib/legacy/legacy-element-mixin.js @@ -104,6 +104,8 @@ export const LegacyElementMixin = dedupingMixin((base) => { this.__isUpgradeDisabled; /** @type {boolean|undefined} */ this.__needsAttributesAtConnected; + /** @type {boolean|undefined} */ + this._legacyForceObservedAttributes; } /** @@ -143,7 +145,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { /** @override */ setAttribute(name, value) { - if (legacyNoObservedAttributes) { + if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) { const oldValue = this.getAttribute(name); super.setAttribute(name, value); // value coerced to String for closure's benefit @@ -155,7 +157,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { /** @override */ removeAttribute(name) { - if (legacyNoObservedAttributes) { + if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) { const oldValue = this.getAttribute(name); super.removeAttribute(name); this.__attributeReaction(name, oldValue, null); @@ -166,7 +168,8 @@ export const LegacyElementMixin = dedupingMixin((base) => { // NOTE: Inlined for perf from version of DisableUpgradeMixin. static get observedAttributes() { - if (legacyNoObservedAttributes) { + if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) { + this.prototype._legacyForceObservedAttributes = this._legacyForceObservedAttributes; // Ensure this element is property registered with the telemetry system. if (!this.hasOwnProperty(JSCompiler_renameProperty('__observedAttributes', this))) { this.__observedAttributes = []; @@ -315,7 +318,7 @@ export const LegacyElementMixin = dedupingMixin((base) => { this.root = /** @type {HTMLElement} */(this); this.created(); // Pull all attribute values 1x if `legacyNoObservedAttributes` is set. - if (legacyNoObservedAttributes) { + if (legacyNoObservedAttributes && !this._legacyForceObservedAttributes) { if (this.hasAttributes()) { this._takeAttributes(); // Element created from scratch or parser generated diff --git a/lib/legacy/polymer-fn.js b/lib/legacy/polymer-fn.js index de019be6a9..4ca4511e50 100644 --- a/lib/legacy/polymer-fn.js +++ b/lib/legacy/polymer-fn.js @@ -39,6 +39,10 @@ const Polymer = function(info) { } else { klass = Polymer.Class(info); } + // Copy opt out for `legacyNoObservedAttributes` from info object to class. + if (info._legacyForceObservedAttributes) { + klass._legacyForceObservedAttributes = info._legacyForceObservedAttributes; + } customElements.define(klass.is, /** @type {!HTMLElement} */(klass)); return klass; }; diff --git a/test/unit/legacy-noattributes.html b/test/unit/legacy-noattributes.html index 6e5399f1cc..56bf947a12 100644 --- a/test/unit/legacy-noattributes.html +++ b/test/unit/legacy-noattributes.html @@ -64,7 +64,7 @@ zot: Boolean, shouldIf: Boolean, camelCase: String, - disabled: {type: Boolean, value: 'true'} + disabled: {type: Boolean, value: 'true'}, }, created() { this.wasCreated = true; @@ -74,6 +74,17 @@ this.attrInfo = {name, old, value}; } }); + + Polymer({ + is: 'x-native-attrs-force', + _legacyForceObservedAttributes: true, + properties: { + tabindex: {reflectToAttribute: true, type: Number} + }, + attributeChanged(name, old, value) { + this.attributeChangedCalled = true; + } + }); @@ -106,6 +117,15 @@ assert.ok(def); }); + test('native property observeable via `attributeChanged` and reflected to property when `_forceObservedAttributes` is used', () => { + el = document.createElement('x-native-attrs-force'); + document.body.appendChild(el); + el.tabIndex = 5; + assert.ok(el.attributeChangedCalled); + assert.equal(el.tabindex, 5); + document.body.removeChild(el); + }); + test('static attributes', () => { assert.equal(el.foo, 'foo'); assert.equal(el.$.child1.getAttribute('foo'), 'x-attrs.foo');