From 3245ddb4043c322603319b77bd82286744292ee1 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Fri, 3 Apr 2020 11:25:31 +0200 Subject: [PATCH] test(e2e): add scroll behavior test --- e2e/scroll-behavior/index.html | 51 ++++++++++++++++++ e2e/scroll-behavior/index.ts | 84 +++++++++++++++++++++++++++++ e2e/scroll-behavior/scrollWaiter.ts | 30 +++++++++++ src/index.ts | 8 ++- src/router.ts | 10 ++-- 5 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 e2e/scroll-behavior/index.html create mode 100644 e2e/scroll-behavior/index.ts create mode 100644 e2e/scroll-behavior/scrollWaiter.ts diff --git a/e2e/scroll-behavior/index.html b/e2e/scroll-behavior/index.html new file mode 100644 index 00000000..e17d8b3e --- /dev/null +++ b/e2e/scroll-behavior/index.html @@ -0,0 +1,51 @@ + + + + + + + Vue Router e2e tests - Scroll Behavior + + + + + + + << Back to Homepage +
+ +
+

Scroll Behavior

+ + + + +
+ + diff --git a/e2e/scroll-behavior/index.ts b/e2e/scroll-behavior/index.ts new file mode 100644 index 00000000..ff5863ba --- /dev/null +++ b/e2e/scroll-behavior/index.ts @@ -0,0 +1,84 @@ +import { createRouter, createWebHistory, ScrollBehavior } from '../../src' +import { RouteComponent } from '../../src/types' +import { createApp } from 'vue' +import { scrollWaiter } from './scrollWaiter' + +const Home: RouteComponent = { template: '
home
' } +const Foo: RouteComponent = { template: '
foo
' } +const Bar: RouteComponent = { + template: ` +
+ bar +
+

Anchor

+

Anchor2

+

with number

+
+ `, +} + +// scrollBehavior: +// - only available in html5 history mode +// - defaults to no scroll behavior +// - return false to prevent scroll +const scrollBehavior: ScrollBehavior = async function(to, from, savedPosition) { + await scrollWaiter.promise + + if (savedPosition) { + // savedPosition is only available for popstate navigations. + return savedPosition + } else { + let position: ReturnType + + // scroll to anchor by returning the selector + if (to.hash) { + position = { selector: to.hash } + + // specify offset of the element + if (to.hash === '#anchor2') { + // TODO: allow partial { y: 100 } + position.offset = { x: 0, y: 100 } + } + + // bypass #1number check + if (/^#\d/.test(to.hash) || document.querySelector(to.hash)) { + return position + } + + // if the returned position is falsy or an empty object, + // will retain current scroll position. + return false + } + + // check if any matched route config has meta that requires scrolling to top + if (to.matched.some(m => m.meta.scrollToTop)) { + // coords will be used if no selector is provided, + // or if the selector didn't match any element. + return { x: 0, y: 0 } + } + + return false + } +} + +const webHistory = createWebHistory('/' + __dirname) +const router = createRouter({ + history: webHistory, + scrollBehavior, + routes: [ + { path: '/', component: Home, meta: { scrollToTop: true } }, + { path: '/foo', component: Foo }, + { path: '/bar', component: Bar, meta: { scrollToTop: true } }, + ], +}) +const app = createApp({ + setup() { + return { + flushWaiter: scrollWaiter.flush, + setupWaiter: scrollWaiter.add, + } + }, +}) +app.use(router) + +window.vm = app.mount('#app') diff --git a/e2e/scroll-behavior/scrollWaiter.ts b/e2e/scroll-behavior/scrollWaiter.ts new file mode 100644 index 00000000..056b42c0 --- /dev/null +++ b/e2e/scroll-behavior/scrollWaiter.ts @@ -0,0 +1,30 @@ +function createScrollWaiter() { + let resolve: (() => void) | undefined + let promise: Promise | undefined + + function add() { + promise = new Promise(r => { + resolve = resolve + }) + } + + function flush() { + resolve && resolve() + resolve = undefined + promise = undefined + } + + const waiter = { + promise, + add, + flush, + } + + Object.defineProperty(waiter, 'promise', { + get: () => promise, + }) + + return waiter +} + +export const scrollWaiter = createScrollWaiter() diff --git a/src/index.ts b/src/index.ts index 8aefcd07..ba7b0ba5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,7 +26,13 @@ export { NavigationGuard, PostNavigationGuard, } from './types' -export { createRouter, Router, RouterOptions, ErrorHandler } from './router' +export { + createRouter, + Router, + RouterOptions, + ErrorHandler, + ScrollBehavior, +} from './router' export { onBeforeRouteLeave } from './navigationGuards' export { Link, useLink } from './components/Link' diff --git a/src/router.ts b/src/router.ts index c4e6ccb8..07287deb 100644 --- a/src/router.ts +++ b/src/router.ts @@ -58,12 +58,16 @@ export type ErrorHandler = (error: any) => any // resolve, reject arguments of Promise constructor type OnReadyCallback = [() => void, (reason?: any) => void] -interface ScrollBehavior { +export interface ScrollBehavior { ( to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, savedPosition: ScrollToPosition | null - ): ScrollPosition | Promise + ): // TODO: implement false nad refactor promise based type + | ScrollPosition + | Promise + | false + | undefined } export interface RouterOptions { @@ -535,7 +539,7 @@ export function createRouter({ await nextTick() const position = await scrollBehavior(to, from, scrollPosition || null) console.log('scrolling to', position) - scrollToPosition(position) + position && scrollToPosition(position) } const router: Router = { -- 2.39.5