diff --git a/src/components/table/helpers/mixin-selectable.js b/src/components/table/helpers/mixin-selectable.js index 82cc580c618..b30806497aa 100644 --- a/src/components/table/helpers/mixin-selectable.js +++ b/src/components/table/helpers/mixin-selectable.js @@ -12,12 +12,15 @@ import { isArray, isNumber } from '../../../utils/inspect' import { looseEqual } from '../../../utils/loose-equal' import { mathMax, mathMin } from '../../../utils/math' import { makeProp } from '../../../utils/props' +import { toString } from '../../../utils/string' import { sanitizeRow } from './sanitize-row' // --- Constants --- const SELECT_MODES = ['range', 'multi', 'single'] +const ROLE_GRID = 'grid' + // --- Props --- export const props = { @@ -70,17 +73,19 @@ export const selectableMixin = Vue.extend({ } }, selectableTableAttrs() { - const role = this.bvAttrs.role || 'grid' + if (!this.isSelectable) { + return {} + } - return this.isSelectable - ? { - role, - // TODO: - // Should this attribute not be included when `no-select-on-click` is set - // since this attribute implies keyboard navigation? - 'aria-multiselectable': role === 'grid' ? String(this.selectableIsMultiSelect) : null - } - : {} + const role = this.bvAttrs.role || ROLE_GRID + + return { + role, + // TODO: + // Should this attribute not be included when `no-select-on-click` is set + // since this attribute implies keyboard navigation? + 'aria-multiselectable': role === ROLE_GRID ? toString(this.selectableIsMultiSelect) : null + } } }, watch: { diff --git a/src/components/table/helpers/mixin-table-renderer.js b/src/components/table/helpers/mixin-table-renderer.js index 0941861b216..545da7af61c 100644 --- a/src/components/table/helpers/mixin-table-renderer.js +++ b/src/components/table/helpers/mixin-table-renderer.js @@ -115,7 +115,7 @@ export const tableRendererMixin = Vue.extend({ const ariaAttrs = this.isTableSimple ? {} : { - 'aria-busy': this.computedBusy ? 'true' : 'false', + 'aria-busy': toString(this.computedBusy), 'aria-colcount': toString(fields.length), // Preserve user supplied `aria-describedby`, if provided 'aria-describedby': @@ -135,7 +135,7 @@ export const tableRendererMixin = Vue.extend({ ...this.bvAttrs, // Now we can override any `$attrs` here id: this.safeId(), - role: 'table', + role: this.bvAttrs.role || 'table', ...ariaAttrs, ...selectableTableAttrs } diff --git a/src/components/table/table-selectable.spec.js b/src/components/table/table-selectable.spec.js index ac07994ddaa..d5eb54c5703 100644 --- a/src/components/table/table-selectable.spec.js +++ b/src/components/table/table-selectable.spec.js @@ -52,6 +52,47 @@ describe('table > row select', () => { wrapper.destroy() }) + it('should apply user role if provided, grid role if multiselectable or table role otherwise', async () => { + let wrapper = mount(BTable, { + propsData: { + fields: testFields, + items: testItems + } + }) + + expect(wrapper).toBeDefined() + await waitNT(wrapper.vm) + + expect(wrapper.attributes('role')).toBe('table') + wrapper.destroy() + + wrapper = mount(BTable, { + propsData: { + fields: testFields, + items: testItems, + role: 'foobar' + } + }) + + await waitNT(wrapper.vm) + + expect(wrapper.attributes('role')).toBe('foobar') + wrapper.destroy() + + wrapper = mount(BTable, { + propsData: { + fields: testFields, + items: testItems, + selectable: true + } + }) + + await waitNT(wrapper.vm) + + expect(wrapper.attributes('role')).toBe('grid') + wrapper.destroy() + }) + it('should have tabindex but not aria-selected when not selectable and has row-clicked listener', async () => { const wrapper = mount(BTable, { propsData: {