Skip to content

Commit

Permalink
feat(b-table, b-table-lite): add new scoped slot custom-foot to all…
Browse files Browse the repository at this point in the history
…ow user to create their own table footer (closes #3960) (#4027)
  • Loading branch information
tmorehouse committed Sep 5, 2019
1 parent 81efb89 commit cbeeef9
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 11 deletions.
26 changes: 24 additions & 2 deletions src/components/table/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,28 @@ Slot `thead-top` can be optionally scoped, receiving an object with the followin
| `selectAllRows` | Method | Select all rows (applicable if the table is in [`selectable`](#row-select-support) mode |
| `clearSelected` | Method | Unselect all rows (applicable if the table is in [`selectable`](#row-select-support) mode |

### Creating a custom footer

If you need greater layout control of the content of the `<tfoot>`, you can use the optionally
scoped slot `custom-foot` to provide your own rows and cells. Use BootstrapVue's
[table helper sub-components](#table-helper-components) `<b-tr>`, `<b-th>`, and `<b-td>` to generate
your custom footer layout.

Slot `custom-foot` can be optionally scoped, receiving an object with the following properties:

| Property | Type | Description |
| --------- | ------ | ------------------------------------------------------------------------------------------ |
| `columns` | Number | The number of columns in the rendered table |
| `fields` | Array | Array of field definition objects (normalized to the array of objects format) |
| `items` | Array | Array of the currently _displayed_ items records - after filtering, sorting and pagination |

**Notes:**

- The `custom-foot` slot will **not** be rendered if the `foot-clone` prop has been set.
- `head-clicked` events are not be emitted when clicking on `custom-foot` cells.
- Sorting and sorting icons are not available for cells in the `custom-foot` slot.
- The custom footer will not be shown when the table is in visually stacked mode.

## Custom empty and emptyfiltered rendering via slots

Aside from using `empty-text`, `empty-filtered-text`, `empty-html`, and `empty-filtered-html`, it is
Expand Down Expand Up @@ -2654,8 +2676,8 @@ helper components. `TableSimplePlugin` is available as a top level named export.
## Table helper components

BootstrapVue provides additional helper child components when using `<b-table-simple>`, or the named
slots `top-row`, `bottom-row`, and `thead-top` (all of which accept table child elements). The
helper components are as follows:
slots `top-row`, `bottom-row`, `thead-top`, and `custom-foot` (all of which accept table child
elements). The helper components are as follows:

- `b-tbody` (`<b-table-simple>` only)
- `b-thead` (`<b-table-simple>` only)
Expand Down
25 changes: 22 additions & 3 deletions src/components/table/helpers/mixin-tfoot.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getComponentConfig } from '../../../utils/config'
import { BTfoot } from '../tfoot'

export default {
props: {
Expand All @@ -20,11 +21,29 @@ export default {
}
},
methods: {
renderTfoot() {
renderTFootCustom() {
const h = this.$createElement

if (this.hasNormalizedSlot('custom-foot')) {
return h(
BTfoot,
{
key: 'bv-tfoot-custom',
class: this.tfootClass || null,
props: { footVariant: this.footVariant || this.headVariant || null }
},
this.normalizeSlot('custom-foot', {
items: this.computedItems.slice(),
fields: this.computedFields.slice(),
columns: this.computedFields.length
})
)
} else {
return h()
}
},
renderTfoot() {
// Passing true to renderThead will make it render a tfoot
return this.footClone ? this.renderThead(true) : h()
return this.footClone ? this.renderThead(true) : this.renderTFootCustom()
}
}
}
20 changes: 14 additions & 6 deletions src/components/table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
},
{
"event": "head-clicked",
"description": "Emitted when a header or footer cell is clicked.",
"description": "Emitted when a header or footer cell is clicked. Not applicable for 'custom-foot' slot.",
"args": [
{
"arg": "key",
Expand Down Expand Up @@ -250,15 +250,19 @@
},
{
"name": "thead-top",
"description": "Slot above the column headers in the `thead` element for user-supplied rows (optionally scoped: columns - number of TDs to provide, fields - array of field definition objects)"
"description": "Slot above the column headers in the `thead` element for user-supplied B-TR's with B-TH/B-TD (optionally scoped: columns - number of TDs to provide, fields - array of field definition objects)"
},
{
"name": "top-row",
"description": "Fixed top row slot for user supplied TD cells (Optionally scoped: columns - number of TDs to provide, fields - array of field definition objects)"
"description": "Fixed top row slot for user supplied B-TD cells (Optionally scoped: columns - number of B-TDs to provide, fields - array of field definition objects)"
},
{
"name": "bottom-row",
"description": "Fixed bottom row slot for user supplied TD cells (Optionally Scoped: columns - number of TDs to provide, fields - array of field definition objects)"
"description": "Fixed bottom row slot for user supplied B-TD cells (Optionally Scoped: columns - number of B-TDs to provide, fields - array of field definition objects)"
},
{
"name": "custom-foot",
"description": "Custom footer content slot for user supplied B-TR, B-TH, B-TD (Optionally Scoped: columns - number columns, fields - array of field definition objects, items - array of currently displayed row items)"
}
]
},
Expand Down Expand Up @@ -375,7 +379,7 @@
},
{
"event": "head-clicked",
"description": "Emitted when a header or footer cell is clicked.",
"description": "Emitted when a header or footer cell is clicked. Not applicable for 'custom-foot' slot.",
"args": [
{
"arg": "key",
Expand Down Expand Up @@ -435,7 +439,11 @@
},
{
"name": "thead-top",
"description": "Slot above the column headers in the `thead` element for user-supplied rows (optionally scoped: columns - number of TDs to provide, fields - array of field definition objects)"
"description": "Slot above the column headers in the `thead` element for user-supplied B-TR with B-TH/B-TD (optionally scoped: columns - number of TDs to provide, fields - array of field definition objects)"
},
{
"name": "custom-foot",
"description": "Custom footer content slot for user supplied B-TR's with B-TH/B-TD (Optionally Scoped: columns - number columns, fields - array of field definition objects, items - array of currently displayed row items)"
}
]
},
Expand Down
91 changes: 91 additions & 0 deletions src/components/table/table-tfoot-custom.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { mount } from '@vue/test-utils'
import { BTable } from './table'

const testItems = [{ a: 1, b: 2, c: 3 }]
const testFields = [{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }, { key: 'c', label: 'C' }]

describe('table > custom tfoot slot', () => {
it('should not render tfoot by default', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
footClone: false
}
})
expect(wrapper).toBeDefined()
expect(wrapper.is('table')).toBe(true)
expect(wrapper.find('thead').exists()).toBe(true)
expect(wrapper.find('tbody').exists()).toBe(true)
expect(wrapper.find('tfoot').exists()).toBe(false)

wrapper.destroy()
})

it('should render custom-foot slot inside b-tfoot', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
footClone: false
},
slots: {
'custom-foot': '<tr><td colspan="3">CUSTOM-FOOTER</td></tr>'
}
})
expect(wrapper).toBeDefined()
expect(wrapper.is('table')).toBe(true)
expect(wrapper.find('thead').exists()).toBe(true)
expect(wrapper.find('tbody').exists()).toBe(true)
expect(wrapper.find('tfoot').exists()).toBe(true)
expect(wrapper.find('tfoot').text()).toContain('CUSTOM-FOOTER')
expect(wrapper.find('tfoot').classes().length).toBe(0)

