diff --git a/src/components/table/README.md b/src/components/table/README.md index 663e449006d..b784193c098 100644 --- a/src/components/table/README.md +++ b/src/components/table/README.md @@ -233,25 +233,26 @@ formatting, etc). Only columns (keys) that appear in the fields array will be sh The following field properties are recognized: -| Property | Type | Description | -| ------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `key` | String | The key for selecting data from the record in the items array. Required when setting the `fields` via an array of objects. | -| `label` | String | Appears in the columns table header (and footer if `foot-clone` is set). Defaults to the field's key (in humanized format) if not provided. It's possible to use empty labels by assigning an empty string `""` but be sure you also set `headerTitle` to provide non-sighted users a hint about the column contents. | -| `headerTitle` | String | Text to place on the fields header `` attribute `title`. Defaults to no `title` attribute. | -| `headerAbbr` | String | Text to place on the fields header `` attribute `abbr`. Set this to the unabbreviated version of the label (or title) if label (or title) is an abbreviation. Defaults to no `abbr` attribute. | -| `class` | String or Array | Class name (or array of class names) to add to `` **and** `` in the column. | -| `formatter` | String or Function | A formatter callback function or name of a method in your component, can be used instead of (or in conjunction with) scoped field slots. Refer to [Custom Data Rendering](#custom-data-rendering) for more details. | -| `sortable` | Boolean | Enable sorting on this column. Refer to the [Sorting](#sorting) Section for more details. | -| `sortDirection` | String | Set the initial sort direction on this column when it becomes sorted. Refer to the [Change initial sort direction](#Change-initial-sort-direction) Section for more details. | -| `sortByFormatted` | Boolean | NEW in 2.0.0-rc.28 Sort the column by the result of the field's `formatter` callback function. Default is `false`. Has no effect if the field does not have a `formatter`. Refer to the [Sorting](#sorting) Section for more details. | -| `filterByFormatted` | Boolean | NEW in 2.0.0-rc.28 Filter the column by the result of the field's `formatter` callback function. Default is `false`. Has no effect if the field does not have a `formatter`. Refer to the [Filtering](#filtering) section for more details. | -| `tdClass` | String or Array or Function | Class name (or array of class names) to add to `` data `` cells in the column. If custom classes per cell are required, a callback function can be specified instead. | -| `thClass` | String or Array | Class name (or array of class names) to add to this field's ``/`` heading `` cell. | -| `thStyle` | Object | JavaScript object representing CSS styles you would like to apply to the table ``/`` field ``. | -| `variant` | String | Apply contextual class to all the `` **and** `` in the column - `active`, `success`, `info`, `warning`, `danger`. These variants map to classes `thead-${variant}` (in the header), `table-${variant}` (in the body), or `bg-${variant}` (when the prop `dark` is set). | -| `tdAttr` | Object or Function | JavaScript object representing additional attributes to apply to the `` field `` cell. If custom attributes per cell are required, a callback function can be specified instead. | -| `isRowHeader` | Boolean | When set to `true`, the field's item data cell will be rendered with `` rather than the default of ``. | -| `stickyColumn` | Boolean | NEW in 2.0.0-rc.28 When set to `true`, and the table in in [responsive](#responsive-tables) mode or has [sticky headers](#sticky-headers), will cause the column to become fixed to the left when the table's horizontal scrollbar is scrolled. See [Sticky columns](#sticky-columns) for more details | +| Property | Type | Description | +| ------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `key` | String | The key for selecting data from the record in the items array. Required when setting the `fields` via an array of objects. | +| `label` | String | Appears in the columns table header (and footer if `foot-clone` is set). Defaults to the field's key (in humanized format) if not provided. It's possible to use empty labels by assigning an empty string `""` but be sure you also set `headerTitle` to provide non-sighted users a hint about the column contents. | +| `headerTitle` | String | Text to place on the fields header `` attribute `title`. Defaults to no `title` attribute. | +| `headerAbbr` | String | Text to place on the fields header `` attribute `abbr`. Set this to the unabbreviated version of the label (or title) if label (or title) is an abbreviation. Defaults to no `abbr` attribute. | +| `class` | String or Array | Class name (or array of class names) to add to `` **and** `` in the column. | +| `formatter` | String or Function | A formatter callback function or name of a method in your component, can be used instead of (or in conjunction with) scoped field slots. Refer to [Custom Data Rendering](#custom-data-rendering) for more details. | +| `sortable` | Boolean | Enable sorting on this column. Refer to the [Sorting](#sorting) Section for more details. | +| `sortDirection` | String | Set the initial sort direction on this column when it becomes sorted. Refer to the [Change initial sort direction](#Change-initial-sort-direction) Section for more details. | +| `sortByFormatted` | Boolean | NEW in 2.0.0-rc.28 Sort the column by the result of the field's `formatter` callback function. Default is `false`. Has no effect if the field does not have a `formatter`. Refer to the [Sorting](#sorting) Section for more details. | +| `filterByFormatted` | Boolean | NEW in 2.0.0-rc.28 Filter the column by the result of the field's `formatter` callback function. Default is `false`. Has no effect if the field does not have a `formatter`. Refer to the [Filtering](#filtering) section for more details. | +| `tdClass` | String or Array or Function | Class name (or array of class names) to add to `` data `` cells in the column. If custom classes per cell are required, a callback function can be specified instead. The function will be called as `tdClass( value, key, item )` and it may return an `Array` or `String`. | +| `thClass` | String or Array | Class name (or array of class names) to add to this field's ``/`` heading `` cell. | +| `thStyle` | Object | JavaScript object representing CSS styles you would like to apply to the table ``/`` field ``. | +| `variant` | String | Apply contextual class to all the `` **and** `` in the column - `active`, `success`, `info`, `warning`, `danger`. These variants map to classes `thead-${variant}` (in the header), `table-${variant}` (in the body), or `bg-${variant}` (when the prop `dark` is set). | +| `tdAttr` | Object or Function | JavaScript object representing additional attributes to apply to the `` field `` cell. If custom attributes per cell are required, a callback function can be specified instead. The function will be called as `tdAttr( value, key, item )` and it may return an `Object`. | +| `thAttr` | Object or Function | JavaScript object representing additional attributes to apply to the field's ``/`` heading `` cell. If the field's `isRowHeader` is set to `true`, the attributes will also apply to the `` field `` cell. If custom attributes per cell are required, a callback function can be specified instead. The function will be called as `thAttr( value, key, item, type )` and it may return an `Object`. | +| `isRowHeader` | Boolean | When set to `true`, the field's item data cell will be rendered with `` rather than the default of ``. | +| `stickyColumn` | Boolean | NEW in 2.0.0-rc.28 When set to `true`, and the table in in [responsive](#responsive-tables) mode or has [sticky headers](#sticky-headers), will cause the column to become fixed to the left when the table's horizontal scrollbar is scrolled. See [Sticky columns](#sticky-columns) for more details | **Notes:** diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index a0f155ae2a0..784d8132277 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -33,6 +33,19 @@ export default { } return defValue }, + getThValues(item, key, thValue, type, defValue) { + const parent = this.$parent + if (thValue) { + const value = get(item, key, '') + if (isFunction(thValue)) { + return thValue(value, key, item, type) + } else if (isString(thValue) && isFunction(parent[thValue])) { + return parent[thValue](value, key, item, type) + } + return thValue + } + return defValue + }, // Method to get the value for a field getFormattedValue(item, field) { const key = field.key @@ -169,7 +182,9 @@ export default { }, attrs: { 'aria-colindex': String(colIndex + 1), - ...this.getTdValues(item, key, field.tdAttr, {}) + ...(field.isRowHeader + ? this.getThValues(item, key, field.thAttr, 'row', {}) + : this.getTdValues(item, key, field.tdAttr, {})) } } const slotScope = { diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index 5468d520451..d6bdddb4f1b 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -95,6 +95,7 @@ export default { title: field.headerTitle || null, 'aria-colindex': String(colIndex + 1), 'aria-label': ariaLabel, + ...this.getThValues(null, field.key, field.thAttr, isFoot ? 'foot' : 'head', {}), ...sortAttrs }, on: handlers diff --git a/src/components/table/index.d.ts b/src/components/table/index.d.ts index 5483ce42cdd..7269c60d5fd 100644 --- a/src/components/table/index.d.ts +++ b/src/components/table/index.d.ts @@ -151,6 +151,7 @@ export interface BvTableField { thStyle?: any variant?: BvTableVariant | string tdAttr?: any | ((value: any, key: string, item: any) => any) + thAttr?: any | ((value: any, key: string, item: any, type: string) => any) isRowHeader?: boolean } diff --git a/src/components/table/table-lite.spec.js b/src/components/table/table-lite.spec.js index 5a16dd1c2b9..98417c5e0fb 100644 --- a/src/components/table/table-lite.spec.js +++ b/src/components/table/table-lite.spec.js @@ -589,6 +589,61 @@ describe('table-lite', () => { wrapper.destroy() }) + it('item field thAttr works', async () => { + const Parent = { + methods: { + parentThAttrs(value, key, item, type) { + return { 'data-type': type } + } + } + } + + const wrapper = mount(BTableLite, { + parentComponent: Parent, + propsData: { + items: [{ a: 1, b: 2, c: 3 }], + fields: [ + { key: 'a', thAttr: { 'data-foo': 'bar' } }, + { key: 'b', thAttr: 'parentThAttrs', isRowHeader: true }, + { + key: 'c', + thAttr: (v, k, i, t) => { + return { 'data-type': t } + } + } + ] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('thead > tr').length).toBe(1) + expect(wrapper.findAll('thead > tr > th').length).toBe(3) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + expect(wrapper.findAll('tbody > tr > th').length).toBe(1) + + const $headerThs = wrapper.findAll('thead > tr > th') + expect($headerThs.at(0).attributes('data-foo')).toBe('bar') + expect($headerThs.at(0).attributes('data-type')).not.toBeDefined() + expect($headerThs.at(0).classes().length).toBe(0) + + expect($headerThs.at(1).attributes('data-foo')).not.toBeDefined() + expect($headerThs.at(1).attributes('data-type')).toBe('head') + expect($headerThs.at(1).classes().length).toBe(0) + + expect($headerThs.at(2).attributes('data-foo')).not.toBeDefined() + expect($headerThs.at(2).attributes('data-type')).toBe('head') + expect($headerThs.at(2).classes().length).toBe(0) + + const $bodyThs = wrapper.findAll('tbody > tr > th') + + expect($bodyThs.at(0).attributes('data-foo')).not.toBeDefined() + expect($bodyThs.at(0).attributes('data-type')).toBe('row') + expect($bodyThs.at(0).classes().length).toBe(0) + + wrapper.destroy() + }) + it('item field formatter as function works', async () => { const wrapper = mount(BTableLite, { propsData: { diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 1bd90bb63e1..332ef0a7e6a 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -632,4 +632,59 @@ describe('table', () => { wrapper.destroy() }) + + it('item field thAttr works', async () => { + const Parent = { + methods: { + parentThAttrs(value, key, item, type) { + return { 'data-type': type } + } + } + } + + const wrapper = mount(BTable, { + parentComponent: Parent, + propsData: { + items: [{ a: 1, b: 2, c: 3 }], + fields: [ + { key: 'a', thAttr: { 'data-foo': 'bar' } }, + { key: 'b', thAttr: 'parentThAttrs', isRowHeader: true }, + { + key: 'c', + thAttr: (v, k, i, t) => { + return { 'data-type': t } + } + } + ] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('thead > tr').length).toBe(1) + expect(wrapper.findAll('thead > tr > th').length).toBe(3) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + expect(wrapper.findAll('tbody > tr > th').length).toBe(1) + + const $headerThs = wrapper.findAll('thead > tr > th') + expect($headerThs.at(0).attributes('data-foo')).toBe('bar') + expect($headerThs.at(0).attributes('data-type')).not.toBeDefined() + expect($headerThs.at(0).classes().length).toBe(0) + + expect($headerThs.at(1).attributes('data-foo')).not.toBeDefined() + expect($headerThs.at(1).attributes('data-type')).toBe('head') + expect($headerThs.at(1).classes().length).toBe(0) + + expect($headerThs.at(2).attributes('data-foo')).not.toBeDefined() + expect($headerThs.at(2).attributes('data-type')).toBe('head') + expect($headerThs.at(2).classes().length).toBe(0) + + const $bodyThs = wrapper.findAll('tbody > tr > th') + + expect($bodyThs.at(0).attributes('data-foo')).not.toBeDefined() + expect($bodyThs.at(0).attributes('data-type')).toBe('row') + expect($bodyThs.at(0).classes().length).toBe(0) + + wrapper.destroy() + }) })