Skip to content

Commit 998bd4f

Browse files
authored
feat(b-table-simple): new <table> wrapper component that allows users to render their own <thead>, <tfoot>, <body> (bootstrap-vue#3799)
1 parent 7a0a5a6 commit 998bd4f

15 files changed

+434
-58
lines changed

src/components/index.esm.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ export { BSpinner } from './spinner/spinner'
286286
export { TablePlugin } from './table'
287287
export { BTable } from './table/table'
288288
export { BTableLite } from './table/table-lite'
289+
export { BTableSimple } from './table/table-simple'
289290

290291
// export * from './tabs'
291292
export { TabsPlugin } from './tabs'

src/components/table/README.md

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
> For displaying tabular data, `<b-table>` supports pagination, filtering, sorting, custom
44
> rendering, various style options, events, and asynchronous data. For simple display of tabular
5-
> data without all the fancy features, BootstrapVue provides a lightweight alternative component
6-
> [`<b-table-lite>`](#light-weight-tables).
5+
> data without all the fancy features, BootstrapVue provides lightweight alternative components
6+
> [`<b-table-lite>`](#light-weight-tables) and [`<b-table-simple>`](#simple-tables).
77
88
**Example: Basic usage**
99

@@ -2098,10 +2098,87 @@ tabular data. The `<b-table-lite>` component provides all of the styling and for
20982098
- Fixed top and bottom rows
20992099
- Empty row support
21002100

2101+
## Simple tables
2102+
2103+
<span class="badge badge-info small">NEW in v2.0.0-rc.28</span>
2104+
2105+
The `<b-table-simple>` component gives the user complete control over the rendering of the table
2106+
content, while providing basic Bootstrap v4 table styling. `<b-table-simple>` is a wrapper component
2107+
around the `<table>` element. Inside the component, via the `default` slot, you can use any or all
2108+
of the regular HTML5 table elements: `<thead>`, `<tfoot>`, `<tbody>`, `<tr>`, `<th>`, `<td>`,
2109+
`<caption>`, and `<colgroup>`.
2110+
2111+
`<b-table-simple>` provides basic styling options via props: `striped`, `bordered`, `borderless`,
2112+
`outlined`, `small`, `hover`, `dark`, `fixed` and `responsive`.
2113+
2114+
Since `b-table-simple` is just a wrapper component, of which you will need to render content inside,
2115+
it does not provide any of the advanced features of `<b-table>` (i.e. row events, head events,
2116+
sorting, pagination, filtering, stacked mode, etc).
2117+
2118+
```html
2119+
<div>
2120+
<b-table-simple hover small bordered responsive="sm">
2121+
<thead class="text-center thead-light">
2122+
<tr>
2123+
<th colspan="2">Name</th>
2124+
<th rowspan="2" class="align-middle">Age</th>
2125+
<th colspan="3" rowspan="2" class="align-middle">Data</th>
2126+
</tr>
2127+
<tr>
2128+
<th>Last</th>
2129+
<th>First</th>
2130+
</td>
2131+
</thead>
2132+
<tbody>
2133+
<tr>
2134+
<td>Macdonald</td>
2135+
<td>Dickerson</td>
2136+
<td>42</td>
2137+
<td>Foo</td>
2138+
<td>Bar</td>
2139+
<td>Baz</td>
2140+
</tr>
2141+
<tr class="table-info">
2142+
<td>Wilson</td>
2143+
<td>Geneva</td>
2144+
<td>35</td>
2145+
<td colspan="2">1234567</td>
2146+
<td>987</td>
2147+
</tr>
2148+
<tr>
2149+
<td rowspan="2">Shaw</td>
2150+
<td>Larsen</td>
2151+
<td>23</td>
2152+
<td>AAA</td>
2153+
<td colspan="2">BBBBB</td>
2154+
</tr>
2155+
<tr>
2156+
<td>Linda</td>
2157+
<td>22</td>
2158+
<td>CCC</td>
2159+
<td class="table-danger">YYY</td>
2160+
<td>ZZZ</td>
2161+
</tr>
2162+
</tbody>
2163+
</b-table>
2164+
</div>
2165+
2166+
<!-- b-table-simple.vue -->
2167+
```
2168+
2169+
Row and cell variant classes are in the form `table-{variant}`, unless you have the table in `dark`
2170+
mode, in which case you should use `bg-{variant}` instead.
2171+
2172+
When in `responsive` mode, the `<table>` element is wrapped inside a `<div>` element. If you need to
2173+
apply additional classes to the `<table>` element, use the `table-classes` prop.
2174+
2175+
Any additional attributes given to `<b-table-simple>` will always be applied to the `<table>`
2176+
element.
2177+
21012178
## Accessibility
21022179

21032180
The `<b-table>` and `<b-table-lite>` components, when using specific features, will attempt to
2104-
provide the best accessibility features possible.
2181+
provide the best accessibility markup possible.
21052182

21062183
### Heading accessibility
21072184

@@ -2130,8 +2207,9 @@ keyboard navigation when focused:
21302207

21312208
### Row event accessibility
21322209

2133-
Note the following row based events/actions are not considered accessible, and should only be used
2134-
if the functionality is non critical or can be provided via other means:
2210+
Note the following row based events/actions (available with `<b-table>` and `<b-table-lite>`) are
2211+
not considered accessible, and should only be used if the functionality is non critical or can be
2212+
provided via other means:
21352213

21362214
- `row-dblclicked`
21372215
- `row-contextmenu`

src/components/table/helpers/constants.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
// Constants used by table helpers
22

3-
// Object of item keys that should be ignored for headers and stringification and filter events
3+
// Object of item keys that should be ignored for headers and
4+
// stringification and filter events
45
export const IGNORED_FIELD_KEYS = {
56
_rowVariant: true,
67
_cellVariants: true,
78
_showDetails: true
89
}
910

10-
// Filter CSS Selector for click/dblclick/etc events
11+
// Filter CSS selector for click/dblclick/etc. events
1112
// If any of these selectors match the clicked element, we ignore the event
1213
export const EVENT_FILTER = [
1314
'a',
14-
'a *', // include content inside links
15+
'a *', // Include content inside links
1516
'button',
16-
'button *', // include content inside buttons
17+
'button *', // Include content inside buttons
1718
'input:not(.disabled):not([disabled])',
1819
'select:not(.disabled):not([disabled])',
1920
'textarea:not(.disabled):not([disabled])',

src/components/table/helpers/default-sort-compare.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import stringifyObjectValues from './stringify-object-values'
66
//
77
// TODO: Add option to sort by multiple columns (tri-state per column,
88
// plus order of columns in sort) where sortBy could be an array
9-
// of objects [ {key: 'foo', sortDir: 'asc'}, {key:'bar', sortDir: 'desc'} ...]
10-
// or an array of arrays [ ['foo','asc'], ['bar','desc'] ]
9+
// of objects `[ {key: 'foo', sortDir: 'asc'}, {key:'bar', sortDir: 'desc'} ...]`
10+
// or an array of arrays `[ ['foo','asc'], ['bar','desc'] ]`
1111
// Multisort will most likely be handled in mixin-sort.js by
1212
// calling this method for each sortBy
1313
const defaultSortCompare = (a, b, sortBy, formatter, localeOpts, locale) => {

src/components/table/helpers/mixin-busy.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ export default {
3535
}
3636
return false
3737
},
38-
// Renter the busy indicator or return null if not busy
38+
// Render the busy indicator or return `null` if not busy
3939
renderBusy() {
4040
const h = this.$createElement
4141

42-
// Return a busy indicator row, or null if not busy
42+
// Return a busy indicator row, or `null` if not busy
4343
if (this.computedBusy && this.hasNormalizedSlot('table-busy')) {
4444
// Show the busy slot
4545
const trAttrs = {
@@ -64,8 +64,8 @@ export default {
6464
[h('td', { attrs: tdAttrs }, [this.normalizeSlot('table-busy')])]
6565
)
6666
} else {
67-
// We return null here so that we can determine if we need to
68-
// render the table items rows or not.
67+
// We return `null` here so that we can determine if we need to
68+
// render the table items rows or not
6969
return null
7070
}
7171
}

src/components/table/helpers/mixin-caption.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default {
2121
}
2222
},
2323
captionId() {
24-
// Even though this.safeId looks like a method, it is a computed prop
24+
// Even though `this.safeId` looks like a method, it is a computed prop
2525
// that returns a new function if the underlying ID changes
2626
return this.isStacked ? this.safeId('_caption_') : null
2727
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Mixin for providing stacked tables
2+
3+
export default {
4+
props: {
5+
stacked: {
6+
type: [Boolean, String],
7+
default: false
8+
}
9+
},
10+
computed: {
11+
isStacked() {
12+
return this.stacked === '' ? true : Boolean(this.stacked)
13+
},
14+
stackedTableClasses() {
15+
return {
16+
'b-table-stacked': this.stacked === true || this.stacked === '',
17+
[`b-table-stacked-${this.stacked}`]: this.stacked !== true && this.stacked
18+
}
19+
}
20+
}
21+
}

src/components/table/helpers/mixin-table-renderer.js

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// Main `<table>` render mixin
2-
// Which indlues all main table stlying options
2+
// Which includes all main table styling options
33

44
export default {
5-
// Don't place ATTRS on root element automatically, as table could be wrapped in responsive div
5+
// Don't place attributes on root element automatically,
6+
// as table could be wrapped in responsive `<div>`
67
inheritAttrs: false,
78
props: {
89
striped: {
@@ -41,20 +42,13 @@ export default {
4142
type: [Boolean, String],
4243
default: false
4344
},
44-
stacked: {
45-
type: [Boolean, String],
46-
default: false
47-
},
4845
tableClass: {
4946
type: [String, Array, Object],
5047
default: null
5148
}
5249
},
5350
computed: {
5451
// Layout related computed props
55-
isStacked() {
56-
return this.stacked === '' ? true : this.stacked
57-
},
5852
isResponsive() {
5953
const responsive = this.responsive === '' ? true : this.responsive
6054
return this.isStacked ? false : responsive
@@ -67,69 +61,88 @@ export default {
6761
: ''
6862
},
6963
tableClasses() {
64+
const hover = this.isTableSimple
65+
? this.hover
66+
: this.hover && this.computedItems.length > 0 && !this.computedBusy
67+
7068
return [
7169
// User supplied classes
7270
this.tableClass,
7371
// Styling classes
7472
{
7573
'table-striped': this.striped,
76-
'table-hover': this.hover && this.computedItems.length > 0 && !this.computedBusy,
74+
'table-hover': hover,
7775
'table-dark': this.dark,
7876
'table-bordered': this.bordered,
7977
'table-borderless': this.borderless,
8078
'table-sm': this.small,
81-
border: this.outlined,
8279
// The following are b-table custom styles
83-
'b-table-fixed': this.fixed,
84-
'b-table-stacked': this.stacked === true || this.stacked === '',
85-
[`b-table-stacked-${this.stacked}`]: this.stacked !== true && this.stacked
80+
border: this.outlined,
81+
'b-table-fixed': this.fixed
8682
},
83+
// Stacked table classes
84+
this.stackedTableClasses,
8785
// Selectable classes
8886
this.selectableTableClasses
8987
]
9088
},
9189
tableAttrs() {
92-
// Preserve user supplied aria-describedby, if provided in $attrs
90+
// Preserve user supplied aria-describedby, if provided in `$attrs`
9391
const adb =
9492
[(this.$attrs || {})['aria-describedby'], this.captionId].filter(Boolean).join(' ') || null
9593
const items = this.computedItems
94+
const filteredItems = this.filteredItems
9695
const fields = this.computedFields
9796
const selectableAttrs = this.selectableTableAttrs || {}
97+
const ariaAttrs = this.isTableSimple
98+
? {}
99+
: {
100+
'aria-busy': this.computedBusy ? 'true' : 'false',
101+
'aria-colcount': String(fields.length),
102+
'aria-describedby': adb
103+
}
104+
const rowCount =
105+
items && filteredItems && filteredItems.length > items.length
106+
? String(filteredItems.length)
107+
: null
108+
98109
return {
99-
// We set aria-rowcount before merging in $attrs, in case user has supplied their own
100-
'aria-rowcount':
101-
this.filteredItems && this.filteredItems.length > items.length
102-
? String(this.filteredItems.length)
103-
: null,
104-
// Merge in user supplied $attrs if any
110+
// We set `aria-rowcount` before merging in `$attrs`,
111+
// in case user has supplied their own
112+
'aria-rowcount': rowCount,
113+
// Merge in user supplied `$attrs` if any
105114
...this.$attrs,
106-
// Now we can override any $attrs here
115+
// Now we can override any `$attrs` here
107116
id: this.safeId(),
108-
role: this.isStacked ? 'table' : null,
109-
'aria-busy': this.computedBusy ? 'true' : 'false',
110-
'aria-colcount': String(fields.length),
111-
'aria-describedby': adb,
117+
role: 'table',
118+
...ariaAttrs,
112119
...selectableAttrs
113120
}
114121
}
115122
},
116123
render(h) {
117-
// Build the caption (from caption mixin)
118-
const $caption = this.renderCaption ? this.renderCaption() : null
124+
const $content = []
119125

120-
// Build the colgroup
121-
const $colgroup = this.renderColgroup ? this.renderColgroup() : null
126+
if (this.isTableSimple) {
127+
$content.push(this.normalizeSlot('default', {}))
128+
} else {
129+
// Build the `<caption>` (from caption mixin)
130+
$content.push(this.renderCaption ? this.renderCaption() : null)
122131

123-
// Build the thead
124-
const $thead = this.renderThead()
132+
// Build the `<colgroup>`
133+
$content.push(this.renderColgroup ? this.renderColgroup() : null)
125134

126-
// Build the tfoot
127-
const $tfoot = this.renderTfoot()
135+
// Build the `<thead>`
136+
$content.push(this.renderThead ? this.renderThead() : null)
128137

129-
// Build the tbody
130-
const $tbody = this.renderTbody()
138+
// Build the `<tfoot>`
139+
$content.push(this.renderTfoot ? this.renderTfoot() : null)
140+
141+
// Build the `<tbody>`
142+
$content.push(this.renderTbody ? this.renderTbody() : null)
143+
}
131144

132-
// Assemble table
145+
// Assemble `<table>`
133146
const $table = h(
134147
'table',
135148
{
@@ -138,7 +151,7 @@ export default {
138151
class: this.tableClasses,
139152
attrs: this.tableAttrs
140153
},
141-
[$caption, $colgroup, $thead, $tfoot, $tbody].filter(Boolean)
154+
$content.filter(Boolean)
142155
)
143156

144157
// Add responsive wrapper if needed and return table

src/components/table/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export declare class BTableLite extends BvComponent {
4444
tbodyTrClass?: string | Array<any> | object | BvTableTbodyTrClassCallback
4545
}
4646

47+
// Component: b-table-simple
48+
export declare class BTableSimple extends BvComponent {
49+
// Props
50+
id?: string
51+
}
52+
4753
export type BvTableVariant =
4854
| 'active'
4955
| 'success'

src/components/table/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { BTable } from './table'
22
import { BTableLite } from './table-lite'
3+
import { BTableSimple } from './table-simple'
34
import { pluginFactory } from '../../utils/plugins'
45

56
const TablePlugin = /*#__PURE__*/ pluginFactory({
6-
components: { BTable, BTableLite }
7+
components: { BTable, BTableLite, BTableSimple }
78
})
89

9-
export { TablePlugin, BTable, BTableLite }
10+
export { TablePlugin, BTable, BTableLite, BTableSimple }
1011

1112
export default TablePlugin

0 commit comments

Comments
 (0)