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
✨amp-consent: Added a default loading placeholder for the consent UI #19841
✨amp-consent: Added a default loading placeholder for the consent UI #19841
Conversation
<defs> | ||
<linearGradient id="grad"> | ||
<stop stop-color="rgb(105, 105, 105)"></stop> | ||
<stop offset="100%" stop-color="rgba(105, 105, 105, 0)"></stop> |
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.
I had to update the code to make it work on Safari correctly. You'll need to do:
stop-color="rgb(105, 105, 105) stop-opacity="0"
instead here
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! Will update now 😄
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.
Looks good to me with a few nits. Before merging the change, please make sure to sync with @spacedino and show them the demo on several real devices.
placeholder: 'i-amphtml-consent-placeholder', | ||
loading: 'i-amphtml-consent-ui-loading', | ||
fill: 'i-amphtml-consent-ui-fill', | ||
loadingPlaceholder: 'i-amphtml-consent-ui-loading-placeholder', |
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.
Nit: Well, placeholder by default means loading placeholder to me. Can we name it placeholder: i-amphtml-consent-ui-placeholder
instead : ) A shorter name
classList.add(consentUiClasses.placeholder); | ||
classList.add(consentUiClasses.loadingPlaceholder); | ||
|
||
const loadingSpinner = htmlFor(placeholder)` |
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.
nit: Is it possible to declare the string as a const at the top of the file?
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.
I believe @kristoferbaxter was looking into doing a compilation pass to do this, so it may not be worth worrying about.
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.
Yeah I originally had it as a const at the top of the file, but from what @kristoferbaxter showed me, it appeared to be using the htmlFor
keyword in order to find the template strings to then be optimized. Thus I did it this way to help with that.
Though I may be wrong, we will see what Kris says 😄
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.
Since this htmlFor
usage is within a method, it will be hoisted automatically in the single pass build. Makes the most sense within source code to place it where it is easiest to understand.
Please note: This optimization is only true in the single-pass build.
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.
Awesome! Thanks!
So I tried hoisting this to the top of the file, and the linter then throws syntax errors. Also, tried binding it with ${placeholderHtml}
, but the template can't take in variables. I think we may need to do some additional work to allow the codebase to handle this.
Doing a quick project search, seems htmlFor()
always has the template immediately after it.
@@ -41,19 +41,35 @@ amp-consent.amp-hidden { | |||
visibility: hidden; | |||
} | |||
|
|||
amp-consent.i-amphtml-consent-loading { | |||
/* TODO: Finalize the placeholder design */ | |||
@keyframes loading-placeholder-spin { |
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.
maybe prefix with amp-consent into the name of this, since keyframe names are global
// Pop onto the back of the event queue, | ||
// so we expect() once our mutate element in show() resolves | ||
return mockInstance.mutateElement(() => {}).then(() => { | ||
expect(placeholder.hasAttribute('hidden')).to.not.be.ok; |
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.
how about expect(placeholder.hidden).to.be.false
?
// so we expect() once our mutate element in show() resolves | ||
return mockInstance.mutateElement(() => {}).then(() => { | ||
expect(placeholder.hasAttribute('hidden')).to.not.be.ok; | ||
expect(placeholder.childNodes.length).to.be.above(0); |
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.
try expect(placeholder.childNodes).to.not.be.empty
@@ -238,12 +239,25 @@ export class ConsentUI { | |||
* @return {!Element} | |||
*/ | |||
createPlaceholder_() { | |||
// TODO(@zhouyx): Allow publishers to provide placeholder upon request | |||
const placeholder = this.parent_.ownerDocument.createElement('placeholder'); | |||
toggle(placeholder, false); | |||
const {classList} = placeholder; |
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.
And can remove this now.
const {classList} = placeholder; |
const placeholder = this.parent_.ownerDocument.createElement('placeholder'); | ||
toggle(placeholder, false); | ||
const {classList} = placeholder; | ||
classList.add(consentUiClasses.fill); | ||
classList.add(consentUiClasses.placeholder); | ||
classList.add(consentUiClasses.loadingPlaceholder); |
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.
Since you're only add
ing a single class to the classList
, you can remove the line above and just call directly.
classList.add(consentUiClasses.loadingPlaceholder); | |
placeholder.classList.add(consentUiClasses.loadingPlaceholder); |
</linearGradient> | ||
</defs> | ||
<path d="M11,4.4 A18,18, 0,1,0, 38,20" stroke="url(#grad)"></path> | ||
</svg>`; |
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're using this definition in multiple places, it also might be a good idea to store it externally and import into this file.
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 of now, we are only thinking of using this here. Was asking @sparhami about this in the past, if we had a system in place for re-used html elements, and it appears that we do not. So we should probably do that as well if we continue to add more HTML literals.
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.
Left some details about htmlFor
and a change for the classList
usage.
@sparhami @zhouyx @kristoferbaxter Replied to comments / Made requested changes 😄 |
toggle(dev().assertElement(this.ui_), false); | ||
this.win_.addEventListener('message', this.boundHandleIframeMessages_); | ||
insertAfterOrAtStart(this.parent_, dev().assertElement(this.ui_), null); | ||
return this.iframeReady_.promise; | ||
|
||
return this.baseInstance_.mutateElement(() => { |
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.
I think this would be a bit more natural as:
return Promise.all([
this.iframeReady_.promise,
this.baseInstance_.mutateElement(() => {
toggle(dev().assertElement(this.placeholder_), true);
}),
]);
Its not really a performance difference in this case, but seems to more clearly communicate that the two async things are independent.
@@ -238,12 +239,24 @@ export class ConsentUI { | |||
* @return {!Element} | |||
*/ | |||
createPlaceholder_() { | |||
// TODO(@zhouyx): Allow publishers to provide placeholder upon request | |||
const placeholder = this.parent_.ownerDocument.createElement('placeholder'); |
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.
I'm not sure if it is too late to change this, but you shouldn't use a tag name like placeholder
. A browser could add one with certain semantics that might be a problem. You should only ever use valid html tag names or include a dash in the name.
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.
I agree we should change this.
However, @zhouyx do you see any problem in changing this? Is there more code already in place around this idea?
@@ -142,6 +145,32 @@ describes.realWin('consent-ui', { | |||
}); | |||
}); | |||
|
|||
describe('placeholder', () => { | |||
it('should add a placeholder element' + |
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.
Having placeholder here feels a bit redundant. The test will read as "consent-ui placeholder should add a placeholder element while loading CMP Iframe". Consider something like "consent-ui placeholder should be present while loading CMP Iframe".
|
||
const placeholder = consentUI.placeholder_; | ||
expect(placeholder).to.be.ok; | ||
expect(placeholder.hasAttribute('hidden')).to.be.ok; |
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.
expect(placeholder.hidden).to.be.true;
|
||
// Pop onto the back of the event queue, | ||
// so we expect() once our mutate element in show() resolves | ||
return mockInstance.mutateElement(() => {}).then(() => { |
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.
Would be better to write this as an async test. Since it is just a test, no need to worry about the code generated to transform async/await into promises. That is:
should('...', async () => {
...
await mockInstance.mutateElement(() => {});
expect(...);
});
…p-consent-loading-placeholder
@sparhami @zhouyx @kristoferbaxter Replied to comments / Made requested changes 😄 |
Thanks for the approval @sparhami ! Going to wait for a design approval from @spacedino and then pull this in (for anyone wondering) |
Had the design review, Loading Placeholder and transition were approved. With some last design changes to be implemented documented here: #19969 Merging... |
…mpproject#19841) * Added the loading placeholder html * Added the svg, and renamed some classes for consistency * Finished styles for the placeholder spinner * Need to stop the height flash/jump thing * Finished up the loading placeholder * Started tests, used htmlFor * Added tests for placeholder * Fixed precommit checks * Added fix for safari on svg * Another safari fix for svg * Made all requested changes * Made requested changes * Made all requested changes
…mpproject#19841) * Added the loading placeholder html * Added the svg, and renamed some classes for consistency * Finished styles for the placeholder spinner * Need to stop the height flash/jump thing * Finished up the loading placeholder * Started tests, used htmlFor * Added tests for placeholder * Fixed precommit checks * Added fix for safari on svg * Another safari fix for svg * Made all requested changes * Made requested changes * Made all requested changes
relates to #18828
This adds a loading spinner provided by @sparhami , and available as a jsbin: https://jsbin.com/yerevoz/1/edit?html,output
Simply adds this svg and styling whenever a loading placeholder is not provided. Note: This placeholder is 30px x 30px as that is the only default height we can guarantee for the amp consent footer thing. Any ideas on how to size the placeholder would be appreciated 😄 👍