wrapper.destroy()
})

it('should not render custom-foot slot when foot-clone is true', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
footClone: true
},
slots: {
'custom-foot': '<tr><td colspan="3">CUSTOM-FOOTER</td></tr>'
}
})
expect(wrapper).toBeDefined()
expect(wrapper.is('table')).toBe(true)
expect(wrapper.find('thead').exists()).toBe(true)
expect(wrapper.find('tbody').exists()).toBe(true)
expect(wrapper.find('tfoot').exists()).toBe(true)
expect(wrapper.find('tfoot').text()).not.toContain('CUSTOM-FOOTER')

wrapper.destroy()
})

it('should have foot-variant on custom-foot slot', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
footClone: false,
footVariant: 'dark'
},
slots: {
'custom-foot': '<tr><td colspan="3">CUSTOM-FOOTER</td></tr>'
}
})
expect(wrapper).toBeDefined()
expect(wrapper.is('table')).toBe(true)
expect(wrapper.find('thead').exists()).toBe(true)
expect(wrapper.find('tbody').exists()).toBe(true)
expect(wrapper.find('tfoot').exists()).toBe(true)
expect(wrapper.find('tfoot').text()).toContain('CUSTOM-FOOTER')
expect(wrapper.find('tfoot').classes()).toContain('thead-dark')
expect(wrapper.find('tfoot').classes().length).toBe(1)

wrapper.destroy()
})
})

0 comments on commit cbeeef9

Please sign in to comment.