-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2.x] Add legacy-data-mixin as 1.x->2.x migration aide. Fixes #5262. #5266
Conversation
This is an alternate implementation of #5263, based on same concept there, with the goal of making the code to implement the legacy behavior purely opt-in. cc: @beckysiegel |
Two affordances were made in core to enable writing such a mixin:
These seem like generally good changes on their own. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small nits. Other than that LGTM!
lib/legacy/legacy-data-mixin.html
Outdated
if (this._legacyUndefinedCheck && args.length > 1) { | ||
for (let i=0; i<args.length; i++) { | ||
if (vals[i] === undefined) { | ||
throw new UndefinedArgumentError(`argument '${args[i].name}' is undefined; ensure it has an undefined check`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, we are throwing an error here to break out of the control flow, as code later executed would erroneously run? If that is correct, maybe add a comment to make this explicit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lib/legacy/legacy-data-mixin.html
Outdated
}); | ||
|
||
const Class = Polymer.Class; | ||
Polymer.Class = info => Class(info, Polymer.LegacyDataMixin); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would break if a mixin was passed in to this function. Therefore, I think the mixin should be optional and the mixin should be conditionally applied to this mixin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good catch. The mixin option was a late addition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, good catch. Updated.
test/unit/legacy-data.html
Outdated
assert.equal(el.static.callCount, 1); | ||
assert.equal(el.one.callCount, 1); | ||
assert.equal(el.two.callCount, 0); | ||
assert.isTrue(console.warn.calledOnce); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make this more uniform, make this the same as line 239. E.g. el.two.callCount, 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, thanks.
Also the tests appear to fail on IE11, likely related to how it handles exceptions. And the latest version of shadydom appears to be now failing some of our tests. We have to look into these regressions separately. |
Thanks! |
lib/legacy/legacy-data-mixin.html
Outdated
(function() { | ||
'use strict'; | ||
|
||
const UndefinedArgumentError = class extends Error {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want this error to have a meanifulful name this pattern should be followed https://azimi.me/2015/09/23/high-custom-error-class-es6.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
* @return {Array<*>} Array of argument values | ||
* @private | ||
*/ | ||
_marshalArgs(args, path, props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function has a non-trivial cyclical complexity score, can you please add unit tests to ensure the functionality is correct. Also it might be a sign that the logic should be split up into smaller more meaningful functions to maintain a higher degree of readability.
* | ||
* @param {!Array<!MethodArg>} args Array of argument metadata | ||
* @param {string} path Property/path name that triggered the method effect | ||
* @param {Object} props Bag of current property changes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is a bag? perhaps restate as a "hash" or key value collection of ...
test/unit/legacy-data.html
Outdated
</head> | ||
<body> | ||
|
||
<script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you drop this since it is empty?
test/unit/legacy-data.html
Outdated
document.body.removeChild(el); | ||
}); | ||
}); | ||
// ------------------------------------------------ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this comment needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did some refactoring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, much nicer!
* | ||
* When loaded, all legacy elements (defined with `Polymer({...})`) | ||
* will have the mixin applied. The mixin only restores legacy data handling | ||
* if `_legacyUndefinedCheck: true` is set on the element's prototype. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason why we are _ prefixing this prototype value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a convention, we generally underscore any property not intended as part of the DOM element's public API, and consider them "protected". https://www.polymer-project.org/2.0/docs/devguide/properties#private-and-protected-properties
test/unit/legacy-data.html
Outdated
el.a = undefined; | ||
el.b = undefined; | ||
assert.equal(el.static.callCount, 1); | ||
assert.equal(el.one.callCount, 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of callCount spy's can we sideeffect in the calls by incrementing a counter and then the tests are totally isolated without having to spy or mock on anything.
For example
foo(a, b) {
this.fooCall++;
}
test/unit/legacy-data.html
Outdated
</test-fixture> | ||
|
||
<script> | ||
suite('imperative', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This suite of tests has a lot of duplication I think we could do with some test helpers to significantly clean up the test implementation to make the code easier to maintain and easier to reason about.
test/unit/shady-content.html
Outdated
if (!window.customElements) { | ||
window.customElements = {}; | ||
if (window.customElements) { | ||
customElements.forcePolyfill = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this change seems unrelated to the intent of the CL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separated out to #5268
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you
Looks like Computed bindings (https://www.polymer-project.org/1.0/docs/devguide/data-binding#annotated-computed) are not handled by this mixin. |
@vdonich Thanks for the report. I confirmed that |
I've hit another snag when using wildcards. Observer is being called despite undefined property value.
Here's the jsfiddle with example: |
Add legacy-data-mixin as 1.x->2.x migration aide.
Mixin to selectively add back Polymer 1.x's
undefined
rules governing when observers & computing functions run based on all arguments being defined (reference https://www.polymer-project.org/1.0/docs/devguide/observers#multi-property-observers).When
polymer/lib/legacy/legacy-data-mixin.html
is loaded at the app level, all legacy elements (defined withPolymer({...})
) will have the mixin applied. The mixin only restores legacy data handling if_legacyUndefinedCheck: true
is set on the element's prototype.This mixin is intended for use to help migration from Polymer 1.x to 2.x+ by allowing legacy code to work during the migration period, while identifying observers and computing functions that need undefined checks to work without the mixin in Polymer 2. It is generally not intended for use in production.
Reference Issue
Fixes #5262.
Supersedes and closes #5263