Browse Source

Maintenance: Mobile - refactor CommonLink to use useLink

Vladimir Sheremet 2 years ago
parent
commit
60f53f722f

+ 28 - 26
app/frontend/common/components/common/CommonLink.vue

@@ -2,9 +2,9 @@
 
 <script setup lang="ts">
 import type { Link } from '@common/types/router'
-import isRouteLink from '@common/router/utils/isRouteLink'
-import { computed } from 'vue'
+import { computed, toRef } from 'vue'
 import stopEvent from '@common/utils/events'
+import { useLink } from 'vue-router'
 
 export interface Props {
   link: Link
@@ -26,19 +26,14 @@ const props = withDefaults(defineProps<Props>(), {
   append: false,
   openInNewTab: false,
   disabled: false,
+  activeClass: 'router-link-active',
+  exactActiveClass: 'router-link-exact-active',
 })
 
 const emit = defineEmits<{
   (e: 'click', event: MouseEvent): void
 }>()
 
-const isInternalLink = computed(() => {
-  if (props.isExternal) return false
-  if (props.isRoute) return true
-
-  return isRouteLink(props.link)
-})
-
 const target = computed(() => {
   if (props.target) return props.target
   if (props.openInNewTab) return '_blank'
@@ -54,6 +49,17 @@ const linkClass = computed(() => {
   return ''
 })
 
+const { href, route, navigate, isActive, isExactActive } = useLink({
+  to: toRef(props, 'link'),
+  replace: toRef(props, 'replace'),
+})
+
+const isInternalLink = computed(() => {
+  if (props.isExternal) return false
+  if (props.isRoute) return true
+  return route.value.matched.length > 0 && route.value.name !== 'Error'
+})
+
 const onClick = (event: MouseEvent) => {
   if (props.disabled) {
     stopEvent(event, { immediatePropagation: true })
@@ -61,6 +67,10 @@ const onClick = (event: MouseEvent) => {
   }
   emit('click', event)
 
+  if (isInternalLink.value) {
+    navigate(event)
+  }
+
   // Stop the scroll-to-top behavior or navigation on regular links when href is just '#'.
   if (!isInternalLink.value && props.link === '#') {
     stopEvent(event, { propagation: false })
@@ -69,26 +79,18 @@ const onClick = (event: MouseEvent) => {
 </script>
 
 <template>
-  <router-link
-    v-if="isInternalLink"
-    v-bind:to="link"
-    v-bind:replace="replace"
-    v-bind:class="linkClass"
-    v-bind:active-class="activeClass"
-    v-bind:exact-active-class="exactActiveClass"
-    v-bind:target="target"
-    data-test-id="common-link"
-    v-on:click="onClick"
-  >
-    <slot></slot>
-  </router-link>
   <a
-    v-else
-    v-bind:href="(link as string)"
+    data-test-id="common-link"
+    v-bind:href="isInternalLink ? href : (link as string)"
     v-bind:target="target"
-    v-bind:class="linkClass"
     v-bind:rel="rel"
-    data-test-id="common-link"
+    v-bind:class="[
+      linkClass,
+      {
+        [activeClass]: isActive,
+        [exactActiveClass]: isExactActive,
+      },
+    ]"
     v-on:click="onClick"
   >
     <slot></slot>

+ 0 - 12
app/frontend/common/router/utils/isRouteLink.ts

@@ -1,12 +0,0 @@
-// Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
-
-import { Link } from '@common/types/router'
-import { useRouter } from 'vue-router'
-
-export default function isRouteLink(link: Link): boolean {
-  if (typeof link === 'object') return true
-
-  const router = useRouter()
-
-  return router.hasRoute(link)
-}

+ 0 - 43
app/frontend/tests/common/router/utils/isRouteLink.spec.ts

@@ -1,43 +0,0 @@
-// Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
-
-/* eslint-disable import/first */
-// Add a mocked router resolve function for the success case.
-vi.mock('vue-router', () => ({
-  useRouter: vi.fn(() => ({
-    hasRoute: () => true,
-    resolve: () => {
-      return {
-        name: 'RouteName',
-        matched: ['RouteName'],
-      }
-    },
-  })),
-}))
-
-import isRouteLink from '@common/router/utils/isRouteLink'
-import { useRouter } from 'vue-router'
-
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const router = useRouter as any
-
-describe('isRouteLink', () => {
-  it('is correct route link', () => {
-    expect(isRouteLink('ticket/12')).toEqual(true)
-  })
-
-  it('is not a correct route link, because there is no such route', () => {
-    router.mockImplementationOnce(() => ({
-      hasRoute: () => false,
-    }))
-
-    expect(isRouteLink('not-existing')).toEqual(false)
-  })
-
-  it('link object is always a route link', () => {
-    expect(
-      isRouteLink({
-        name: 'Login',
-      }),
-    ).toEqual(true)
-  })
-})

+ 7 - 0
app/frontend/tests/support/components/renderComponent.ts

@@ -72,6 +72,13 @@ const initializeRouter = (routes?: RouteRecordRaw[]) => {
         template: 'This is a example page.',
       },
     },
+    {
+      name: 'Error',
+      path: '/:pathMatch(.*)*',
+      component: {
+        template: 'Error page',
+      },
+    },
   ]
 
   // Use only the default routes, if nothing was given.