Vaclav Pelc
Vaclav Pelc

Reputation: 675

Vuetify tooltip shows in top left corner when a link from a v-button is followed

I have encountered a strange behaviour of the Tooltip component in Vuetify. I am using this component inside Vuetify's DataTable component, where I use a slot for one of the data columns into which I add Vuetify's Button component and then I use the Tooltip component inside that button.

Problem definition:

The tooltip behavior is following:

Additional information:

I have found out that the behavior is probably caused by the fact that in my app, I have set this component to be kept alive via

<keep-alive :include="['PrintHistory']">
  <router-view />
</keep-alive>

When I remove the PrintHistory string, it behaves as expected.

I have also found out that when I set the open-delay parameter to a higher number, it shows the tooltip in the top left corner with higher delay.

It does not matter what page I'm redirecting to, the problem still prevales.

The code of the entire component:

<template>
  <div>
    <v-layout style="width:100%;" justify-space-between class="mb-0">
        <h2>{{ $t('print_history.title') }}</h2>
    </v-layout>
    <v-card class="elevation-1 my-1 pt-1" v-resize="onResize">

      <v-layout style="text-align: right;">
        <v-spacer></v-spacer>
        <v-text-field
          v-model="search"
          append-icon="mdi-magnify"
          :label="$t('print_history.search')"
          class="ma-2"
        ></v-text-field>
      </v-layout>

      <v-data-table :headers="headers"
                    :items="rows"
                    :loading="loading"
                    :loading-text="$t('v_big_table.loading')"
                    :sort-by="'action_at'"
                    :sort-desc="true"
                    :search="search"
                    :page.sync="page"
                    @page-count="page_count = $event"
                    hide-default-footer
                    :items-per-page="computed_items_per_page">
        <template v-for="header in headers" v-slot:[`header.`+header.value]="{ header }">
          <span class="table_header" v-bind:key="`header_customization_`+header.value">{{ $t(header.text) }}</span>
        </template>
        <template v-slot:item.show_file="{ item }">
          <!-- The redirecting is not yet finished -->
          <v-btn :to="`/pdfjs-viewer`" icon small class="ma-0" color="primary">
            <v-tooltip top open-delay="600" :color="tooltip_bg_color">
              <template v-slot:activator="{ on, attrs }">
                    <v-icon
                      v-bind="attrs"
                      v-on="on">mdi-file-search-outline</v-icon>
              </template>
              <span class="tooltip">{{ $t('print_history.show_file') }} {{ item['path_to_file'].split("/")[item['path_to_file'].split("/").length-1] }}</span>
            </v-tooltip>
          </v-btn>
        </template>
      </v-data-table>
      <v-pagination
        v-model="page"
        :length="page_count"
        :total-visible="9"
      ></v-pagination>
    </v-card>
  </div>
</template>

<script>

/**
 * @typedef {Object} Sorter - stores information about column sorting
 * @property {string} field - unique name of the column to sort
 * @property {string} dir - direction of sorting: ascending ('asc'), descending ('desc')
 */

/**
 * @typedef {Object} Filter - stores information about column sorting
 * @property {string} field - unique name of the column to filter
 * @property {string} operator - operator defining the filter
 * @property {string} value - direction of sorting: ascending ('asc'), descending ('desc')
 * @property {string} color - color of the v-chip when the filter is shown
 */

import { utcTimeStamp, utcToLocal } from '../../../utils'

/**
 * Shows a history of printing arranged in a table with sorting and filtering capabilities.
 */
