1# Table
2
3All tables in the application are using the [BoostrapVue table component](https://bootstrap-vue.org/docs/components/table).
4
5To use the component, include the `<b-table>` tag in the template. The component is registered globally so does not need to be imported in each SFC.
6
7## Basic table
8There are a few required properties to maintain consistency across the application. The full list of options can be viewed on the [Bootstrap-vue table component's documentation page](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-props).
9
10
11### Required properties
12
13- `items` - renders table items
14- `fields` - renders table header
15- `hover` - enables table row hover state
16- `responsive` or `stacked` - makes the table responsive (enables horizontal scrolling or stacked view) at the defined breakpoint
17- `show-empty` *(required if table data is generated dynamically)* - shows an empty message if there are no items in the table
18- `empty-text` *(required if table data is generated dynamically)* - the translated empty message
19
20![Basic table example](./table.png)
21![Basic empty table example](./table-empty.png)
22
23```vue
24<template>
25  <b-table
26    hover
27    show-empty
28    responsive="md"
29    :items="items"
30    :fields="fields"
31    :empty-text="$t('global.table.emptyMessage')"
32  />
33</template>
34
35<script>
36  export default {
37    data() {
38      items: [
39        {
40          name: 'Babe',
41          age: '3 years',
42          color: 'white, orange, grey'
43        },
44        {
45          name: 'Grey Boy',
46          age: '4 months',
47          color: 'grey'
48        },
49      ],
50      fields: [
51        {
52          key: 'name',
53          label: this.$t('table.name') //translated label
54        },
55        {
56          key: 'age',
57          label: this.$t('table.age') //translated label
58        },
59        {
60          key: 'color',
61          label: this.$t('table.color') // translated label
62        }
63      ]
64    }
65  }
66</script>
67```
68
69## Sort
70
71To enable table sort, include `sortable: true` in the fields array for sortable columns and add the following props to the `<b-table>` component:
72
73- `sort-by`
74- `no-sort-reset`
75- `sort-icon-left`
76
77![Table sort example](./table-sort.png)
78
79
80```vue
81<template>
82  <b-table
83    hover
84    no-sort-reset
85    sort-icon-left
86    sort-by="rank"
87    responsive="md"
88    :items="items"
89    :fields="fields"
90  />
91</template>
92<script>
93export default {
94  data() {
95    return {
96      items: [...],
97      fields: [
98        {
99          key: 'name',
100          label: 'Name', //should be translated
101          sortable: true
102        },
103        {
104          key: 'rank',
105          label: 'Rank', //should be translated
106          sortable: true
107        },
108        {
109          key: 'description',
110          label: 'Description', //should be translated
111          sortable: false
112        }
113      ]
114    }
115  }
116}
117</script>
118```
119
120## Expandable rows
121
122To add an expandable row in the table, add a column for the expand button in the fields array. Include the tdClass `table-row-expand` to ensure icon rotation is handled. Use the built in [cell slot](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-slots) to target the expand button column and add a button with the chevron icon.
123
124Include the [TableRowExpandMixin](https://github.com/openbmc/webui-vue/blob/master/src/components/Mixins/TableRowExpandMixin.js). The mixin contains the dynamic `aria-label` and `title` attribute values that need to be included with the expand button. The `toggleRowDetails` method should be the button's click event callback. Be sure to pass the `row` object to the function.
125
126Use the [row-details slot](https://bootstrap-vue.org/docs/components/table#comp-ref-b-table-slots) to format the expanded row content. The slot has access to the row `item` property.
127
128### Summary
129
1301. Add a column for the expansion row button with the tdClass, `table-row-expand`
1312. Include the `TableRowExpandMixin` to handle the dynamic aria label, title, and row expansion toggling
1323. Use the `#cell` slot to target the expandable row column and add the button with accessible markup and click handler
1334. Use the `#row-details` slot to format expanded row content
134
135![Table row expand example](./table-expand-row.png)
136
137```vue
138<template>
139  <b-table
140    hover
141    responsive="md"
142    :items="items"
143    :fields="fields"
144  >
145    <template #cell(expandRow)="row">
146      <b-button
147        variant="link"
148        :aria-label="expandRowLabel"
149        :title="expandRowLabel"
150        @click="toggleRowDetails(row)"
151      >
152        <icon-chevron />
153      </b-button>
154    </template>
155    <template #row-details="row">
156      <h3>Expanded row details</h3>
157      {{ row.item }}
158    </template>
159  </b-table>
160</template>
161<script>
162import IconChevron from '@carbon/icons-vue/es/chevron--down/20';
163import TableRowExpandMixin, { expandRowLabel } from '@/components/Mixins/TableRowExpandMixin';
164
165export default {
166  components: { IconChevron },
167  mixins: [ TableRowExpandMixin ],
168  data() {
169    return {
170      items: [...],
171      fields: [
172        {
173          key: 'expandRow',
174          label: '',
175          tdClass: 'table-row-expand',
176        },
177        ...
178      ],
179      expandRowLabel
180    }
181  }
182}
183</script>
184```
185
186## Search
187
188The table is leveraging [BootstrapVue table filtering](https://bootstrap-vue.org/docs/components/table#filtering) for search. Add the [@filtered](https://bootstrap-vue.org/docs/components/table#filter-events) event listener onto the `<b-table>` component. The event callback should track the total filtered items count.
189
190Import the `<search>` and `<table-cell-count>` components and include them in the template above the `<b-table>` component.
191
192Include the [SearchFilterMixin](https://github.com/openbmc/webui-vue/blob/master/src/components/Mixins/SearchFilterMixin.js). Add the `@change-search` and `@clear-search` event listeners on the `<search>` component and use the corresponding `onChangeSearchInput` and `onClearSearchInput` methods as the event callbacks. The table should also include the dynamic `:filter` prop with `searchFilter` set as the value.
193
194The `<table-cell-count>` component requires two properties, total table item count and total filtered items count.
195
196Add the `:empty-filtered-text` prop to the table to show the translated message if there are no search matches.
197
198![Table search example](./table-search.png)
199
200![Table search active example](./table-search-active.png)
201
202![Table search empty example](./table-search-empty.png)
203
204```vue
205<template>
206  <b-container>
207  <b-row>
208    <b-col>
209      <search
210        @changeSearch="onChangeSearchInput"
211        @clearSearch="onClearSearchInput"
212      />
213    </b-col>
214    <b-col>
215      <table-cell-count
216        :filtered-items-count="filteredItemsCount"
217        :total-number-of-cells="items.length"
218      />
219    </b-col>
220  </b-row>
221  <b-table
222    hover
223    responsive="md"
224    :items="items"
225    :fields="fields"
226    :filter="searchFilter"
227    :empty-filtered-text="$t('global.table.emptySearchMessage')"
228    @filtered="onFiltered"
229  />
230  </b-container>
231</template>
232<script>
233import Search from '@/components/Global/Search';
234import TableCellCount from '@/components/Global/TableCellCount';
235import SearchFilterMixin, { searchFilter } from '@/components/Mixins/SearchFilterMixin';
236
237export default {
238  components: { Search, TableCellCount },
239  mixins: [ SearchFilterMixin ],
240  data() {
241    return {
242      items: [...],
243      fields: [...],
244      searchFilter,
245      filteredItems: [],
246    }
247  },
248  computed: {
249    filteredItemsCount() {
250      return this.filteredItems.length;
251    },
252  },
253  methods: {
254    onFiltered(items) {
255      this.filteredItems = items;
256    },
257  },
258}
259</script>
260```
261
262## Row actions
263
264To add table row actions, add a column for the action buttons in the table. Then in the array of table items, add a corresponding array of actions for each item. The array should have each desired row action with a `value` and `title` property.
265
266Import the `<table-row-action>` component. Provide the `value` and `title` props to the component and use the named `#icons` slot to include an icon. The component will emit a `@click-table-action` with the event value.
267
268![Table row actions example](./table-row-actions.png)
269
270```vue
271<template>
272  <b-table
273    hover
274    responsive="md"
275    :items="itemsWithActions"
276    :fields="fields"
277  >
278    <template #cell(actions)="row">
279      <table-row-action
280        v-for="(action, index) in row.item.actions"
281        :key="index"
282        :value="action.value"
283        :title="action.title"
284        @click-table-action="onTableRowAction($event, row.item)"
285      />
286        <template #icon>
287          <icon-edit v-if="action.value === 'edit'"/>
288          <icon-delete v-if="action.value === 'delete'"/>
289        </template>
290      </table-row-action>
291    </template>
292  </b-table>
293</template>
294<script>
295import IconDelete from '@carbon/icons-vue/es/trash-can/20';
296import IconEdit from '@carbon/icons-vue/es/edit/20';
297import TableRowAction from '@/components/Global/TableRowAction';
298
299export default {
300  components: { IconDelete, IconEdit, TableRowAction },
301  data() {
302    return {
303      items: [...],
304      fields: [
305        ...,
306        {
307          key: 'actions',
308          label: '',
309          tdClass: 'text-right text-nowrap',
310        }
311      ],
312    }
313  },
314  computed: {
315    itemsWithActions() {
316      return this.items.map((item) => {
317        return {
318          ...item,
319          actions: [
320            {
321              value: 'edit',
322              title: this.$t('global.action.edit'),
323            },
324            {
325              value: 'delete',
326              title: this.$t('global.action.delete'),
327            },
328          ],
329        };
330      });
331    }
332  },
333  methods: {
334    onTableRowAction(event, row) {
335      // row action callback
336    }
337  }
338}
339</script>
340```
341
342<!-- ## Pagination -->
343<!-- ## Batch actions -->
344<!-- ## Filter -->
345