]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
test(e2e): add scroll behavior test
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 3 Apr 2020 09:25:31 +0000 (11:25 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 3 Apr 2020 09:25:31 +0000 (11:25 +0200)
e2e/scroll-behavior/index.html [new file with mode: 0644]
e2e/scroll-behavior/index.ts [new file with mode: 0644]
e2e/scroll-behavior/scrollWaiter.ts [new file with mode: 0644]
src/index.ts
src/router.ts

diff --git a/e2e/scroll-behavior/index.html b/e2e/scroll-behavior/index.html
new file mode 100644 (file)
index 0000000..e17d8b3
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+    <title>Vue Router e2e tests - Scroll Behavior</title>
+    <!-- TODO: replace with local imports for promises and anything else needed -->
+    <script src="https://polyfill.io/v3/polyfill.min.js?features=default%2Ces2015"></script>
+
+    <style>
+      .fade-enter-active,
+      .fade-leave-active {
+        transition: opacity 0.5s ease;
+      }
+      .fade-enter,
+      .fade-leave-active {
+        opacity: 0;
+      }
+      .view {
+        border: 1px solid red;
+        height: 4500px;
+        position: relative;
+      }
+    </style>
+  </head>
+  <body>
+    <a href="/">&lt;&lt; Back to Homepage</a>
+    <hr />
+
+    <div id="app">
+      <h1>Scroll Behavior</h1>
+      <ul>
+        <li><router-link to="/">/</router-link></li>
+        <li><router-link to="/foo">/foo</router-link></li>
+        <li><router-link to="/bar">/bar</router-link></li>
+        <li><router-link to="/bar#anchor">/bar#anchor</router-link></li>
+        <li><router-link to="/bar#anchor2">/bar#anchor2</router-link></li>
+        <li><router-link to="/bar#1number">/bar#1number</router-link></li>
+      </ul>
+      <transition
+        name="fade"
+        mode="out-in"
+        @before-enter="flushWaiter"
+        @before-leave="setupWaiter"
+      >
+        <router-view class="view"></router-view>
+      </transition>
+    </div>
+  </body>
+</html>
diff --git a/e2e/scroll-behavior/index.ts b/e2e/scroll-behavior/index.ts
new file mode 100644 (file)
index 0000000..ff5863b
--- /dev/null
@@ -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: '<div class="home">home</div>' }
+const Foo: RouteComponent = { template: '<div class="foo">foo</div>' }
+const Bar: RouteComponent = {
+  template: `
+    <div class="bar">
+      bar
+      <div style="height:1500px"></div>
+      <p id="anchor" style="height:500px">Anchor</p>
+      <p id="anchor2" style="height:500px">Anchor2</p>
+      <p id="1number">with number</p>
+    </div>
+  `,
+}
+
+// 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<ScrollBehavior>
+
+    // 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 (file)
index 0000000..056b42c
--- /dev/null
@@ -0,0 +1,30 @@
+function createScrollWaiter() {
+  let resolve: (() => void) | undefined
+  let promise: Promise<any> | 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()
index 8aefcd07e23d37746465d83db7cdac43fdd28b11..ba7b0ba5cf538315924402c84f0242ddb1969d4b 100644 (file)
@@ -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'
index c4e6ccb8171b5398ef443887f08c249ae7864448..07287deb6f1fa375435c54731c0118cbf6aafbf5 100644 (file)
@@ -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<ScrollPosition>
+  ): // TODO: implement false nad refactor promise based type
+  | ScrollPosition
+    | Promise<ScrollPosition | false | undefined>
+    | 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 = {