Browse Source

Maintenance: Move tooltip directive to the shared namespace

Benjamin Scharf 6 months ago
parent
commit
64d29a43de

+ 3 - 41
app/frontend/apps/desktop/initializer/initializeGlobalDirectives.ts

@@ -1,49 +1,11 @@
 // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
-import { type App, type Directive } from 'vue'
+import { type App } from 'vue'
 
-import type { ImportGlobEagerDefault } from '#shared/types/utils.ts'
-
-import type vTooltip from '#desktop/plugins/directives/tooltip.ts'
-
-interface DirectiveModule {
-  name: string
-  directive: Record<string, unknown> | ((...args: unknown[]) => unknown)
-}
-
-const directiveModules: ImportGlobEagerDefault<DirectiveModule>[] =
-  Object.values(
-    import.meta.glob('../plugins/directives/*', {
-      eager: true,
-    }),
-  )
-
-export const directives: Record<string, Directive> = directiveModules.reduce(
-  (directiveRecord: Record<string, Directive>, module) => {
-    const { name, directive } = module.default
-    directiveRecord[name] = directive
-    return directiveRecord
-  },
-  {},
-)
+import { initializeTooltipDirective } from '#shared/initializer/initializeDirectives.ts'
 
 const initializeGlobalDirectives = (app: App) => {
-  directiveModules.forEach(
-    (module: ImportGlobEagerDefault<DirectiveModule>) => {
-      const { name, directive } = module.default
-      app.directive(name, directive)
-    },
-  )
+  initializeTooltipDirective(app)
 }
 
 export default initializeGlobalDirectives
-
-// :TODO improve DX by adding type definitions for global directives
-declare module '@vue/runtime-core' {
-  export interface GlobalDirectives {
-    vTooltip: typeof vTooltip
-  }
-  // export interface ComponentCustomProperties {
-  //   vTooltip: typeof vTooltip.directive
-  // }
-}

+ 0 - 4
app/frontend/apps/desktop/plugins/directives/__tests__/tooltip.spec.ts

@@ -138,10 +138,6 @@ describe('TooltipDirective', () => {
       await wrapper.events.hover(
         wrapper.getByText('This is a very long text that will be truncated'),
       )
-
-      // await waitFor(() => {
-      //   expect(wrapper.queryByText('Foo Test world')).toBeInTheDocument()
-      // })
     })
   })
 })

+ 3 - 41
app/frontend/apps/mobile/initializer/initializeGlobalDirectives.ts

@@ -1,49 +1,11 @@
 // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
-import { type App, type Directive } from 'vue'
+import { type App } from 'vue'
 
-import type { ImportGlobEagerDefault } from '#shared/types/utils.ts'
-
-import type vTooltip from '#mobile/plugins/directives/tooltip.ts'
-
-interface DirectiveModule {
-  name: string
-  directive: Record<string, unknown> | ((...args: unknown[]) => unknown)
-}
-
-const directiveModules: ImportGlobEagerDefault<DirectiveModule>[] =
-  Object.values(
-    import.meta.glob('../plugins/directives/*', {
-      eager: true,
-    }),
-  )
-
-export const directives: Record<string, Directive> = directiveModules.reduce(
-  (directiveRecord: Record<string, Directive>, module) => {
-    const { name, directive } = module.default
-    directiveRecord[name] = directive
-    return directiveRecord
-  },
-  {},
-)
+import { initializeTooltipDirective } from '#shared/initializer/initializeDirectives.ts'
 
 const initializeGlobalDirectives = (app: App) => {
-  directiveModules.forEach(
-    (module: ImportGlobEagerDefault<DirectiveModule>) => {
-      const { name, directive } = module.default
-      app.directive(name, directive)
-    },
-  )
+  initializeTooltipDirective(app)
 }
 
 export default initializeGlobalDirectives
-
-// :TODO improve DX by adding type definitions for global directives
-declare module '@vue/runtime-core' {
-  export interface GlobalDirectives {
-    vTooltip: typeof vTooltip
-  }
-  // export interface ComponentCustomProperties {
-  //   vTooltip: typeof vTooltip.directive
-  // }
-}

+ 23 - 0
app/frontend/apps/mobile/plugins/directives/__tests__/tooltip.spec.ts

