javascriptvue.jsautomated-testsvuetify.jswebdriver-io

How to add a data-xx attribute the rendered <table> in a Vuetify 2 v-simple-table


I would like to add an HTML data-xxxx attribute to the HTML <table> represented by a v-simple-table, but if I add a data attribute to the <v-simple-table> it ends up in a wrapping div two layers above the actual <table>.

I use data attributes to target components in my test suite. In my POM I want to do:

get tableResultsAvailable () {
    return $('table[data-qaid="results_available-panel"]');
}

In a page I might do this:

        <v-skeleton-loader v-if="loading" type="table-tbody"
                           :types="{ 'table-tbody': 'table-row-divider@4', 'table-row': 'table-cell@3', }"/>
        <v-simple-table v-else data-qaid="results_available-panel">
            <thead>

Then in a test spec I might await the table while it loads:

    await expect(MyAccountPage.tableResultsAvailable).toBeDisplayed()

However in the HTML the data is attached to a div not a table

<div data-v-b386ddc8="" class="v-data-table v-data-table--fixed-height theme--light" data-qaid="results_available-panel">
    <div class="v-data-table__wrapper" style="height: 395px;">
        <table>
            <thead data-v-b386ddc8="">

Therefore in my POM I have to unnecessarily broaden the selector to '[data-qaid="results_available-panel"]' or rely on 'div[data-qaid="results_available-panel"]' which is specific to the Vuetify structure which might change, neither of which are ideal.

I'm guessing I could use the default slot to override the whole table definition, but that seems overkill. I've noticed that css classes are also applied to the wrapper in this way.


Solution

  • Looking at the code, there is no way to programmatically access the <table> element. You cannot override the table through the default slot, however, there is an undocumented wrapper slot:

    <v-simple-table>
      <template #wrapper>
        <div class="v-data-table__wrapper">
          <table data-qaid="results_available-panel">
            ... (previous default slot content goes here)
          <table>
        </div>
      </template>
    </v-simple-table>
    

    But you already said you are not comfortable with overriding the whole wrapper.

    The remaining option is to set the attribute in pure JS, for example through a ref:

    mounted(){
      this.$refs.wrapper.$el.querySelector('table').dataset['quaid'] = 'results_available-panel'
    }
    

    But this is quite fragile since you are doing direct DOM manipulation on a node managed by Vue.

    In both options, you rely on the internal structure of VSimpleTable, so you could just use the adjusted selector, which is probably easiest.

    new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      mounted(){
        this.$refs.wrapper.$el.querySelector('table').dataset['prop'] = 'asdf'
      }
    })
    *[data-prop="asdf"]{
      background: red;
    }
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
    
      <div id="app">
        <v-app>
          <v-main>
            <v-container>
            
            Table with wrapper slot:
            <v-simple-table>
              <template #wrapper>
                <div class="v-data-table__wrapper">
                  <table data-prop="asdf">
                    <tr><td>Table content</td><tr>
                  </table>
                </div>
              </template>
            </v-simple-table>
            
            Table with ref:
            <v-simple-table ref="wrapper">
              <template #default>
                <tbody>
                  <tr><td>Table content</td><tr>
                </tbody>
              </template>
            </v-simple-table>
            
            </v-container>
          </v-main>
        </v-app>
      </div>
    
      <script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
      <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>