Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[feat] Always scroll to top on router change #2309

Merged
merged 4 commits into from
Apr 21, 2020
Merged

[feat] Always scroll to top on router change #2309

merged 4 commits into from
Apr 21, 2020

Conversation

brendanfalkowski
Copy link
Contributor

Description

On router changes, the page should always scroll to top.

This also removes prior attempts to manage scrolling-to-top per route. This should be a universal behavior.

Related Issue

Closes #2289.

Acceptance

Verification Stakeholders

Specification

Verification Steps

  1. Go to any page.
  2. Scroll down.
  3. Click a link.
  4. The page should scroll up to top.

Screenshots / Screen Captures (if appropriate)

Checklist

  • I have updated the documentation accordingly, if necessary.
  • I have added tests to cover my changes, if necessary.

Removes prior attempts to manage scrolling to top per route. This should be a universal behavior.
@brendanfalkowski
Copy link
Contributor Author

I removed window.scrollTo from the PDP since that is handled by the global routing behavior.

I did not remove window.scrollTo from the category/search areas because some changes like sorting don't invoke the router. These inline calls are still needed.

@tjwiebell tjwiebell added CI_APPROVED This PR should be included in the CI process. Event: Global-Contribution-Day Groomed for GCD 2020 labels Apr 5, 2020
@PWAStudioBot
Copy link
Contributor

PWAStudioBot commented Apr 5, 2020

Messages
📖

Access a deployed version of this PR here. Make sure to wait for the "pwa-pull-request-deploy" job to complete.

📖 DangerCI Failures related to missing labels/description/linked issues/etc will persist until the next push or next nightly build run (assuming they are fixed).

Generated by 🚫 dangerJS against a51749e

Copy link
Contributor

@jimbo jimbo left a comment

Choose a reason for hiding this comment

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

@brendanfalkowski Thanks for contributing this, it makes sense to me.

I was concerned that the browser wouldn't be able to preserve scroll position for a history entry if we did this, but after trying your branch locally, it seems to work fine. That is, going forward or backward in browser history seems to return the page to the scroll position, which is good.

I'm inclined to say that the ScrollToTop component should be a custom hook instead, though. Typically a component should return an element, or at least render an element under certain conditions. Here, it's only being used as a vessel for a side effect, a case in which we normally just use a custom hook.

Would this work?

const Routes = () => {
  useScrollToTop()

  return (
    <Suspense fallback={fullPageLoadingIndicator}>
      <Switch />
    </Suspense>
  )
}

// effect because the re-render happens before smooth scrolling starts. Some
// browsers try to do this internally but it's not universal.

const ScrollToTop = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we're migrated to function components this should be a peregrine hook, useScrollTop. It could even be useScrollTopOnChange and take a parameter like pathname which it uses in the effect. Then any component could configure when it should change such as useScrollTopOnChange(user.isSignedIn), etc.

Could also have useScrollTopOnMount that scrolls only on mount of a component.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just saw Jimmy said basically the same thing a few days ago 😆

sirugh
sirugh previously requested changes Apr 14, 2020
Copy link
Contributor

@sirugh sirugh left a comment

Choose a reason for hiding this comment

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

This is a nice change, and should obviate a lot of repeated code throughout the app. That said, please make this a custom Peregrine hook :)

Naming of the hook is up for debate but I don't think we should hard code the variable (pathname in your component) into the hook as other components may want to use the behavior on some different change. I would actually prefer it be a hook that accepts a variable on which to trigger the event. For example:

const Routes = () => {
  const { pathname } = useLocation();
  useScrollTopOnChange(pathname);

  return (...)
}

As always, we'd be happy to make these changes if you are occupied with more important things :) Let us know!

@sirugh sirugh added the version: Minor This changeset includes functionality added in a backwards compatible manner. label Apr 21, 2020

const CartPage = lazy(() => import('../CartPage'));
const CheckoutPage = lazy(() => import('../CheckoutPage'));
const CreateAccountPage = lazy(() => import('../CreateAccountPage'));
const Search = lazy(() => import('../../RootComponents/Search'));

const Routes = () => {
const { pathname } = useLocation();
useScrollTopOnChange(pathname);
Copy link
Contributor

Choose a reason for hiding this comment

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

Some other examples I saw out on the web included also observing search. I'm not sure if we want to implement that here or in the components that actually care about search such as pagination or the search/filter components.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it makes sense to do it here. Those components are changing search but scrolling is a page-level concern; I don't think they should know / care / control the scrolling.

Copy link
Contributor

@jimbo jimbo Apr 21, 2020

Choose a reason for hiding this comment

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

Edit: I wouldn't scroll to top when search params change. Yes, scrolling is a page-level concern, but so is changing the URL—even search.

It's reasonable for a component that changes location, which is global state, to have an opinion on whether the page should scroll. Also, on the web we try to minimize the number of overflowing containers we have, so it ends up being fairly common for a component to scroll the window.

Anyway, the issue I see with scroll-to-top on search change is that we can't really disable that behavior on a case-by-case basis. We'd have to accept that it'll happen in every case where we ever change search...and I don't have that level of confidence. In contrast, I do have that level of confidence when it comes to pathname.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

We'd have to accept that it'll happen in every case where we ever change search...and I don't have that level of confidence. In contrast, I do have that level of confidence when it comes to pathname.

To be specific, the Router component can confidently control scrolling on path changes but not search changes. That said, other components such as the Category or Search pages should be able to utilize the new hook to handle scrolling to top as they do already.

// CURRENT
const Category = props => {
    const { search } = useLocation();
    const filtersFromSearch = getFilters(search);
    useEffect(() => {
        runFilterQuery()
        window.scrollTop();
    }, [filtersFromSearch])    
}
// PROPOSED
const Category = props => {
    const { search } = useLocation();
    const filtersFromSearch = getFilters(search);
    useScrollTopOnChange(filtersFromSearch);
    useEffect(() => {
        runFilterQuery()
    }, [filtersFromSearch])
}

@sirugh sirugh changed the title Closes #2289 always scroll to top on router change [feat] Always scroll to top on router change Apr 21, 2020
Copy link
Contributor

@supernova-at supernova-at left a comment

Choose a reason for hiding this comment

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

Approved, and I'd also add search.


const CartPage = lazy(() => import('../CartPage'));
const CheckoutPage = lazy(() => import('../CheckoutPage'));
const CreateAccountPage = lazy(() => import('../CreateAccountPage'));
const Search = lazy(() => import('../../RootComponents/Search'));

const Routes = () => {
const { pathname } = useLocation();
useScrollTopOnChange(pathname);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it makes sense to do it here. Those components are changing search but scrolling is a page-level concern; I don't think they should know / care / control the scrolling.

@devops-pwa-codebuild
Copy link
Collaborator

Performance Test Results

The following fails have been reported by WebpageTest. These numbers indicates a possible performance issue with the PR which requires further manual testing to validate.

https://pr-2309.pwa-venia.com/venia-tops.html : LH Performance Expected 0.75 Actual 0.65

@sirugh sirugh dismissed their stale review April 21, 2020 20:18

I made changes.

@dpatil-magento dpatil-magento merged commit 507c48c into magento:develop Apr 21, 2020
@brendanfalkowski brendanfalkowski deleted the 2289-always-scroll-top-on-router-change branch January 30, 2021 01:52
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
CI_APPROVED This PR should be included in the CI process. Event: Global-Contribution-Day Groomed for GCD 2020 pkg:peregrine pkg:venia-ui version: Minor This changeset includes functionality added in a backwards compatible manner.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[feature]: Always scroll to top on a router change
8 participants