Skip to content
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

Stickyjs improvements #7139

Merged
merged 18 commits into from
Jun 27, 2017
Merged

Conversation

vovayatsyuk
Copy link
Member

@vovayatsyuk vovayatsyuk commented Oct 22, 2016

  1. Added _sticky class for "stuck" element
  2. Added spacingTop feature (Added spacingTop feature to sticky plugin. #6536)
  3. Added stickAfter feature, that allows postponing sticking, until element will go out from the screen for stickAfter pixels.

Screenshots

Default Settings

sticky-default

With spacingTop = 25

sticky-spacing-top

With spacingTop = 25 and stickAfter = 60

sticky-spacing-top-and-stick-after

@vasiliyseleznev
Copy link

vasiliyseleznev commented Oct 25, 2016

Hi @vovayatsyuk,

Thanks for second loop with this improvement. We will go ahead and accept it.
Internal ticket: MAGETWO-60046

@MomotenkoNatalia MomotenkoNatalia added the Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development label Oct 26, 2016
@okorshenko
Copy link
Contributor

@vovayatsyuk could you please resolve conflicts so that we can proceed with PR acceptance?

@vovayatsyuk
Copy link
Member Author

Done.

@okorshenko
Copy link
Contributor

@vovayatsyuk thank you for resolving the merge conflicts

@okorshenko okorshenko self-assigned this Mar 17, 2017
@okorshenko okorshenko added this to the March 2017 milestone Mar 17, 2017
@@ -41,8 +44,34 @@ define([

if (!isStatic && this.element.is(':visible')) {
offset = $(document).scrollTop() - this.parentOffset;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this line of code? Variable offset will be redefined in any case in line 49 or 51.
Could you please review this logic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing the point, but it's not redefined at those lines, it just modified with the possible spacingTop parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, can you comment on this? I don't understand how you'd like to modify the source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good. Thank you

Copy link

@zetlen zetlen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some readability issues and at least one bug, but mostly this is great!

@@ -41,8 +44,34 @@ define([

if (!isStatic && this.element.is(':visible')) {
offset = $(document).scrollTop() - this.parentOffset;

if (typeof this.options.spacingTop === 'function') {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check to see whether spacingTop is a Number, or whether it returns a Number.

var spacingTop = this.options.spacingTop || 0;
if (typeof this.options.spacingTop === 'function') {
  spacingTop = this.options.spacingTop();
}
spacingTop = Number(spacingTop);
if (isNaN(spacingTop)) {
  throw new Error('sticky: spacingTop must be a Number');
}
offset += this.options.spacingTop;

See below for more notes.

offsetTop = this.options.offsetTop();
} else {
offsetTop = this.options.offsetTop;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern happens twice, and there may be further configurable numbers, so it probably should be separated into a private function.
At the top of this file:

function optionToNumber(self, option) {
  var value = self.options[option] || 0;
  if (typeof value === 'function') {
    value = self.options[option]();
  }
  var converted = Number(value);
  if (isNaN(converted)) {
    throw Error('sticky: Could not convert supplied option "' + option + '" to Number from "' + value + '"');
  }
  return converted;
}

And then here, you can just have:

var offsetTop = optionToNumber(this, 'offsetTop');
if (offset < offsetTop) {
  offset = 0;
}

And above, you can just have:

offset += optionToNumber(this, 'spacingTop');

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice optimization, but is it ok to send this? Maybe it's better to place the function inside the widget to get rid of this parameter? I know the function is not needed there, but I'm not sure what is better: "send this" or create a function inside the widget?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay to send this from an object method to a locally declared function. It's a way to do real privacy in JavaScript. I see it in lots of codebases! But the way that you implemented it, as an underscore-prefixed method, is also just fine.

offsetTop = this.options.offsetTop;
}

if (offset < this.options.offsetTop) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is a bug; this.options.offsetTop may still be a function. You might have means if (offset < offsetTop). The refactor I suggest in my above comments should fix it, though.


if (offset &&
this.options.offsetTop &&
!this.element.hasClass(this.options.stickyClass)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain the desired logic here? It's hard to tell from the code what !this.element.hasClass(this.options.stickyClass) means for the business logic. Does it mean "the element is currently displayed as sticky" or "the element is NOT currently displayed as sticky"? For more readability, you could assign it to a variable stuck.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic inside of this if should work for the non-sticked element, when offsetTop is used. (It makes smooth return of the items with offsetTop parameter, when scrolling up)

In other words, I check if an element should be stuck (offset > 0), offsetTop is used and an element is not yet stuck.

@vherasymenko
Copy link

@vovayatsyuk Hi. I can`t apply your improvement.
I did checkout your branch. And I do not have any changes in my application. Behavior is still old.

@vovayatsyuk
Copy link
Member Author

You should redeploy static content:

Quick solution:

  1. Remove old file:

    $ bin/magento deploy:mode:set developer
    $ find pub/static -regex ".*/sticky.js" -exec rm -rf {} \;;
    
  2. Refresh the page in browser

@vherasymenko
Copy link

I do it same.

I Also try apply your commit like a patch for my project and nothing changing.

@vovayatsyuk
Copy link
Member Author

Sorry, it's a bit off topic to talk about magento deployment here.

If you'd like to see how the patch is working, you can open deployed file and apply the changes manually.

If you use English locale and luma theme, you'll find the file right here: pub/static/frontend/Magento/luma/en_US/mage/sticky.js

If that will not work for you, I really don't know how to help you, sorry.

p.s. did you flush browser cache?
p.p.s What changes do you want to see? My changes do not affect any visible parts of magento. It's up to third-party developers to use the extended functionality.

@okorshenko okorshenko modified the milestones: May 2017, June 2017 Jun 1, 2017
stickyClass: '_sticky'
},

_optionToNumber(option) {
Copy link
Contributor

@omiroshnichenko omiroshnichenko Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct function declaration. Move it out of widget in to module private scope or declare this like method of widget. Also, i have a question, why are you check on type of function offsetTop and spacingTop when it's declared below as Number?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thank you @omiroshnichenko, I missed that. ES6 object shorthand is not currently allowed in Magento JavaScript. This should change to:

_optionToNumber: function(option) {

Copy link
Contributor

@omiroshnichenko omiroshnichenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, validate it with static test locallygrunt static --file=lib/web/mage/sticky.js or rerun travis build.


var converted = Number(value);
if (isNaN(converted)) {
throw Error(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have practice to throwing errors in magento js.

@@ -11,7 +11,26 @@ define([

$.widget('mage.sticky', {
options: {
container: ''
container: '',
spacingTop: 0,
Copy link
Contributor

@omiroshnichenko omiroshnichenko Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have been provide new settings properties that by default is Number, no reason to check it on another types. User can't change this property, only developer, when developer will be configuring sticky widget he can look at type of parameter. Also, offsetTop it's native property of HTMLElement would be nice to change it to another, e.g. blockOffsetTop.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still thinking about a new name for it 😫

Copy link
Member Author

@vovayatsyuk vovayatsyuk Jun 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you say to rename it to stickAfter or triggerOffset or stickOffset?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've picked a stickAfter. It seems that it better and self-explanatory name.

var stuck = this.element.hasClass(this.options.stickyClass);
if (offset && this.options.offsetTop && !stuck) {
var offsetTop = this._optionToNumber('offsetTop');
if (offset < offsetTop) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check can be include in upper if statement via AND(&&) operator.

@okorshenko
Copy link
Contributor

Hi @vovayatsyuk
Could you please check review comments?

@vovayatsyuk
Copy link
Member Author

Sorry for the delay. I think I'm done.

_getOptionValue: function (option) {
var value = this.options[option] || 0;

if (typeof value === 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you check value on function type? In what case it can be function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be useful to set a function for stickAfter parameter that will calculate sticky element height when you wish to stick element after it goes out of the viewport and the element could change its height dynamically (shopping cart for example)
You can use different spacing top value for different screen size/orientation also.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but can you leave docblock with description. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@magento-team magento-team merged commit 62b822c into magento:develop Jun 27, 2017
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Frontend improvement Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants