]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat(link): add active class
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 24 Jan 2020 16:37:55 +0000 (17:37 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 24 Jan 2020 16:37:55 +0000 (17:37 +0100)
__tests__/RouterLink.spec.ts
__tests__/__snapshots__/RouterLink.spec.ts.snap [new file with mode: 0644]
src/components/Link.ts

index 5d544c6ba293338ae24c3fafd522a2fded691df3..5200cbf56e80a60899ac586fdb28cee2c444f27f 100644 (file)
@@ -87,7 +87,7 @@ describe('RouterLink', () => {
       { to: locations.basic.string },
       locations.basic.normalized
     )
-    expect(el.innerHTML).toBe('<a href="/home">a link</a>')
+    expect(el.innerHTML).toBe('<a class="" href="/home">a link</a>')
   })
 
   it('displays a link with an object with path prop', () => {
@@ -96,7 +96,18 @@ describe('RouterLink', () => {
       { to: { path: locations.basic.string } },
       locations.basic.normalized
     )
-    expect(el.innerHTML).toBe('<a href="/home">a link</a>')
+    expect(el.innerHTML).toBe('<a class="" href="/home">a link</a>')
+  })
+
+  it('can be active', () => {
+    const { el } = factory(
+      locations.basic.normalized,
+      { to: locations.basic.string },
+      locations.basic.normalized
+    )
+    expect(el.innerHTML).toBe(
+      '<a class="router-link-active" href="/home">a link</a>'
+    )
   })
 
   it('calls ensureLocation', () => {
@@ -121,4 +132,50 @@ describe('RouterLink', () => {
     expect(router.push).toHaveBeenCalledTimes(1)
     expect(router.push).toHaveBeenCalledWith(locations.basic.normalized)
   })
+
+  describe('v-slot', () => {
+    function factory(
+      currentLocation: RouteLocationNormalized,
+      propsData: any,
+      resolvedLocation: RouteLocationNormalized
+    ) {
+      const router = {
+        history: createMemoryHistory(),
+        createHref(to: RouteLocationNormalized): string {
+          return this.history.base + to.fullPath
+        },
+        resolve: jest.fn(),
+        push: jest.fn().mockResolvedValue(resolvedLocation),
+        currentRoute: ref(markNonReactive(currentLocation)),
+        setActiveApp: jest.fn(),
+      }
+
+      router.resolve.mockReturnValueOnce(resolvedLocation)
+      const { app, el } = mount(router as any, {
+        template: `<RouterLink :to="to" v-slot="data">
+        route: {{ JSON.stringify(data.route) }}
+        href: "{{ data.href }}"
+        isActive: "{{ data.isActive }}"
+      </RouterLink>`,
+        components: { RouterLink } as any,
+        setup() {
+          const to = ref(propsData.to)
+
+          return { to }
+        },
+      })
+
+      return { app, router, el }
+    }
+
+    it('provides information on v-slot', () => {
+      const { el } = factory(
+        locations.basic.normalized,
+        { to: locations.basic.string },
+        locations.basic.normalized
+      )
+
+      expect(el.innerHTML).toMatchSnapshot()
+    })
+  })
 })
diff --git a/__tests__/__snapshots__/RouterLink.spec.ts.snap b/__tests__/__snapshots__/RouterLink.spec.ts.snap
new file mode 100644 (file)
index 0000000..61bd8ed
--- /dev/null
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`RouterLink v-slot provides information on v-slot 1`] = `"<a class=\\"router-link-active\\" href=\\"/home\\"> route: {\\"fullPath\\":\\"/home\\",\\"path\\":\\"/home\\",\\"params\\":{},\\"meta\\":{},\\"query\\":{},\\"hash\\":\\"\\",\\"matched\\":[]} href: \\"/home\\" isActive: \\"true\\" </a>"`;
index b7dbc114e4097f14301a75c5790937ded33769fd..a07cebce922378bc9ca37a8fb7442fde6ecaae71 100644 (file)
@@ -1,8 +1,31 @@
 import { defineComponent, h, PropType, inject } from '@vue/runtime-core'
-import { computed } from '@vue/reactivity'
-import { Router } from '../router'
+import { computed, reactive, isRef, Ref } from '@vue/reactivity'
 import { RouteLocation } from '../types'
 
+export function useLink(to: Ref<RouteLocation> | RouteLocation) {
+  const router = inject('router')
+
+  const route = computed(() => router.resolve(isRef(to) ? to.value : to))
+  const href = computed(() => router.createHref(route.value))
+  const isActive = computed<boolean>(
+    () => router.currentRoute.value.path.indexOf(route.value.path) === 0
+  )
+
+  // TODO: handle replace prop
+
+  function navigate(e: MouseEvent) {
+    // TODO: handle navigate with empty parameters for scoped slot and composition api
+    if (guardEvent(e)) router.push(route.value)
+  }
+
+  return {
+    route,
+    href,
+    isActive,
+    navigate,
+  }
+}
+
 const Link = defineComponent({
   name: 'RouterLink',
   props: {
@@ -12,30 +35,28 @@ const Link = defineComponent({
     },
   },
 
-  setup(props, context) {
-    const router = inject<Router>('router')!
+  setup(props, { slots, attrs }) {
+    const { route, isActive, href, navigate } = useLink(props.to)
 
-    const route = computed(() => router.resolve(props.to))
+    const elClass = computed(() => ({
+      'router-link-active': isActive.value,
+    }))
 
-    // TODO: active classes
+    // TODO: exact active classes
     // TODO: handle replace prop
 
-    const onClick = (e: MouseEvent) => {
-      // TODO: handle navigate with empty parameters for scoped slot and composition api
-      if (guardEvent(e)) {
-        router.push(route.value)
-      }
-    }
-
-    return () =>
-      h(
+    return () => {
+      return h(
         'a',
         {
-          onClick,
-          href: router.createHref(route.value),
+          class: elClass.value,
+          onClick: navigate,
+          href: href.value,
+          ...attrs,
         },
-        context.slots.default()
+        slots.default(reactive({ route, href, isActive }))
       )
+    }
   },
 })