export default {
  name: "PrintHistory",
  data () {
    return {
      tooltip_bg_color: 'rgba(0,0,0,0.7)',
      err_msg: '',
      search: '',
      row_count: 0,
      total_row_count: 0,
      rows: [],
      rows_filtered: [],
      loading: true,
      page: 1,
      page_count: null,
      default_items_per_page: 13,
      window_size: {
        x: 0,
        y: 0
      },
      initial_sorters: [{field: 'action_at', dir: 'desc'}],
      headers: [
        {
          text: 'print_history.date_and_time',
          value: 'action_at',
          align: 'end'
        },
        {
          text: 'print_history.report_type',
          value: 'translated_report_type'
        },
        {
          text: 'print_history.user_id',
          value: 'user_id'
        },
        {
          text: 'print_history.user_name',
          value: 'user_name'
        },
        {
          text: 'print_history.state',
          value: 'translated_state'
        },
        {
          text: 'print_history.show_file',
          value: 'show_file',
          sortable: false,
        },
      ],
      last_update: null,
    }
  },
  computed: {
    computed_items_per_page() {
      return Math.max(Math.floor((this.window_size.y - 300)/48), 1)
    }
  },
  methods: {
    utcToLocal,
    /**
     * Loads data from database. Assigns loading error in case the data cannot be loaded.
     */
    loadData() {
      this.loading = true
      this.last_update = utcTimeStamp()
      let start_time = Date.now()
      this.$store.dispatch('getPrintHistory')
      .then((response) => {
        this.rows = this.transformData(response.data)
        this.row_count = this.rows.length
        this.total_row_count = response.data.total_row_count
        console.log("Total time:" + (Date.now() - start_time))
      })
      .catch(() => {
        this.err_msg = 'errors.error_loading_data'
        this.data = []
      })
      .finally(() => {
        this.loading = false
      })
    },
    /**
     * This will show file in a javascript PDF browser once implemented.
     * @param absolute_path absolute path to the file to be shown
     */
    showFile(absolute_path) {
      console.log(absolute_path)
    },
    /**
     * Processes data before saving them to rows.
     * @param {object[]} data_to_transform array of objects that should be processed
     * @return {object[]} array of objects that are formatted for the data table
     */
    transformData(data_to_transform) {
      let data_to_return = data_to_transform
      data_to_return.forEach((entry) => {
        entry['user_name'] = entry.user.name
        entry['path_to_file'] = entry.misc.path_to_file
        entry['action_at'] = this.$d(utcToLocal(entry.action_at), 'datetime')
        entry['translated_report_type'] = this.$t(`print_history.report_types.` + entry.report_type)
        entry['translated_state'] = this.$t(`print_history.states.` + entry.state)
      })
      return data_to_return
    },
    onResize() {
      this.window_size = {x: window.innerWidth, y: window.innerHeight}
    }
  },
  beforeMount() {
    this.loadData()
  },
  activated() {
    // check if the server has new data, refresh component data if true
    this.$store.dispatch('lastDataUpdate')
    .then((response) => {
      if (response.data > this.last_update) {
        this.loadData()
      }
    })
    // on error refresh anyway
    .catch(() => {
      this.loadData()
    })
  },
}
</script>

<style scoped>
.table_header {
  font-weight: bold;
  color: black;
}
</style>

An example of the response.data retrieved from the database via the loadData method:

[{
   action_at: "2021-01-20T13:03:39.528843",
   id: "1",
   inventory_id: "1",
   reporty_type: "my_report_type",
   state: "archived",
   misc: {
     path_to_file: '/some/path/to/file.pdf'
   },
   user: {
     name: "John Doe",
     id: "123456",
     roles: {role_1: true}
   },
   user_id: "123456"
 },
 {
   action_at: "2021-01-20T13:05:39.528843",
   id: "2",
   inventory_id: "1",
   reporty_type: "my_other_report_type",
   state: "moved_to_print",
   misc: {
     path_to_file: '/some/path/to/file2.pdf'
   },
   user: {
     name: "Jane Doe",
     id: "123457",
     roles: {role_1: true}
   },
   user_id: "123457"
 }]

Question:

Is this tooltip behavior a bug or do I have to set some additional settings for it? Is there some workaround so it behaves correctly even when the component is kept alive?

In case of some additional information, ask away.

Upvotes: 2

Views: 3285

Answers (1)

Karin C
Karin C

Reputation: 505

Seems like you have the same issue as this: https://github.com/vuetifyjs/vuetify/issues/2480 but with different versions of Vuetify.

There are many issues and requests for an attribute of tooltip for buttons, but for now the solution can be like in this fix: https://github.com/vuetifyjs/vuetify/pull/2780 :

  1. Define show in the data (I think it should be set to false if you use v-model for the tooltip)
  2. Add @click event to the button like this: @click="show = false"
  3. For the tooltip you have 2 options: Add either v-if="show" or v-model="show"

Upvotes: 1

Related Questions