@@ -0,0 +1,23 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import { waitFor } from '@testing-library/vue'
+import { describe } from 'vitest'
+
+import renderComponent from '#tests/support/components/renderComponent.ts'
+
+describe('Shared TooltipDirective', () => {
+  it('adds aria label without showing tooltip', async () => {
+    const wrapper = renderComponent({
+      template: `
+          <div v-tooltip="'Hello, Tooltip'">Foo Test World</div>
+         `,
+    })
+
+    expect(wrapper.getByLabelText('Hello, Tooltip')).toBeInTheDocument()
+
+    await wrapper.events.hover(wrapper.getByText('Foo Test World'))
+    await waitFor(() =>
+      expect(wrapper.queryByText('Hello, Tooltip')).not.toBeInTheDocument(),
+    )
+  })
+})

+ 0 - 11
app/frontend/apps/mobile/plugins/directives/tooltip.ts

@@ -1,11 +0,0 @@
-// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
-
-import { type ObjectDirective } from 'vue'
-
-export default {
-  name: 'tooltip',
-  directive: {},
-} as {
-  name: string
-  directive: ObjectDirective
-}

+ 9 - 0
app/frontend/shared/initializer/initializeDirectives.ts

@@ -0,0 +1,9 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+import { type App } from 'vue'
+
+import tooltip from '#shared/plugins/directives/tooltip/index.ts'
+
+export const initializeTooltipDirective = (app: App) => {
+  const { name, directive } = tooltip
+  app.directive(name, directive)
+}

+ 16 - 0
app/frontend/shared/plugins/directives/directives.d.ts

@@ -0,0 +1,16 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import type tooltip from '#shared/plugins/directives/tooltip'
+
+/**
+ * Should be support for typed global directives
+ * Webstorm IDE does not recognize the global directive
+ * Volar should pick it up
+ * @link https://github.com/vuejs/core/pull/3399
+ */
+// declare module '@vue/runtime-core' {
+declare module 'vue' {
+  export interface GlobalDirectives {
+    vTooltip: typeof tooltip.directive
+  }
+}

+ 4 - 1
app/frontend/apps/desktop/plugins/directives/tooltip.ts → app/frontend/shared/plugins/directives/tooltip/index.ts

@@ -2,6 +2,7 @@
 
 import { type ObjectDirective } from 'vue'
 
+import { useAppName } from '#shared/composables/useAppName.ts'
 import { useLocaleStore } from '#shared/stores/locale.ts'
 
 interface Modifiers {
@@ -266,7 +267,9 @@ export default {
       if (!message) return
 
       element.setAttribute('aria-label', message)
-      // :TODO be careful to not override existing aria-label
+
+      // Mobile does not have tooltips, hence we don't apply the rest of the logic
+      if (useAppName() === 'mobile') return
 
       element.setAttribute('data-tooltip', 'true')
 

+ 1 - 0
app/frontend/tests/support/components/app.ts

@@ -4,6 +4,7 @@ import type { AppName } from '#shared/types/app.ts'
 
 // internal Vitest variable, ideally should check expect.getState().testPath, but it's not populated in 0.34.6 (a bug)
 const { filepath } = (globalThis as any).__vitest_worker__ as any
+
 const isDesktop = filepath.includes('apps/desktop')
 
 export const getTestAppName = (): AppName => {

+ 2 - 2
app/frontend/tests/support/components/renderComponent.ts

@@ -29,6 +29,7 @@ import { initializeTwoFactorPlugins } from '#shared/entities/two-factor/composab
 import { buildFormKitPluginConfig } from '#shared/form/index.ts'
 import { i18n } from '#shared/i18n.ts'
 import applicationConfigPlugin from '#shared/plugins/applicationConfigPlugin.ts'
+import tooltip from '#shared/plugins/directives/tooltip/index.ts'
 import { initializeWalker } from '#shared/router/walker.ts'
 import type { AppName } from '#shared/types/app.ts'
 import type { FormFieldTypeImportModules } from '#shared/types/form.ts'
@@ -36,7 +37,6 @@ import type { ImportGlobEagerOutput } from '#shared/types/utils.ts'
 
 import { twoFactorConfigurationPluginLookup } from '#desktop/entities/two-factor-configuration/plugins/index.ts'
 import desktopIconsAliases from '#desktop/initializer/desktopIconsAliasesMap.ts'
-import { directives } from '#desktop/initializer/initializeGlobalDirectives.ts'
 import mobileIconsAliases from '#mobile/initializer/mobileIconsAliasesMap.ts'
 
 import { setTestState, waitForNextTick } from '../utils.ts'
@@ -156,8 +156,8 @@ const defaultWrapperOptions: ExtendedMountingOptions<unknown> = {
       CommonLabel,
       CommonBadge,
     },
-    directives,
     stubs: {},
+    directives: { [tooltip.name]: tooltip.directive },
     plugins,
   },
 }