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