diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index 24a92c5da1d..485429971cb 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -85,6 +85,20 @@ export default { const hasDetailsSlot = this.hasNormalizedSlot(detailsSlotName) const formatted = this.getFormattedValue(item, field) const key = field.key + // We only uses the helper components for sticky columns to + // improve performance of BTable/BTableLite by reducing the + // total number of vue instances created during render + const cellTag = field.stickyColumn + ? field.isRowHeader + ? BTh + : BTd + : field.isRowHeader + ? 'th' + : 'td' + const cellVariant = + item._cellVariants && item._cellVariants[key] + ? item._cellVariants[key] + : field.variant || null const data = { // For the Vue key, we concatenate the column index and // field key (as field keys could be duplicated) @@ -92,14 +106,7 @@ export default { // So we could change this to: `row-${rowIndex}-cell-${key}` key: `row-${rowIndex}-cell-${colIndex}-${key}`, class: [field.class ? field.class : '', this.getTdValues(item, key, field.tdClass, '')], - props: { - stackedHeading: this.isStacked ? field.label : null, - stickyColumn: field.stickyColumn, - variant: - item._cellVariants && item._cellVariants[key] - ? item._cellVariants[key] - : field.variant || null - }, + props: {}, attrs: { 'aria-colindex': String(colIndex + 1), ...(field.isRowHeader @@ -107,6 +114,25 @@ export default { : this.getTdValues(item, key, field.tdAttr, {})) } } + if (field.stickyColumn) { + // We are using the helper BTd or BTh + data.props = { + stackedHeading: this.isStacked ? field.label : null, + stickyColumn: field.stickyColumn, + variant: cellVariant + } + } else { + // Using native TD or TH element, so we need to + // add in the attributes and variant class + data.attrs['data-label'] = + this.isStacked && !isUndefinedOrNull(field.label) ? toString(field.label) : null + data.attrs.role = field.isRowHeader ? 'rowheader' : 'cell' + data.attrs.scope = field.isRowHeader ? 'row' : null + // Add in the variant class + if (cellVariant) { + data.class.push(`${this.dark ? 'bg' : 'table'}-${cellVariant}`) + } + } const slotScope = { item: item, index: rowIndex, @@ -135,7 +161,7 @@ export default { $childNodes = [h('div', {}, [$childNodes])] } // Render either a td or th cell - return h(field.isRowHeader ? BTh : BTd, data, [$childNodes]) + return h(cellTag, data, [$childNodes]) }, renderTbodyRow(item, rowIndex) { // Renders an item's row (or rows if details supported) diff --git a/src/components/table/table-sticky-column.spec.js b/src/components/table/table-sticky-column.spec.js new file mode 100644 index 00000000000..e0e04243f92 --- /dev/null +++ b/src/components/table/table-sticky-column.spec.js @@ -0,0 +1,55 @@ +import { mount } from '@vue/test-utils' +import { BTable } from './table' +import { BTd } from './td' +import { BTh } from './th' + +const items = [{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }] +const fields = [ + { key: 'a', stickyColumn: true, isRowHeader: true }, + { key: 'b', stickyColumn: true }, + 'c' +] + +describe('table > sticky columns', () => { + it('has expected classes when sticky column is enabled', async () => { + const wrapper = mount(BTable, { + propsData: { + responsive: true, + items: items, + fields: fields + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(BTable)).toBe(true) + expect(wrapper.is('div')).toBe(true) + expect(wrapper.classes()).toContain('table-responsive') + const table = wrapper.find('table') + expect(table.classes()).toContain('table') + expect(table.classes()).toContain('b-table') + + const trs = wrapper.findAll('tbody > tr') + expect(trs.length).toBe(2) + + const cells = trs.at(0).findAll('th, td') + expect(cells.length).toBe(3) + + // First column should be BTh with sticky classes + expect(cells.at(0).is(BTh)).toBe(true) + expect(cells.at(0).is('th')).toBe(true) + expect(cells.at(0).classes()).toContain('b-table-sticky-column') + + // Second column should be BTd with sticky classes + expect(cells.at(1).is(BTd)).toBe(true) + expect(cells.at(1).is('td')).toBe(true) + expect(cells.at(1).classes()).toContain('b-table-sticky-column') + + // Third column should be td + expect(cells.at(2).is(BTd)).toBe(false) + expect(cells.at(2).is(BTh)).toBe(false) + expect(cells.at(2).is('td')).toBe(true) + expect(cells.at(2).classes()).not.toContain('b-table-sticky-column') + + wrapper.destroy() + }) +})