Skip to content

feat(b-table): add selectRow() and unselectRow() methods to cell and row-details slot scopes, and new prop no-select-on-click #4283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b1d37d3
feat(b-table): add selectRow() and unselectRow() methods to cell and …
tmorehouse Oct 21, 2019
f0f2200
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
c8f6023
Update package.json
tmorehouse Oct 21, 2019
4c92763
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
2bc95bc
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
1b3630a
Update package.json
tmorehouse Oct 21, 2019
afd2dc7
Update mixin-selectable.js
tmorehouse Oct 21, 2019
7978f4d
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
68b686b
Update mixin-selectable.js
tmorehouse Oct 21, 2019
dafcb13
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
87dd169
Update mixin-selectable.js
tmorehouse Oct 21, 2019
eb7c3a6
Update mixin-selectable.js
tmorehouse Oct 21, 2019
a8c85eb
Update mixin-tbody.js
tmorehouse Oct 21, 2019
1e1bab3
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
50b17e4
Update mixin-selectable.js
tmorehouse Oct 21, 2019
8abc292
Update mixin-tbody-row.js
tmorehouse Oct 21, 2019
19312e9
Update mixin-tbody.js
tmorehouse Oct 21, 2019
060a743
Update mixin-selectable.js
tmorehouse Oct 21, 2019
58f7ed3
Update mixin-selectable.js
tmorehouse Oct 21, 2019
81159b2
Update mixin-selectable.js
tmorehouse Oct 21, 2019
92caa79
Update package.json
tmorehouse Oct 21, 2019
19f08e0
Update _table.scss
tmorehouse Oct 21, 2019
a3592ca
Update mixin-selectable.js
tmorehouse Oct 21, 2019
75b685b
Update README.md
tmorehouse Oct 21, 2019
4d9b490
Update table-selectable.spec.js
tmorehouse Oct 21, 2019
2655da5
Update README.md
tmorehouse Oct 21, 2019
d43330b
Merge branch 'dev' into feat/table-select
tmorehouse Oct 21, 2019
078ac5e
Update mixin-selectable.js
jacobmllr95 Oct 21, 2019
5c70cdb
Merge branch 'feat/table-select' of https://github.com/bootstrap-vue/…
jacobmllr95 Oct 21, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions src/components/table/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,8 @@ The slot's scope variable (`data` in the above sample) will have the following p
| `detailsShowing` | Boolean | Will be `true` if the row's `row-details` scoped slot is visible. See section [Row details support](#row-details-support) below for additional information |
| `toggleDetails` | Function | Can be called to toggle the visibility of the rows `row-details` scoped slot. See section [Row details support](#row-details-support) below for additional information |
| `rowSelected` | Boolean | Will be `true` if the row has been selected. See section [Row select support](#row-select-support) for additional information |
| `selectRow` | Function | When called, selects the current row. See section [Row select support](#row-select-support) for additional information |
| `unselectRow` | Function | When called, unselects the current row. See section [Row select support](#row-select-support) for additional information |

**Notes:**

Expand Down Expand Up @@ -1407,12 +1409,17 @@ for proper reactive detection of changes to it's value. Read more about

**Available `row-details` scoped variable properties:**

| Property | Type | Description |
| --------------- | -------- | ------------------------------------------------------------------------- |
| `item` | Object | The entire row record data object |
| `index` | Number | The current visible row number |
| `fields` | Array | The normalized fields definition array (in the _array of objects_ format) |
| `toggleDetails` | Function | Function to toggle visibility of the row's details slot |
| Property | Type | Description |
| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `item` | Object | The entire row record data object |
| `index` | Number | The current visible row number |
| `fields` | Array | The normalized fields definition array (in the _array of objects_ format) |
| `toggleDetails` | Function | Function to toggle visibility of the row's details slot |
| `rowSelected` | Boolean | Will be `true` if the row has been selected. See section [Row select support](#row-select-support) for additional information |
| `selectRow` | Function | When called, selects the current row. See section [Row select support](#row-select-support) for additional information |
| `unselectRow` | Function | When called, unselects the current row. See section [Row select support](#row-select-support) for additional information |

Note: the row select related scope properties are only available in `<b-table>`.

In the following example, we show two methods of toggling the visibility of the details: one via a
button, and one via a checkbox. We also have the third row details defaulting to have details
Expand Down Expand Up @@ -1510,6 +1517,8 @@ Rows can also be programmatically selected and unselected via the following expo
- In `single` mode, `selectRow(index)` will unselect any previous selected row.
- Attempting to `selectRow(index)` or `unselectRow(index)` on a non-existent row will be ignored.
- The table must be `selectable` for any of these methods to have effect.
- You can disable selection of rows via click events by setting the `no-select-on-click` prop. Rows
will then only be selectable programmatically.

**Row select notes:**

Expand Down Expand Up @@ -2757,10 +2766,11 @@ cells.

### Data row accessibility

When the table is in `selectable` mode (`<b-table>` only), or if there is a `row-clicked` event
listener registered (`<b-table>` and `<b-table-lite>`), all data item rows (`<tr>` elements) will be
placed into the document tab sequence (via `tabindex="0"`) to allow keyboard-only and screen reader
users the ability to click the rows by pressing <kbd>ENTER</kbd>.
When the table is in `selectable` mode (`<b-table>` only, and prop `no-select-on-click` is not set),
or if there is a `row-clicked` event listener registered (`<b-table>` and `<b-table-lite>`), all
data item rows (`<tr>` elements) will be placed into the document tab sequence (via `tabindex="0"`)
to allow keyboard-only and screen reader users the ability to click the rows by pressing
<kbd>ENTER</kbd> or <kbd>SPACE</kbd>.

When the table items rows are placed in the document tab sequence (`<b-table>` and
`<b-table-lite>`), they will also support basic keyboard navigation when focused:
Expand All @@ -2770,8 +2780,6 @@ When the table items rows are placed in the document tab sequence (`<b-table>` a
- <kbd>END</kbd> or <kbd>DOWN</kbd>+<kbd>SHIFT</kbd> will move to the last row
- <kbd>HOME</kbd> or <kbd>UP</kbd>+<kbd>SHIFT</kbd> will move to the first row
- <kbd>ENTER</kbd> or <kbd>SPACE</kbd> to click the row.
- <kbd>SHIFT</kbd> and <kbd>CTRL</kbd> modifiers will also work (depending on the table selectable
mode, for `<b-table>` only).

### Row event accessibility

Expand Down
2 changes: 1 addition & 1 deletion src/components/table/_table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ $bv-escaped-characters: (("<", "%3c"), (">", "%3e"), ("#", "%23"));

// --- Selectable rows ---
.table.b-table {
&.b-table-selectable {
&.b-table-selectable:not(.b-table-selectable-no-click) {
& > tbody > tr {
cursor: pointer;
}
Expand Down
29 changes: 23 additions & 6 deletions src/components/table/helpers/mixin-selectable.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export default {
selectedVariant: {
type: String,
default: () => getComponentConfig('BTable', 'selectedVariant')
},
noSelectOnClick: {
// Disable use of click handlers for row selection
type: Boolean,
default: false
}
},
data() {
Expand All @@ -31,6 +36,12 @@ export default {
isSelectable() {
return this.selectable && this.selectMode
},
hasSelectableRowClick() {
return this.isSelectable && !this.noSelectOnClick
},
supportsSelectableRows() {
return true
},
selectableHasSelection() {
return (
this.isSelectable &&
Expand All @@ -46,11 +57,15 @@ export default {
return {
'b-table-selectable': this.isSelectable,
[`b-table-select-${this.selectMode}`]: this.isSelectable,
'b-table-selecting': this.selectableHasSelection
'b-table-selecting': this.selectableHasSelection,
'b-table-selectable-no-click': this.isSelectable && !this.hasSelectableRowClick
}
},
selectableTableAttrs() {
return {
// TODO:
// Should this attribute not be included when no-select-on-click is set
// since this attribute implies keyboard navigation?
'aria-multiselectable': !this.isSelectable
? null
: this.selectableIsMultiSelect
Expand Down Expand Up @@ -82,6 +97,10 @@ export default {
selectMode(newVal, oldVal) {
this.clearSelected()
},
hasSelectableRowClick(newVal, oldVal) {
this.clearSelected()
this.setSelectionHandlers(!newVal)
},
selectedRows(selectedRows, oldVal) {
if (this.isSelectable && !looseEqual(selectedRows, oldVal)) {
const items = []
Expand All @@ -96,7 +115,7 @@ export default {
}
},
beforeMount() {
// Set up handlers
// Set up handlers if needed
if (this.isSelectable) {
this.setSelectionHandlers(true)
}
Expand Down Expand Up @@ -161,7 +180,7 @@ export default {
}
},
setSelectionHandlers(on) {
const method = on ? '$on' : '$off'
const method = on && !this.noSelectOnClick ? '$on' : '$off'
// Handle row-clicked event
this[method]('row-clicked', this.selectionHandler)
// Clear selection on filter, pagination, and sort changes
Expand All @@ -170,11 +189,9 @@ export default {
},
selectionHandler(item, index, evt) {
/* istanbul ignore if: should never happen */
if (!this.isSelectable) {
if (!this.isSelectable || this.noSelectOnClick) {
// Don't do anything if table is not in selectable mode
/* istanbul ignore next: should never happen */
this.clearSelected()
/* istanbul ignore next: should never happen */
return
}
const selectMode = this.selectMode
Expand Down
16 changes: 13 additions & 3 deletions src/components/table/helpers/mixin-tbody-row.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,12 @@ export default {
toggleDetails: this.toggleDetailsFactory(hasDetailsSlot, item),
detailsShowing: Boolean(item._showDetails)
}
if (this.selectedRows) {
// Add in rowSelected scope property if selectable rows supported
// If table supports selectable mode, then add in the following scope
// this.supportsSelectableRows will be undefined if mixin isn't loaded
if (this.supportsSelectableRows) {
slotScope.rowSelected = this.isRowSelected(rowIndex)
slotScope.selectRow = () => this.selectRow(rowIndex)
slotScope.unselectRow = () => this.unselectRow(rowIndex)
}
// The new `v-slot` syntax doesn't like a slot name starting with
// a square bracket and if using in-document HTML templates, the
Expand All @@ -174,7 +177,7 @@ export default {
const tableStriped = this.striped
const hasDetailsSlot = this.hasNormalizedSlot(detailsSlotName)
const rowShowDetails = Boolean(item._showDetails && hasDetailsSlot)
const hasRowClickHandler = this.$listeners['row-clicked'] || this.isSelectable
const hasRowClickHandler = this.$listeners['row-clicked'] || this.hasSelectableRowClick

// We can return more than one TR if rowDetails enabled
const $rows = []
Expand Down Expand Up @@ -253,6 +256,13 @@ export default {
fields: fields,
toggleDetails: this.toggleDetailsFactory(hasDetailsSlot, item)
}
// If table supports selectable mode, then add in the following scope
// this.supportsSelectableRows will be undefined if mixin isn't loaded
if (this.supportsSelectableRows) {
detailsScope.rowSelected = this.isRowSelected(rowIndex)
detailsScope.selectRow = () => this.selectRow(rowIndex)
detailsScope.unselectRow = () => this.unselectRow(rowIndex)
}

// Render the details slot in a TD
const $details = h(BTd, { props: { colspan: fields.length }, class: this.detailsTdClass }, [
Expand Down
2 changes: 1 addition & 1 deletion src/components/table/helpers/mixin-tbody.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export default {
const items = this.computedItems
// Shortcut to `createElement` (could use `this._c()` instead)
const h = this.$createElement
const hasRowClickHandler = this.$listeners['row-clicked'] || this.isSelectable
const hasRowClickHandler = this.$listeners['row-clicked'] || this.hasSelectableRowClick

// Prepare the tbody rows
const $rows = []
Expand Down
51 changes: 49 additions & 2 deletions src/components/table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@
"prop": "selectedVariant",
"description": "Bootstrap color theme variant to set selected rows to. Use any of the standard Bootstrap theme color variants, or the special table row variant 'active' (default). Set to an empty string to not use a variant"
},
{
"prop": "noSelectOnClick",
"version": "2.1.0",
"description": "Disables row selection via click events. Row selection will be only available programmatically"
},
{
"prop": "showEmpty",
"description": "When enabled, and there are no item records to show, shows a message that there are no rows to show"
Expand Down Expand Up @@ -551,7 +556,19 @@
{
"prop": "rowSelected",
"type": "Boolean",
"description": "Will be true if the row has been selected."
"description": "Will be true if the row has been selected. Only applicable when table is in selectable mode"
},
{
"prop": "selectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to select the current row. Only applicable when table is in selectable mode"
},
{
"prop": "unselectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to unselect the current row. Only applicable when table is in selectable mode"
}
]
},
Expand Down Expand Up @@ -595,7 +612,19 @@
{
"prop": "rowSelected",
"type": "Boolean",
"description": "Will be true if the row has been selected."
"description": "Will be true if the row has been selected. Only applicable when table is in selectable mode"
},
{
"prop": "selectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to select the current row. Only applicable when table is in selectable mode"
},
{
"prop": "unselectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to unselect the current row. Only applicable when table is in selectable mode"
}
]
},
Expand Down Expand Up @@ -782,6 +811,24 @@
"prop": "toggleDetails",
"type": "Function",
"description": "Function to toggle visibility of the row's details slot"
},
{
"prop": "rowSelected",
"type": "Boolean",
"version": "2.1.0",
"description": "Will be true if the row has been selected. Only applicable when table is in selectable mode"
},
{
"prop": "selectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to select the current row. Only applicable when table is in selectable mode"
},
{
"prop": "unselectRow",
"type": "Function",
"version": "2.1.0",
"description": "Can be called to unselect the current row. Only applicable when table is in selectable mode"
}
]
},
Expand Down
28 changes: 28 additions & 0 deletions src/components/table/table-selectable.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('table > row select', () => {
await waitNT(wrapper.vm)
expect(wrapper.attributes('aria-multiselectable')).not.toBeDefined()
expect(wrapper.classes()).not.toContain('b-table-selectable')
expect(wrapper.classes()).not.toContain('b-table-selectable-no-click')
expect(wrapper.classes()).not.toContain('b-table-selecting')
expect(wrapper.classes()).not.toContain('b-table-select-single')
expect(wrapper.classes()).not.toContain('b-table-select-multi')
Expand Down Expand Up @@ -59,6 +60,7 @@ describe('table > row select', () => {
await waitNT(wrapper.vm)
expect(wrapper.attributes('aria-multiselectable')).not.toBeDefined()
expect(wrapper.classes()).not.toContain('b-table-selectable')
expect(wrapper.classes()).not.toContain('b-table-selectable-no-click')
expect(wrapper.classes()).not.toContain('b-table-selecting')
expect(wrapper.classes()).not.toContain('b-table-select-single')
expect(wrapper.classes()).not.toContain('b-table-select-multi')
Expand All @@ -73,6 +75,31 @@ describe('table > row select', () => {
wrapper.destroy()
})

it('has class b-table-selectable-no-click when prop no-select-on-click set', async () => {
const wrapper = mount(BTable, {
propsData: {
fields: testFields,
items: testItems,
selectable: true,
selectMode: 'single',
noSelectOnClick: true
}
})

expect(wrapper).toBeDefined()
await waitNT(wrapper.vm)
expect(wrapper.attributes('aria-multiselectable')).toBe('false')
expect(wrapper.classes()).toContain('b-table-selectable')
expect(wrapper.classes()).toContain('b-table-select-single')
expect(wrapper.classes()).toContain('b-table-selectable-no-click')
expect(wrapper.classes()).not.toContain('b-table-selecting')
expect(wrapper.classes()).not.toContain('b-table-select-multi')
expect(wrapper.classes()).not.toContain('b-table-select-range')
expect(wrapper.emitted('row-selected')).not.toBeDefined()

wrapper.destroy()
})

it('select mode single works', async () => {
const wrapper = mount(BTable, {
propsData: {
Expand All @@ -89,6 +116,7 @@ describe('table > row select', () => {
expect(wrapper.attributes('aria-multiselectable')).toBe('false')
expect(wrapper.classes()).toContain('b-table-selectable')
expect(wrapper.classes()).toContain('b-table-select-single')
expect(wrapper.classes()).not.toContain('b-table-selectable-no-click')
expect(wrapper.classes()).not.toContain('b-table-selecting')
expect(wrapper.classes()).not.toContain('b-table-select-multi')
expect(wrapper.classes()).not.toContain('b-table-select-range')
Expand Down