From 4d1bc61467fc67fddd001ec33557db8d04a87c9a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 16:34:08 -0300 Subject: [PATCH 001/159] Create pagination-mixin.js --- .../table/helpers/pagination-mixin.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/components/table/helpers/pagination-mixin.js diff --git a/src/components/table/helpers/pagination-mixin.js b/src/components/table/helpers/pagination-mixin.js new file mode 100644 index 00000000000..cb668244848 --- /dev/null +++ b/src/components/table/helpers/pagination-mixin.js @@ -0,0 +1,29 @@ +export default { + props: { + perPage: { + type: [Number, String], + default: 0 + }, + currentPage: { + type: [Number, String], + default: 1 + } + }, + computed: { + localPaging() { + return this.hasProvider ? !!this.noProviderPaging : true + }, + paginatedItems() { + let items = this.sortedItems || [] + const currentPage = Math.max(parseInt(this.currentPage, 10) || 1, 1) + const perPage = Math.max(parseInt(this.perPage, 10) || 0, 0) + // Apply local pagination + if (this.localPaging && !!perPage) { + // Grab the current page of data (which may be past filtered items limit) + items = items.slice((currentPage - 1) * perPage, currentPage * perPage) + } + // Return the items to display in the table + return items + } + } +} From 9aa4e553b56da0aa8945e7286cb33e5f4b5be1d7 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 16:49:55 -0300 Subject: [PATCH 002/159] Rename pagination-mixin.js to mixin-pagination.js --- .../table/helpers/{pagination-mixin.js => mixin-pagination.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/table/helpers/{pagination-mixin.js => mixin-pagination.js} (100%) diff --git a/src/components/table/helpers/pagination-mixin.js b/src/components/table/helpers/mixin-pagination.js similarity index 100% rename from src/components/table/helpers/pagination-mixin.js rename to src/components/table/helpers/mixin-pagination.js From 8aa783367dcb1c95953a92e840b06e7f21cdd9f0 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 16:54:22 -0300 Subject: [PATCH 003/159] Create mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 src/components/table/helpers/mixin-sorting.js diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js new file mode 100644 index 00000000000..d88b625b383 --- /dev/null +++ b/src/components/table/helpers/mixin-sorting.js @@ -0,0 +1,228 @@ +import stableSort from '../../../utils/stable-sort' +import startCase from '../../../utils/startcase' +import { arrayIncludes } from '../../../utils/array' +import defaultSortCompare from './default-sort-compare' + +export default { + props: { + sortBy: { + type: String, + default: null + }, + sortDesc: { + // To Do: Make this tri-state: true, false, null + type: Boolean, + default: false + }, + sortDirection: { + type: String, + default: 'asc', + validator: direction => arrayIncludes(['asc', 'desc', 'last'], direction) + }, + sortCompare: { + type: Function, + default: null + }, + noSortReset: { + type: Boolean, + default: false + }, + labelSortAsc: { + type: String, + default: 'Click to sort Ascending' + }, + labelSortDesc: { + type: String, + default: 'Click to sort Descending' + }, + labelSortClear: { + type: String, + default: 'Click to clear sorting' + }, + noLocalSorting: { + type: Boolean, + default: false + }, + noFooterSorting: { + type: Boolean, + default: false + } + }, + data() { + return { + localSortBy: this.sortBy || '', + localSortDesc: this.sortDesc || false, + } + }, + computed: { + localSorting() { + return this.hasProvider ? !!this.noProviderSorting : !this.noLocalSorting + }, + isSortable() { + return this.computedFields.some(f => f.sortable !== undefined) + }, + sortedItems() { + // Sorts the filtered items and returns a new array of the sorted items + // or the original items array if not sorted. + let items = (this.filteredItems || []).slice() + const sortBy = this.localSortBy + const sortDesc = this.localSortDesc + const sortCompare = this.sortCompare + const localSorting = this.localSorting + if (sortBy && localSorting) { + // stableSort returns a new array, and leaves the original array intact + return stableSort(items, (a, b) => { + let result = null + if (typeof sortCompare === 'function') { + // Call user provided sortCompare routine + result = sortCompare(a, b, sortBy, sortDesc) + } + if (result === null || result === undefined || result === false) { + // Fallback to built-in defaultSortCompare if sortCompare + // is not defined or returns null/false + result = defaultSortCompare(a, b, sortBy) + } + // Negate result if sorting in descending order + return (result || 0) * (sortDesc ? -1 : 1) + }) + } + return items + }, + // Classes and Attributes to add to the + // Here for possible future use + sortingTableClasses() { + return {} + }, + sortingTableAttrs() { + return {} + }, + }, + watch: { + isSortable(newVal, oldVal) { + if (newVal) { + if (this.isSortable) { + this.$on('head-clicked', this.handleSort) + } + } else { + this.$off('head-clicked', this.handleSort) + } + }, + sortDesc(newVal, oldVal) { + if (newVal === this.localSortDesc) { + /* istanbul ignore next */ + return + } + this.localSortDesc = newVal || false + }, + sortBy(newVal, oldVal) { + if (newVal === this.localSortBy) { + /* istanbul ignore next */ + return + } + this.localSortBy = newVal || null + }, + // Update .sync props + localSortDesc(newVal, oldVal) { + // Emit update to sort-desc.sync + if (newVal !== oldVal) { + this.$emit('update:sortDesc', newVal) + } + }, + localSortBy(newVal, oldVal) { + if (newVal !== oldVal) { + this.$emit('update:sortBy', newVal) + } + } + }, + created() { + if (this.isSortable) { + this.$on('head-clicked', this.handleSort) + } + }, + methods: { + // Handlers + // Need to move from thead-mixin + handleSort(key, field, evt, isFoot) { + if (isFoot && this.noFoooterSorting) { + return + } + if (!this.isSortable) { + /* istanbul ignore next */ + return + } + // TODO: make this tri-state sorting + // cycle desc => asc => none => desc => ... + let sortChanged = false + const toggleLocalSortDesc = () => { + const sortDirection = field.sortDirection || this.sortDirection + if (sortDirection === 'asc') { + this.localSortDesc = false + } else if (sortDirection === 'desc') { + this.localSortDesc = true + } + } + if (field.sortable) { + if (key === this.localSortBy) { + // Change sorting direction on current column + this.localSortDesc = !this.localSortDesc + } else { + // Start sorting this column ascending + this.localSortBy = key + toggleLocalSortDesc() + } + sortChanged = true + } else if (this.localSortBy && !this.noSortReset) { + this.localSortBy = null + toggleLocalSortDesc() + sortChanged = true + } + if (sortChanged) { + // Sorting parameters changed + this.$emit('sort-changed', this.context) + } + }, + // methods to compute classes and attrs for thead>th cells + sortTheadThClasses(key, field, idx) { + return { + // No Classes for sorting currently... + // All styles targeted using aria-* attrs + } + }, + sortTheadThAttrs(key, field, isFoot) { + if (!this.isSortable || (isFoot && this.noFooterSorting)) { + // No atributes if not a sortable table + return {} + } + const sortable = field.sortable + let ariaLabel = '' + if (!field.label.trim() && !field.headerTitle) { + // In case field's label and title are empty/blank, we need to + // add a hint about what the column is about for non-sighted users + /* istanbul ignore next */ + ariaLabel = startCase(key) + } + const sortable = field.sortable + const ariaLabelSorting = sortable + ? this.localSortDesc && this.localSortBy === key + ? this.labelSortAsc + : this.labelSortDesc + : this.labelSortClear + // Assemble the aria-label + ariaLabel = [ariaLabel, ariaLabelSorting].filter(Boolean).join(': ') + // Assemble the aria-sort + const ariaSort = + sortable && this.localSortBy === key + ? this.localSortDesc + ? 'descending' + : 'ascending' + : sortable + ? 'none' + : null + // Return the attributes + return { + 'aria-label': ariaLabel || null, + 'aria-sort': ariaSort + } + } + } +} From 04766fa7e567e05218bac29c8bcfc149b07af180 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 16:58:55 -0300 Subject: [PATCH 004/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index d88b625b383..a6d15a28f30 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -51,7 +51,7 @@ export default { data() { return { localSortBy: this.sortBy || '', - localSortDesc: this.sortDesc || false, + localSortDesc: this.sortDesc || false } }, computed: { @@ -95,7 +95,7 @@ export default { }, sortingTableAttrs() { return {} - }, + } }, watch: { isSortable(newVal, oldVal) { @@ -201,7 +201,6 @@ export default { /* istanbul ignore next */ ariaLabel = startCase(key) } - const sortable = field.sortable const ariaLabelSorting = sortable ? this.localSortDesc && this.localSortBy === key ? this.labelSortAsc From d86687301c00f6e0eceeb6d030cec00857b5ede2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:02:20 -0300 Subject: [PATCH 005/159] Create mixin-filter.js --- src/components/table/helpers/mixin-filter.js | 184 +++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/components/table/helpers/mixin-filter.js diff --git a/src/components/table/helpers/mixin-filter.js b/src/components/table/helpers/mixin-filter.js new file mode 100644 index 00000000000..5cf3bed8b1c --- /dev/null +++ b/src/components/table/helpers/mixin-filter.js @@ -0,0 +1,184 @@ +import stringifyRecordValues from './stringify-record-values' +import looseEqual from '../../../utils/loose-equal' +import warn from '../../../utils/warn' + +export default: { + props: { + filter: { + // Pasing a function to filter is deprecated and should be avoided + type: [String, RegExp, Object, Array, Function], + default: null + }, + filterFunction: { + type: Function, + default: null + }, + }, + data() { + return { + // Flag for displaying which empty slot to show, and for some event triggering. + isFiltered: false + } + }, + computed: { + localFiltering() { + return this.hasProvider ? !!this.noProviderFiltering : true + }, + filteredCheck() { + // For watching changes to filteredItems vs localItems + return { + filteredItems: this.filteredItems, + localItems: this.localItems, + localFilter: this.localFilter + } + }, + localFilter() { + // Returns a sanitized/normalized version of filter prop + if (typeof this.filter === 'function') { + // this.localFilterFn will contain the correct function ref. + // Deprecate setting prop filter to a function + /* istanbul ignore next */ + return '' + } else if ( + typeof this.filterFunction !== 'function' && + !(typeof this.filter === 'string' || this.filter instanceof RegExp) + ) { + // Using internal filter function, which only accepts string or regexp at the moment + return '' + } else { + // Could be a string, object or array, as needed by external filter function + return this.filter + } + }, + localFilterFn() { + let filter = this.filter + let filterFn = this.filterFunction + // Sanitized/normalize filter-function prop + if (typeof filterFn === 'function') { + return filterFn + } else if (typeof filter === 'function') { + // Deprecate setting prop filter to a function + /* istanbul ignore next */ + warn('b-table: Supplying a function to prop "filter" is deprecated. Use "filterFn" instead.') + /* istanbul ignore next */ + return filter + } else { + // no filterFunction, so signal to use internal filter function + return null + } + }, + filteredItems() { + // Returns the records in localItems that match the filter criteria. + // Returns the original localItems array if not sorting + let items = this.localItems || [] + const criteria = this.localFilter + const filterFn = + this.filterFnFactory(this.localFilterFn, criteria) || this.defaultFilterFnFactory(criteria) + + // We only do local filtering if requested, and if the are records to filter and + // if a filter criteria was specified + if (this.localFiltering && filterFn && items.length > 0) { + items = items.filter(filterFn) + } + return items + }, + }, + watch: { + // Watch for changes to the filter criteria and filtered items vs localItems). + // And set visual state and emit events as required + filteredCheck({ filteredItems, localItems, localFilter }) { + // Determine if the dataset is filtered or not + let isFiltered + if (!localFilter) { + // If filter criteria is falsey + isFiltered = false + } else if (looseEqual(localFilter, []) || looseEqual(localFilter, {})) { + // If filter criteria is an empty array or object + isFiltered = false + } else if (localFilter) { + // if Filter criteria is truthy + isFiltered = true + } else { + isFiltered = false + } + if (isFiltered) { + this.$emit('filtered', filteredItems, filteredItems.length) + } + this.isFiltered = isFiltered + }, + isFiltered(newVal, oldVal) { + if (newVal === false && oldVal === true) { + // We need to emit a filtered event if isFiltered transitions from true to + // false so that users can update their pagination controls. + this.$emit('filtered', this.localItems, this.localItems.length) + } + } + }, + methods() { + // Filter Function factories + filterFnFactory(filterFn, criteria) { + // Wrapper factory for external filter functions. + // Wrap the provided filter-function and return a new function. + // Returns null if no filter-function defined or if criteria is falsey. + // Rather than directly grabbing this.computedLocalFilterFn or this.filterFunction + // we have it passed, so that the caller computed prop will be reactive to changes + // in the original filter-function (as this routine is a method) + if (!filterFn || !criteria || typeof filterFn !== 'function') { + return null + } + + // Build the wrapped filter test function, passing the criteria to the provided function + const fn = item => { + // Generated function returns true if the criteria matches part + // of the serialized data, otherwise false + return filterFn(item, criteria) + } + + // Return the wrapped function + return fn + }, + defaultFilterFnFactory(criteria) { + // Generates the default filter function, using the given filter criteria + if (!criteria || !(typeof criteria === 'string' || criteria instanceof RegExp)) { + // Built in filter can only support strings or RegExp criteria (at the moment) + return null + } + + // Build the regexp needed for filtering + let regexp = criteria + if (typeof regexp === 'string') { + // Escape special RegExp characters in the string and convert contiguous + // whitespace to \s+ matches + const pattern = criteria + .replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + .replace(/[\s\uFEFF\xA0]+/g, '\\s+') + // Build the RegExp (no need for global flag, as we only need + // to find the value once in the string) + regexp = new RegExp(`.*${pattern}.*`, 'i') + } + + // Generate the wrapped filter test function to use + const fn = item => { + // This searches all row values (and sub property values) in the entire (excluding + // special _ prefixed keys), because we convert the record to a space-separated + // string containing all the value properties (recursively), even ones that are + // not visible (not specified in this.fields). + // + // TODO: Enable searching on formatted fields and scoped slots + // TODO: Should we filter only on visible fields (i.e. ones in this.fields) by default? + // TODO: Allow for searching on specific fields/key, this could be combined with the previous TODO + // TODO: Give stringifyRecordValues extra options for filtering (i.e. passing the + // fields definition and a reference to $scopedSlots) + // + // Generated function returns true if the criteria matches part of + // the serialized data, otherwise false + // We set lastIndex = 0 on regex in case someone uses the /g global flag + regexp.lastIndex = 0 + return regexp.test(stringifyRecordValues(item)) + } + + // Return the generated function + return fn + } + } +} From bd50e2867404a1dc879b6456d9eb4606c39bba97 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:04:31 -0300 Subject: [PATCH 006/159] Update mixin-filter.js --- src/components/table/helpers/mixin-filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-filter.js b/src/components/table/helpers/mixin-filter.js index 5cf3bed8b1c..229c909534d 100644 --- a/src/components/table/helpers/mixin-filter.js +++ b/src/components/table/helpers/mixin-filter.js @@ -2,7 +2,7 @@ import stringifyRecordValues from './stringify-record-values' import looseEqual from '../../../utils/loose-equal' import warn from '../../../utils/warn' -export default: { +export default { props: { filter: { // Pasing a function to filter is deprecated and should be avoided From 927424d2af0855b3b53938646b301831185c4a55 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:06:12 -0300 Subject: [PATCH 007/159] Update mixin-filter.js --- src/components/table/helpers/mixin-filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-filter.js b/src/components/table/helpers/mixin-filter.js index 229c909534d..56e21d2c8ac 100644 --- a/src/components/table/helpers/mixin-filter.js +++ b/src/components/table/helpers/mixin-filter.js @@ -114,7 +114,7 @@ export default { } } }, - methods() { + methods: { // Filter Function factories filterFnFactory(filterFn, criteria) { // Wrapper factory for external filter functions. From b578825fac865b5376227f2a0f3ce9bc74099be7 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:11:22 -0300 Subject: [PATCH 008/159] Update mixin-filter.js --- src/components/table/helpers/mixin-filter.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/table/helpers/mixin-filter.js b/src/components/table/helpers/mixin-filter.js index 56e21d2c8ac..3c090ca6687 100644 --- a/src/components/table/helpers/mixin-filter.js +++ b/src/components/table/helpers/mixin-filter.js @@ -12,7 +12,7 @@ export default { filterFunction: { type: Function, default: null - }, + } }, data() { return { @@ -59,7 +59,9 @@ export default { } else if (typeof filter === 'function') { // Deprecate setting prop filter to a function /* istanbul ignore next */ - warn('b-table: Supplying a function to prop "filter" is deprecated. Use "filterFn" instead.') + warn( + 'b-table: Supplying a function to prop "filter" is deprecated. Use "filterFn" instead.' + ) /* istanbul ignore next */ return filter } else { @@ -81,7 +83,7 @@ export default { items = items.filter(filterFn) } return items - }, + } }, watch: { // Watch for changes to the filter criteria and filtered items vs localItems). From 2175d0d5f4648924f7cd35d85af44db43ce11b5e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:18:58 -0300 Subject: [PATCH 009/159] Create mixins-items.js --- src/components/table/helpers/mixins-items.js | 59 ++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/components/table/helpers/mixins-items.js diff --git a/src/components/table/helpers/mixins-items.js b/src/components/table/helpers/mixins-items.js new file mode 100644 index 00000000000..e0ba05b731b --- /dev/null +++ b/src/components/table/helpers/mixins-items.js @@ -0,0 +1,59 @@ +import normalizeFields from './normalize-fields' +import { isArray } from '../../../utils/array' + +export default { + props: { + items: { + type: [Array, Function], + default() /* istanbul ignore next */ { + return [] + } + }, + fields: { + // Object format is deprecated and should be avoided + type: [Array, Object], + default: null + }, + primaryKey: { + // Primary key for record. + // If provided the value in each row must be unique!!! + type: String, + default: null + } + }, + data() { + return { + // Our local copy of the items. Must be an array + localItems: isArray(this.items) ? this.items.slice() : [], + } + }, + computed: { + computedFields() { + // We normalize fields into an array of objects + // [ { key:..., label:..., ...}, {...}, ..., {..}] + return normalizeFields(this.fields, this.localItems) + }, + computedFieldsObj() { + // Fields as a simple lookup hash object + // Mainly for scopedSlots for convenience + return this.computedFields.reduce((f, obj) => { + obj[f.key] = f + return obj + }, {}) + } + }, + watch: { + items(newItems) { + // TODO: Move provider check into provider mixin + if (this.hasProvider || newItems instanceof Function) { + this.$nextTick(this._providerUpdate) + } else if (isArray(newItems)) { + // Set localItems/filteredItems to a copy of the provided array + this.localItems = newItems.slice() + } else if (newItems === null || newItems === undefined) { + /* istanbul ignore next */ + this.localItems = [] + } + } + } +} From 0352116e3a0343414d630ec68509466a86c2cf09 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:20:36 -0300 Subject: [PATCH 010/159] Update and rename mixins-items.js to mixin-items.js --- .../table/helpers/{mixins-items.js => mixin-items.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/components/table/helpers/{mixins-items.js => mixin-items.js} (96%) diff --git a/src/components/table/helpers/mixins-items.js b/src/components/table/helpers/mixin-items.js similarity index 96% rename from src/components/table/helpers/mixins-items.js rename to src/components/table/helpers/mixin-items.js index e0ba05b731b..9e2c3b2bc50 100644 --- a/src/components/table/helpers/mixins-items.js +++ b/src/components/table/helpers/mixin-items.js @@ -24,7 +24,7 @@ export default { data() { return { // Our local copy of the items. Must be an array - localItems: isArray(this.items) ? this.items.slice() : [], + localItems: isArray(this.items) ? this.items.slice() : [] } }, computed: { From 222a386eab25f6129ab2458e6e11f6be4a8d6292 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:24:23 -0300 Subject: [PATCH 011/159] Rename mixin-filter.js to mixin-filtering.js --- .../table/helpers/{mixin-filter.js => mixin-filtering.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/table/helpers/{mixin-filter.js => mixin-filtering.js} (100%) diff --git a/src/components/table/helpers/mixin-filter.js b/src/components/table/helpers/mixin-filtering.js similarity index 100% rename from src/components/table/helpers/mixin-filter.js rename to src/components/table/helpers/mixin-filtering.js From 262fd4c21735f7eb1e71c36a9e0d2a51d3136d24 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:28:32 -0300 Subject: [PATCH 012/159] Update table.js --- src/components/table/table.js | 194 ++-------------------------------- 1 file changed, 6 insertions(+), 188 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 1678a43955e..7b3481f5935 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -13,6 +13,8 @@ import idMixin from '../../mixins/id' import normalizeSlotMixin from '../../mixins/normalize-slot' // Table helper mixins +import filteringMixin from './helpers/mixin-filtering' +import paginationMixin from './helpers/mixin-pagination' import captionMixin from './helpers/mixin-caption' import colgroupMixin from './helpers/mixin-colgroup' import theadMixin from './helpers/mixin-thead' @@ -26,9 +28,13 @@ import providerMixin from './helpers/mixin-provider' // @vue/component export default { name: 'BTable', + // Order of mixins is important. + // They are merged from left to fight, followed by this component. mixins: [ idMixin, normalizeSlotMixin, + filteringMixin, + paginationMixin, busyMixin, captionMixin, colgroupMixin, @@ -126,22 +132,6 @@ export default { type: String, default: 'Click to sort Descending' }, - perPage: { - type: [Number, String], - default: 0 - }, - currentPage: { - type: [Number, String], - default: 1 - }, - filter: { - type: [String, RegExp, Object, Array, Function], - default: null - }, - filterFunction: { - type: Function, - default: null - }, noLocalSorting: { type: Boolean, default: false @@ -165,8 +155,6 @@ export default { localSortDesc: this.sortDesc || false, // Our local copy of the items. Must be an array localItems: isArray(this.items) ? this.items.slice() : [], - // Flag for displaying which empty slot to show, and for some event triggering. - isFiltered: false } }, computed: { @@ -226,15 +214,9 @@ export default { } }, // Items related computed props - localFiltering() { - return this.hasProvider ? !!this.noProviderFiltering : true - }, localSorting() { return this.hasProvider ? !!this.noProviderSorting : !this.noLocalSorting }, - localPaging() { - return this.hasProvider ? !!this.noProviderPaging : true - }, context() { // Current state of sorting, filtering and pagination props/values return { @@ -251,62 +233,6 @@ export default { // [ { key:..., label:..., ...}, {...}, ..., {..}] return normalizeFields(this.fields, this.localItems) }, - filteredCheck() { - // For watching changes to filteredItems vs localItems - return { - filteredItems: this.filteredItems, - localItems: this.localItems, - localFilter: this.localFilter - } - }, - localFilter() { - // Returns a sanitized/normalized version of filter prop - if (typeof this.filter === 'function') { - // this.localFilterFn will contain the correct function ref. - // Deprecate setting prop filter to a function - /* istanbul ignore next */ - return '' - } else if ( - typeof this.filterFunction !== 'function' && - !(typeof this.filter === 'string' || this.filter instanceof RegExp) - ) { - // Using internal filter function, which only accepts string or regexp at the moment - return '' - } else { - // Could be a string, object or array, as needed by external filter function - return this.filter - } - }, - localFilterFn() { - let filter = this.filter - let filterFn = this.filterFunction - // Sanitized/normalize filter-function prop - if (typeof filterFn === 'function') { - return filterFn - } else if (typeof filter === 'function') { - // Deprecate setting prop filter to a function - /* istanbul ignore next */ - return filter - } else { - // no filterFunction, so signal to use internal filter function - return null - } - }, - filteredItems() { - // Returns the records in localItems that match the filter criteria. - // Returns the original localItems array if not sorting - let items = this.localItems || [] - const criteria = this.localFilter - const filterFn = - this.filterFnFactory(this.localFilterFn, criteria) || this.defaultFilterFnFactory(criteria) - - // We only do local filtering if requested, and if the are records to filter and - // if a filter criteria was specified - if (this.localFiltering && filterFn && items.length > 0) { - items = items.filter(filterFn) - } - return items - }, sortedItems() { // Sorts the filtered items and returns a new array of the sorted items // or the original items array if not sorted. @@ -334,18 +260,6 @@ export default { } return items }, - paginatedItems() { - let items = this.sortedItems || [] - const currentPage = Math.max(parseInt(this.currentPage, 10) || 1, 1) - const perPage = Math.max(parseInt(this.perPage, 10) || 0, 0) - // Apply local pagination - if (this.localPaging && !!perPage) { - // Grab the current page of data (which may be past filtered items limit) - items = items.slice((currentPage - 1) * perPage, currentPage * perPage) - } - // Return the items to display in the table - return items - }, computedItems() { return this.paginatedItems || [] } @@ -393,35 +307,6 @@ export default { computedItems(newVal, oldVal) { this.$emit('input', newVal) }, - // Watch for changes to the filter criteria and filtered items vs localItems). - // And set visual state and emit events as required - filteredCheck({ filteredItems, localItems, localFilter }) { - // Determine if the dataset is filtered or not - let isFiltered - if (!localFilter) { - // If filter criteria is falsey - isFiltered = false - } else if (looseEqual(localFilter, []) || looseEqual(localFilter, {})) { - // If filter criteria is an empty array or object - isFiltered = false - } else if (localFilter) { - // if Filter criteria is truthy - isFiltered = true - } else { - isFiltered = false - } - if (isFiltered) { - this.$emit('filtered', filteredItems, filteredItems.length) - } - this.isFiltered = isFiltered - }, - isFiltered(newVal, oldVal) { - if (newVal === false && oldVal === true) { - // We need to emit a filtered event if isFiltered transitions from true to - // false so that users can update their pagination controls. - this.$emit('filtered', this.localItems, this.localItems.length) - } - }, context(newVal, oldVal) { // Emit context info for external paging/filtering/sorting handling if (!looseEqual(newVal, oldVal)) { @@ -433,73 +318,6 @@ export default { // Initially update the v-model of displayed items this.$emit('input', this.computedItems) }, - methods: { - // Filter Function factories - filterFnFactory(filterFn, criteria) { - // Wrapper factory for external filter functions. - // Wrap the provided filter-function and return a new function. - // returns null if no filter-function defined or if criteria is falsey. - // Rather than directly grabbing this.computedLocalFilterFn or this.filterFunction - // We have it passed, so that the caller computed prop will be reactive to changes - // in the original filter-function (as this routine is a method) - if (!filterFn || !criteria || typeof filterFn !== 'function') { - return null - } - - // Build the wrapped filter test function, passing the criteria to the provided function - const fn = item => { - // Generated function returns true if the criteria matches part - // of the serialized data, otherwise false - return filterFn(item, criteria) - } - - // Return the wrapped function - return fn - }, - defaultFilterFnFactory(criteria) { - // Generates the default filter function, using the given filter criteria - if (!criteria || !(typeof criteria === 'string' || criteria instanceof RegExp)) { - // Built in filter can only support strings or RegExp criteria (at the moment) - return null - } - - // Build the regexp needed for filtering - let regexp = criteria - if (typeof regexp === 'string') { - // Escape special RegExp characters in the string and convert contiguous - // whitespace to \s+ matches - const pattern = criteria - .replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') - .replace(/[\s\uFEFF\xA0]+/g, '\\s+') - // Build the RegExp (no need for global flag, as we only need - // to find the value once in the string) - regexp = new RegExp(`.*${pattern}.*`, 'i') - } - - // Generate the wrapped filter test function to use - const fn = item => { - // This searches all row values (and sub property values) in the entire (excluding - // special _ prefixed keys), because we convert the record to a space-separated - // string containing all the value properties (recursively), even ones that are - // not visible (not specified in this.fields). - // - // TODO: Enable searching on formatted fields and scoped slots - // TODO: Should we filter only on visible fields (i.e. ones in this.fields) by default? - // TODO: Allow for searching on specific fields/key, this could be combined with the previous TODO - // TODO: Give stringifyRecordValues extra options for filtering (i.e. passing the - // fields definition and a reference to $scopedSlots) - // - // Generated function returns true if the criteria matches part of - // the serialized data, otherwise false - // We set lastIndex = 0 on regex in case someone uses the /g global flag - regexp.lastIndex = 0 - return regexp.test(stringifyRecordValues(item)) - } - - // Return the generated function - return fn - } - }, render(h) { // Build the caption (from caption mixin) const $caption = this.renderCaption() From c12bced5114cdd2a128ee59caf09dfe8bcba2533 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:30:32 -0300 Subject: [PATCH 013/159] Update table.js --- src/components/table/table.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 7b3481f5935..4f6ea65b68b 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -5,7 +5,6 @@ import { arrayIncludes, isArray } from '../../utils/array' // Table helper functions import normalizeFields from './helpers/normalize-fields' -import stringifyRecordValues from './helpers/stringify-record-values' import defaultSortCompare from './helpers/default-sort-compare' // Mixins @@ -154,7 +153,7 @@ export default { localSortBy: this.sortBy || '', localSortDesc: this.sortDesc || false, // Our local copy of the items. Must be an array - localItems: isArray(this.items) ? this.items.slice() : [], + localItems: isArray(this.items) ? this.items.slice() : [] } }, computed: { From b3ea883482910e514938ff1512b093bbaddb5f4d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:40:12 -0300 Subject: [PATCH 014/159] Update mixin-items.js --- src/components/table/helpers/mixin-items.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/table/helpers/mixin-items.js b/src/components/table/helpers/mixin-items.js index 9e2c3b2bc50..90657b6245f 100644 --- a/src/components/table/helpers/mixin-items.js +++ b/src/components/table/helpers/mixin-items.js @@ -44,10 +44,7 @@ export default { }, watch: { items(newItems) { - // TODO: Move provider check into provider mixin - if (this.hasProvider || newItems instanceof Function) { - this.$nextTick(this._providerUpdate) - } else if (isArray(newItems)) { + if (isArray(newItems)) { // Set localItems/filteredItems to a copy of the provided array this.localItems = newItems.slice() } else if (newItems === null || newItems === undefined) { From 4557f38ba525a957737d15c9a6ceebe449801cee Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:43:08 -0300 Subject: [PATCH 015/159] Update mixin-provider.js --- src/components/table/helpers/mixin-provider.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/table/helpers/mixin-provider.js b/src/components/table/helpers/mixin-provider.js index ce564d10a9a..8df30811883 100644 --- a/src/components/table/helpers/mixin-provider.js +++ b/src/components/table/helpers/mixin-provider.js @@ -54,6 +54,12 @@ export default { }, watch: { // Provider update triggering + items(newVal, oldVal) { + // If a new provider has been specified, trigger an update + if (this.hasProvider || newVal instanceof Function) { + this.$nextTick(this._providerUpdate) + } + }, providerTriggerContext(newVal, oldVal) { // Trigger the provider to update as the relevant context values have changed. if (!looseEqual(newVal, oldVal)) { From 43d7a17c693ff239d3607be5b22dac6efcaa5c4d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:44:21 -0300 Subject: [PATCH 016/159] Update table.js --- src/components/table/table.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 4f6ea65b68b..67789863b2a 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -266,12 +266,10 @@ export default { watch: { // Watch props for changes and update local values items(newItems) { - if (this.hasProvider || newItems instanceof Function) { - this.$nextTick(this._providerUpdate) - } else if (isArray(newItems)) { + if (isArray(newItems)) { // Set localItems/filteredItems to a copy of the provided array this.localItems = newItems.slice() - } else { + } else if (newItems === null || newItems === undefined) { /* istanbul ignore next */ this.localItems = [] } From 0f3ed4d6741abd9ed1f4e09cbd4b7774289914c4 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:55:23 -0300 Subject: [PATCH 017/159] Create table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/components/table/table-filtering.spec.js diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js new file mode 100644 index 00000000000..659b632209e --- /dev/null +++ b/src/components/table/table-filtering.spec.js @@ -0,0 +1,68 @@ +import Table from './table' +import defaultSortCompare from './helpers/default-sort-compare' +import { mount } from '@vue/test-utils' + +const testItems = [{ a: 3, b: 'b', c: 'x' }, { a: 1, b: 'c', c: 'y' }, { a: 2, b: 'a', c: 'z' }] +const testFields = [ + { key: 'a', label: 'A', sortable: true }, + { key: 'b', label: 'B', sortable: true }, + { key: 'c', label: 'C', sortable: false } +] + +describe('table > filtering', () => { + it('should not be filtered by default', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('input').length).toBe(1) + expect(wrapper.emitted('input')[0][0]).toEqual(testItems) + const $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + const columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + wrapper.destroy() + }) + + it('should be filtered when filter is a string', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + filter: 'z' + } + }) + expect(wrapper).toBeDefined() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + let $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(1) + + const $tds = $rows.at(0).findAll('td') + + expect($tds.at(0).text()).toBe('2') + expect($tds.at(1).text()).toBe('a') + expect($tds.at(1).text()).toBe('z') + + wrapper.destroy() + }) +}) From fb59b71a9572f3e6493a3ce1c23f5c877e0a0700 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 17:59:36 -0300 Subject: [PATCH 018/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 659b632209e..6a0df17e5ad 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -1,13 +1,8 @@ import Table from './table' -import defaultSortCompare from './helpers/default-sort-compare' import { mount } from '@vue/test-utils' const testItems = [{ a: 3, b: 'b', c: 'x' }, { a: 1, b: 'c', c: 'y' }, { a: 2, b: 'a', c: 'z' }] -const testFields = [ - { key: 'a', label: 'A', sortable: true }, - { key: 'b', label: 'B', sortable: true }, - { key: 'c', label: 'C', sortable: false } -] +const testFields = ['a', 'b', 'c'] describe('table > filtering', () => { it('should not be filtered by default', async () => { @@ -54,7 +49,7 @@ describe('table > filtering', () => { expect(wrapper.findAll('tbody > tr').exists()).toBe(true) expect(wrapper.findAll('tbody > tr').length).toBe(1) - let $rows = wrapper.findAll('tbody > tr').wrappers + let $rows = wrapper.findAll('tbody > tr') expect($rows.length).toBe(1) const $tds = $rows.at(0).findAll('td') From c69d50e7bcce2cab54186f644d0feeb2274fd2e6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:01:28 -0300 Subject: [PATCH 019/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 6a0df17e5ad..d5e2b7e85ac 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -56,7 +56,7 @@ describe('table > filtering', () => { expect($tds.at(0).text()).toBe('2') expect($tds.at(1).text()).toBe('a') - expect($tds.at(1).text()).toBe('z') + expect($tds.at(2).text()).toBe('z') wrapper.destroy() }) From 6d14f1bb870b8b4d8530e57e568edce17b0adb74 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:11:10 -0300 Subject: [PATCH 020/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index d5e2b7e85ac..b4f4e69c7a1 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -60,4 +60,51 @@ describe('table > filtering', () => { wrapper.destroy() }) + + it('should emit filtered event when filter string is changed', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + filter: '' + } + }) + expect(wrapper).toBeDefined() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + expect(wrapper.emitted('filtered')).not.toBeDefined() + + wrapper.setProps({ + filter: 'z' + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + expect(wrapper.emitted('filtered')).toBeDefined() + expect(wrapper.emitted('filtered').length).toBe(1) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[0][0]).toEqual([testItems[2]]) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[0][1]).toEqual(1) + + wrapper.setProps({ + filter: '' + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + + expect(wrapper.emitted('filtered').length).toBe(2) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[0][0]).toEqual(testItems) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[0][1]).toEqual(3) + + wrapper.destroy() + }) }) From eadf5e60c510648d9cdfc0635157c8d4d56d087b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:16:54 -0300 Subject: [PATCH 021/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index b4f4e69c7a1..bfdfa7814fb 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -101,9 +101,9 @@ describe('table > filtering', () => { expect(wrapper.emitted('filtered').length).toBe(2) // Copy of items matching filter - expect(wrapper.emitted('filtered')[0][0]).toEqual(testItems) + expect(wrapper.emitted('filtered')[1][0]).toEqual(testItems) // Number of rows matching filter - expect(wrapper.emitted('filtered')[0][1]).toEqual(3) + expect(wrapper.emitted('filtered')[1][1]).toEqual(3) wrapper.destroy() }) From f7a83f114266ed047f997a26326906cac4710721 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:28:57 -0300 Subject: [PATCH 022/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index bfdfa7814fb..3074deb898d 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -105,6 +105,35 @@ describe('table > filtering', () => { // Number of rows matching filter expect(wrapper.emitted('filtered')[1][1]).toEqual(3) + wrapper.setProps({ + filter: 3 + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + expect(wrapper.emitted('filtered').length).toBe(3) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[2][0]).toEqual([testItems[0]]) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[2][1]).toEqual(1) + + wrapper.setProps({ + // Setting to an empty array will also clear the filter + filter: [] + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + + expect(wrapper.emitted('filtered').length).toBe(4) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[3][0]).toEqual(testItems) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[3][1]).toEqual(3) + wrapper.destroy() }) }) From 5219fc5e908f0ebc70a44178b4c7a8c341f1f50b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:37:25 -0300 Subject: [PATCH 023/159] Update mixin-filtering.js --- src/components/table/helpers/mixin-filtering.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index 3c090ca6687..ad25bc643d2 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -141,17 +141,18 @@ export default { }, defaultFilterFnFactory(criteria) { // Generates the default filter function, using the given filter criteria - if (!criteria || !(typeof criteria === 'string' || criteria instanceof RegExp)) { - // Built in filter can only support strings or RegExp criteria (at the moment) + const type = typeof criteria + if (!criteria || !(type === 'string' || type === 'number' || criteria instanceof RegExp)) { + // Built in filter can only support strings/numbers or RegExp criteria (at the moment) return null } // Build the regexp needed for filtering let regexp = criteria - if (typeof regexp === 'string') { + if (typeof regexp === 'string' || typeof regexp === 'number') { // Escape special RegExp characters in the string and convert contiguous // whitespace to \s+ matches - const pattern = criteria + const pattern = String(criteria) .replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') .replace(/[\s\uFEFF\xA0]+/g, '\\s+') // Build the RegExp (no need for global flag, as we only need From 759da3226412761980e014a37f5162f46bbb2667 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:44:22 -0300 Subject: [PATCH 024/159] Update mixin-filtering.js --- src/components/table/helpers/mixin-filtering.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index ad25bc643d2..3c090ca6687 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -141,18 +141,17 @@ export default { }, defaultFilterFnFactory(criteria) { // Generates the default filter function, using the given filter criteria - const type = typeof criteria - if (!criteria || !(type === 'string' || type === 'number' || criteria instanceof RegExp)) { - // Built in filter can only support strings/numbers or RegExp criteria (at the moment) + if (!criteria || !(typeof criteria === 'string' || criteria instanceof RegExp)) { + // Built in filter can only support strings or RegExp criteria (at the moment) return null } // Build the regexp needed for filtering let regexp = criteria - if (typeof regexp === 'string' || typeof regexp === 'number') { + if (typeof regexp === 'string') { // Escape special RegExp characters in the string and convert contiguous // whitespace to \s+ matches - const pattern = String(criteria) + const pattern = criteria .replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') .replace(/[\s\uFEFF\xA0]+/g, '\\s+') // Build the RegExp (no need for global flag, as we only need From ac7604268cab7ca59bba6c30242f866b5f36f2a0 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:45:17 -0300 Subject: [PATCH 025/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 3074deb898d..475a9c5d7b8 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -106,7 +106,7 @@ describe('table > filtering', () => { expect(wrapper.emitted('filtered')[1][1]).toEqual(3) wrapper.setProps({ - filter: 3 + filter: '3' }) await wrapper.vm.$nextTick() From 7a7ef98522a6753a456ee26f3fd80101932b565e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:45:42 -0300 Subject: [PATCH 026/159] Update table-bottom-row.spec.js --- src/components/table/table-bottom-row.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-bottom-row.spec.js b/src/components/table/table-bottom-row.spec.js index df91e2d256c..6e2c04e2360 100644 --- a/src/components/table/table-bottom-row.spec.js +++ b/src/components/table/table-bottom-row.spec.js @@ -5,7 +5,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table bottom-row slot', () => { +describe('table > bottom-row slot', () => { it('should not have bottom row by default', async () => { const wrapper = mount(Table, { propsData: { From a957261118b9114b787c1b6ca6543e2660a2d945 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 18:46:04 -0300 Subject: [PATCH 027/159] Update table-busy.spec.js --- src/components/table/table-busy.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-busy.spec.js b/src/components/table/table-busy.spec.js index 59c34ad48c3..1e3153f1ef1 100644 --- a/src/components/table/table-busy.spec.js +++ b/src/components/table/table-busy.spec.js @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] -describe('b-table busy state', () => { +describe('b-table > busy state', () => { it('default should have attribute aria-busy=false', async () => { const wrapper = mount(Table, { propsData: { From ec6b9d8aeed978b9b1e10f8f257fc245d4ced5c1 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:18:54 -0300 Subject: [PATCH 028/159] Update mixin-filtering.js --- src/components/table/helpers/mixin-filtering.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index 3c090ca6687..f52b621c544 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -125,7 +125,13 @@ export default { // Rather than directly grabbing this.computedLocalFilterFn or this.filterFunction // we have it passed, so that the caller computed prop will be reactive to changes // in the original filter-function (as this routine is a method) - if (!filterFn || !criteria || typeof filterFn !== 'function') { + if ( + !filterFn || + typeof filterFn !== 'function' || + !criteria || + looseEqual(criteria, []) || + looseEqual(criteria, {}) + ) { return null } From 6749fa174e608026110daaa3faa3fd823357f628 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:23:43 -0300 Subject: [PATCH 029/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 55 +++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 475a9c5d7b8..f03ee742edb 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -120,8 +120,8 @@ describe('table > filtering', () => { expect(wrapper.emitted('filtered')[2][1]).toEqual(1) wrapper.setProps({ - // Setting to an empty array will also clear the filter - filter: [] + // Setting to null will also clear the filter + filter: null }) await wrapper.vm.$nextTick() @@ -136,4 +136,55 @@ describe('table > filtering', () => { wrapper.destroy() }) + + it('should work with filter function', async () => { + const filterFn = (item, crteria) => { + return regexp.test(stringifyRecordValues(item)) + } + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + filter: '', + filterFunction: filterFn + } + }) + expect(wrapper).toBeDefined() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + expect(wrapper.emitted('filtered')).not.toBeDefined() + + wrapper.setProps({ + filter: /z/ + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + expect(wrapper.emitted('filtered')).toBeDefined() + expect(wrapper.emitted('filtered').length).toBe(1) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[0][0]).toEqual([testItems[2]]) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[0][1]).toEqual(1) + + wrapper.setProps({ + filter: [] + }) + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + + expect(wrapper.emitted('filtered').length).toBe(2) + // Copy of items matching filter + expect(wrapper.emitted('filtered')[1][0]).toEqual(testItems) + // Number of rows matching filter + expect(wrapper.emitted('filtered')[1][1]).toEqual(3) + + wrapper.destroy() + }) }) From 220baade094b7337b4e577a5e914602907b2fc2e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:32:24 -0300 Subject: [PATCH 030/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index f03ee742edb..904ad49b87d 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -1,4 +1,5 @@ import Table from './table' +import stringifyRecordValues from './helpers/stringify-record-values' import { mount } from '@vue/test-utils' const testItems = [{ a: 3, b: 'b', c: 'x' }, { a: 1, b: 'c', c: 'y' }, { a: 2, b: 'a', c: 'z' }] @@ -138,7 +139,8 @@ describe('table > filtering', () => { }) it('should work with filter function', async () => { - const filterFn = (item, crteria) => { + const filterFn = (item, regexp) => { + // We are passing a regexp for this test return regexp.test(stringifyRecordValues(item)) } const wrapper = mount(Table, { From c80a1112daa3050ddf05a4522d125f3cc07eebbf Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:38:28 -0300 Subject: [PATCH 031/159] Update mixin-filtering.js --- src/components/table/helpers/mixin-filtering.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index f52b621c544..5298c6217d9 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -101,6 +101,7 @@ export default { // if Filter criteria is truthy isFiltered = true } else { + /* istanbul ignore next: rare chance of reaching this else */ isFiltered = false } if (isFiltered) { From 1f6f28c7f4c9e0c59eb6125ce1eb6aaef0e0812b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:53:33 -0300 Subject: [PATCH 032/159] Update table.js --- src/components/table/table.js | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 67789863b2a..76efa3e3960 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -12,6 +12,7 @@ import idMixin from '../../mixins/id' import normalizeSlotMixin from '../../mixins/normalize-slot' // Table helper mixins +import itemsMixin from './helpers/mixin-items' import filteringMixin from './helpers/mixin-filtering' import paginationMixin from './helpers/mixin-pagination' import captionMixin from './helpers/mixin-caption' @@ -32,6 +33,7 @@ export default { mixins: [ idMixin, normalizeSlotMixin, + itemsMixin, filteringMixin, paginationMixin, busyMixin, @@ -46,22 +48,6 @@ export default { // Don't place ATTRS on root element automatically, as table could be wrapped in responsive div inheritAttrs: false, props: { - items: { - type: [Array, Function], - default() /* istanbul ignore next */ { - return [] - } - }, - fields: { - type: [Object, Array], - default: null - }, - primaryKey: { - // Primary key for record. - // If provided the value in each row must be unique!!! - type: String, - default: null - }, striped: { type: Boolean, default: false @@ -152,8 +138,6 @@ export default { // Mixins will also add to data localSortBy: this.sortBy || '', localSortDesc: this.sortDesc || false, - // Our local copy of the items. Must be an array - localItems: isArray(this.items) ? this.items.slice() : [] } }, computed: { @@ -227,11 +211,6 @@ export default { apiUrl: this.apiUrl } }, - computedFields() { - // We normalize fields into an array of objects - // [ { key:..., label:..., ...}, {...}, ..., {..}] - return normalizeFields(this.fields, this.localItems) - }, sortedItems() { // Sorts the filtered items and returns a new array of the sorted items // or the original items array if not sorted. @@ -265,15 +244,6 @@ export default { }, watch: { // Watch props for changes and update local values - items(newItems) { - if (isArray(newItems)) { - // Set localItems/filteredItems to a copy of the provided array - this.localItems = newItems.slice() - } else if (newItems === null || newItems === undefined) { - /* istanbul ignore next */ - this.localItems = [] - } - }, sortDesc(newVal, oldVal) { if (newVal === this.localSortDesc) { /* istanbul ignore next */ From abd15a281c27f0c32a686596384fd358121ac94f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:55:21 -0300 Subject: [PATCH 033/159] Update table.js --- src/components/table/table.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 76efa3e3960..c801a010b22 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -1,10 +1,9 @@ // Utilities import looseEqual from '../../utils/loose-equal' import stableSort from '../../utils/stable-sort' -import { arrayIncludes, isArray } from '../../utils/array' +import { arrayIncludes } from '../../utils/array' // Table helper functions -import normalizeFields from './helpers/normalize-fields' import defaultSortCompare from './helpers/default-sort-compare' // Mixins @@ -137,7 +136,7 @@ export default { return { // Mixins will also add to data localSortBy: this.sortBy || '', - localSortDesc: this.sortDesc || false, + localSortDesc: this.sortDesc || false } }, computed: { From 82e685d9fac8c887663b8f2344cd94881e6d2f42 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 19:59:20 -0300 Subject: [PATCH 034/159] Update mixin-items.js --- src/components/table/helpers/mixin-items.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-items.js b/src/components/table/helpers/mixin-items.js index 90657b6245f..332c0941fb0 100644 --- a/src/components/table/helpers/mixin-items.js +++ b/src/components/table/helpers/mixin-items.js @@ -33,7 +33,7 @@ export default { // [ { key:..., label:..., ...}, {...}, ..., {..}] return normalizeFields(this.fields, this.localItems) }, - computedFieldsObj() { + computedFieldsObj() /* istanbul ignore next: not using at the moment */ { // Fields as a simple lookup hash object // Mainly for scopedSlots for convenience return this.computedFields.reduce((f, obj) => { From b4401941480c099c286794fe9f2ea3eba6ee73bd Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 20:22:45 -0300 Subject: [PATCH 035/159] Update table.js --- src/components/table/table.js | 105 ++-------------------------------- 1 file changed, 5 insertions(+), 100 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index c801a010b22..7f28e5e1689 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -13,6 +13,7 @@ import normalizeSlotMixin from '../../mixins/normalize-slot' // Table helper mixins import itemsMixin from './helpers/mixin-items' import filteringMixin from './helpers/mixin-filtering' +import sortingMixin from './helpers/mixin-sorting' import paginationMixin from './helpers/mixin-pagination' import captionMixin from './helpers/mixin-caption' import colgroupMixin from './helpers/mixin-colgroup' @@ -34,6 +35,7 @@ export default { normalizeSlotMixin, itemsMixin, filteringMixin, + sortingMixin, paginationMixin, busyMixin, captionMixin, @@ -87,43 +89,6 @@ export default { type: [Boolean, String], default: false }, - sortBy: { - type: String, - default: null - }, - sortDesc: { - type: Boolean, - default: false - }, - sortDirection: { - type: String, - default: 'asc', - validator: direction => arrayIncludes(['asc', 'desc', 'last'], direction) - }, - sortCompare: { - type: Function, - default: null - }, - noSortReset: { - type: Boolean, - default: false - }, - labelSortAsc: { - type: String, - default: 'Click to sort Ascending' - }, - labelSortDesc: { - type: String, - default: 'Click to sort Descending' - }, - noLocalSorting: { - type: Boolean, - default: false - }, - noFooterSorting: { - type: Boolean, - default: false - }, value: { // v-model for retrieving the current displayed rows type: Array, @@ -134,9 +99,7 @@ export default { }, data() { return { - // Mixins will also add to data - localSortBy: this.sortBy || '', - localSortDesc: this.sortDesc || false + // Mixins add to data } }, computed: { @@ -195,80 +158,22 @@ export default { ...this.selectableTableAttrs } }, - // Items related computed props - localSorting() { - return this.hasProvider ? !!this.noProviderSorting : !this.noLocalSorting - }, context() { // Current state of sorting, filtering and pagination props/values return { filter: this.localFilter, sortBy: this.localSortBy, sortDesc: this.localSortDesc, - perPage: this.perPage, - currentPage: this.currentPage, + perPage: parseInt(this.perPage, 10) || 0, + currentPage: parseInt(this.currentPage, 10) || 1, apiUrl: this.apiUrl } }, - sortedItems() { - // Sorts the filtered items and returns a new array of the sorted items - // or the original items array if not sorted. - let items = this.filteredItems || [] - const sortBy = this.localSortBy - const sortDesc = this.localSortDesc - const sortCompare = this.sortCompare - const localSorting = this.localSorting - if (sortBy && localSorting) { - // stableSort returns a new array, and leaves the original array intact - return stableSort(items, (a, b) => { - let result = null - if (typeof sortCompare === 'function') { - // Call user provided sortCompare routine - result = sortCompare(a, b, sortBy, sortDesc) - } - if (result === null || result === undefined || result === false) { - // Fallback to built-in defaultSortCompare if sortCompare - // is not defined or returns null/false - result = defaultSortCompare(a, b, sortBy) - } - // Negate result if sorting in descending order - return (result || 0) * (sortDesc ? -1 : 1) - }) - } - return items - }, computedItems() { return this.paginatedItems || [] } }, watch: { - // Watch props for changes and update local values - sortDesc(newVal, oldVal) { - if (newVal === this.localSortDesc) { - /* istanbul ignore next */ - return - } - this.localSortDesc = newVal || false - }, - sortBy(newVal, oldVal) { - if (newVal === this.localSortBy) { - /* istanbul ignore next */ - return - } - this.localSortBy = newVal || null - }, - // Update .sync props - localSortDesc(newVal, oldVal) { - // Emit update to sort-desc.sync - if (newVal !== oldVal) { - this.$emit('update:sortDesc', newVal) - } - }, - localSortBy(newVal, oldVal) { - if (newVal !== oldVal) { - this.$emit('update:sortBy', newVal) - } - }, // Watch for changes on computedItems and update the v-model computedItems(newVal, oldVal) { this.$emit('input', newVal) From 9aefd8f786091c3550aa49a99e054070a796d5d8 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 20:28:07 -0300 Subject: [PATCH 036/159] Update table.js --- src/components/table/table.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/table/table.js b/src/components/table/table.js index 7f28e5e1689..e4918054d48 100644 --- a/src/components/table/table.js +++ b/src/components/table/table.js @@ -1,16 +1,11 @@ // Utilities import looseEqual from '../../utils/loose-equal' -import stableSort from '../../utils/stable-sort' -import { arrayIncludes } from '../../utils/array' - -// Table helper functions -import defaultSortCompare from './helpers/default-sort-compare' // Mixins import idMixin from '../../mixins/id' import normalizeSlotMixin from '../../mixins/normalize-slot' -// Table helper mixins +// Table helper Mixins import itemsMixin from './helpers/mixin-items' import filteringMixin from './helpers/mixin-filtering' import sortingMixin from './helpers/mixin-sorting' @@ -98,9 +93,8 @@ export default { } }, data() { - return { - // Mixins add to data - } + // Mixins add to data + return {} }, computed: { // Layout related computed props From 9fdb81378e3c7a2c625a6861527387d8407f611f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 20:31:55 -0300 Subject: [PATCH 037/159] Update mixin-thead.js --- src/components/table/helpers/mixin-thead.js | 30 --------------------- 1 file changed, 30 deletions(-) diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index c00ade5cb0e..0f1b304904b 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -47,37 +47,7 @@ export default { } e.stopPropagation() e.preventDefault() - let sortChanged = false - const toggleLocalSortDesc = () => { - const sortDirection = field.sortDirection || this.sortDirection - if (sortDirection === 'asc') { - this.localSortDesc = false - } else if (sortDirection === 'desc') { - this.localSortDesc = true - } - } - if (!(isFoot && this.noFooterSorting)) { - if (field.sortable) { - if (field.key === this.localSortBy) { - // Change sorting direction on current column - this.localSortDesc = !this.localSortDesc - } else { - // Start sorting this column ascending - this.localSortBy = field.key - toggleLocalSortDesc() - } - sortChanged = true - } else if (this.localSortBy && !this.noSortReset) { - this.localSortBy = null - toggleLocalSortDesc() - sortChanged = true - } - } this.$emit('head-clicked', field.key, field, e, isFoot) - if (sortChanged) { - // Sorting parameters changed - this.$emit('sort-changed', this.context) - } }, renderThead(isFoot = false) { const h = this.$createElement From aa770d17db3a1560ad02eb464ea99d8f30a98962 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 20:58:56 -0300 Subject: [PATCH 038/159] Update mixin-thead.js --- src/components/table/helpers/mixin-thead.js | 65 +++++++++------------ 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index 0f1b304904b..48856c6b1ec 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -33,11 +33,11 @@ export default { field.thClass ? field.thClass : '' ] }, - headClicked(e, field, isFoot) { - if (this.stopIfBusy(e)) { + headClicked(evt, field, isFoot) { + if (this.stopIfBusy(evt)) { // If table is busy (via provider) then don't propagate return - } else if (filterEvent(e)) { + } else if (filterEvent(evt)) { // clicked on a non-disabled control so ignore return } else if (textSelectionActive(this.$el)) { @@ -45,9 +45,9 @@ export default { /* istanbul ignore next: JSDOM doesn't support getSelection() */ return } - e.stopPropagation() - e.preventDefault() - this.$emit('head-clicked', field.key, field, e, isFoot) + evt.stopPropagation() + evt.preventDefault() + this.$emit('head-clicked', field.key, field, evt, isFoot) }, renderThead(isFoot = false) { const h = this.$createElement @@ -61,54 +61,45 @@ export default { // Helper function to generate a field TH cell const makeCell = (field, colIndex) => { - let ariaLabel = '' + let ariaLabel = null if (!field.label.trim() && !field.headerTitle) { // In case field's label and title are empty/blank // We need to add a hint about what the column is about for non-sighted users /* istanbul ignore next */ ariaLabel = startCase(field.key) } - const sortable = field.sortable && !(isFoot && this.noFooterSorting) - const ariaLabelSorting = sortable - ? this.localSortDesc && this.localSortBy === field.key - ? this.labelSortAsc - : this.labelSortDesc - : null - // Assemble the aria-label - ariaLabel = [ariaLabel, ariaLabelSorting].filter(a => a).join(': ') || null - const ariaSort = - sortable && this.localSortBy === field.key - ? this.localSortDesc - ? 'descending' - : 'ascending' - : sortable - ? 'none' - : null + const hasHeadClickListener = this.$listeners['head-clicked'] + const handlers = {} + if (hasHeadClickListener) { + handlers.click = evt => { + this.headClicked(evt, field, isFoot) + } + handlers.keydown = evt => { + const keyCode = evt.keyCode + if (keyCode === KeyCodes.ENTER || keyCode === KeyCodes.SPACE) { + this.headClicked(evt, field, isFoot) + } + } + } const data = { key: field.key, - class: this.fieldClasses(field), + class: [ + this.fieldClasses(field), + this.sortTheadThClasses(field.key, field, isFoot) + ], style: field.thStyle || {}, attrs: { - tabindex: sortable ? '0' : null, + // We only add a tabindex of 0 if htere is a head-clicked listener + tabindex: hasHeadClickListener ? '0' : null, abbr: field.headerAbbr || null, title: field.headerTitle || null, role: 'columnheader', scope: 'col', 'aria-colindex': String(colIndex + 1), 'aria-label': ariaLabel, - 'aria-sort': ariaSort + ...this.sortTheadThAttrs(field.key, field, isFoot) }, - on: { - click: evt => { - this.headClicked(evt, field, isFoot) - }, - keydown: evt => { - const keyCode = evt.keyCode - if (keyCode === KeyCodes.ENTER || keyCode === KeyCodes.SPACE) { - this.headClicked(evt, field, isFoot) - } - } - } + on: handlers } let fieldScope = { label: field.label, column: field.key, field: field } let slot = From efb11db9b7e08e1629199422861abac47ca1d73d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:00:36 -0300 Subject: [PATCH 039/159] Update mixin-thead.js --- src/components/table/helpers/mixin-thead.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index 48856c6b1ec..00589c5aa88 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -83,10 +83,7 @@ export default { } const data = { key: field.key, - class: [ - this.fieldClasses(field), - this.sortTheadThClasses(field.key, field, isFoot) - ], + class: [this.fieldClasses(field), this.sortTheadThClasses(field.key, field, isFoot)], style: field.thStyle || {}, attrs: { // We only add a tabindex of 0 if htere is a head-clicked listener From d40f33c6808004a6e6e2e523718ecb9a7d7ebe82 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:08:23 -0300 Subject: [PATCH 040/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index a6d15a28f30..3b3ce5b0891 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -59,7 +59,7 @@ export default { return this.hasProvider ? !!this.noProviderSorting : !this.noLocalSorting }, isSortable() { - return this.computedFields.some(f => f.sortable !== undefined) + return this.computedFields.some(f => f.sortable) }, sortedItems() { // Sorts the filtered items and returns a new array of the sorted items From c21a22991bfb8d8b69614e449c38ee6385a212b9 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:10:20 -0300 Subject: [PATCH 041/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 3b3ce5b0891..aafa2ae8205 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -87,14 +87,6 @@ export default { }) } return items - }, - // Classes and Attributes to add to the
- // Here for possible future use - sortingTableClasses() { - return {} - }, - sortingTableAttrs() { - return {} } }, watch: { @@ -182,7 +174,7 @@ export default { } }, // methods to compute classes and attrs for thead>th cells - sortTheadThClasses(key, field, idx) { + sortTheadThClasses(key, field, isFoot) { return { // No Classes for sorting currently... // All styles targeted using aria-* attrs From 35a9d334d8d8aad97f86e17161bc300aeab2fc91 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:13:33 -0300 Subject: [PATCH 042/159] Update mixin-thead.js --- src/components/table/helpers/mixin-thead.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index 00589c5aa88..ff03ecbd9e0 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -68,7 +68,7 @@ export default { /* istanbul ignore next */ ariaLabel = startCase(field.key) } - const hasHeadClickListener = this.$listeners['head-clicked'] + const hasHeadClickListener = this.$listeners['head-clicked'] || this.isSortable const handlers = {} if (hasHeadClickListener) { handlers.click = evt => { @@ -86,7 +86,7 @@ export default { class: [this.fieldClasses(field), this.sortTheadThClasses(field.key, field, isFoot)], style: field.thStyle || {}, attrs: { - // We only add a tabindex of 0 if htere is a head-clicked listener + // We only add a tabindex of 0 if there is a head-clicked listener tabindex: hasHeadClickListener ? '0' : null, abbr: field.headerAbbr || null, title: field.headerTitle || null, From 43eb47021bfd09fb701bc16d88c24376a4a898c9 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:21:36 -0300 Subject: [PATCH 043/159] Update table.spec.js --- src/components/table/table.spec.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index ac4cca5f4f7..b1cbfde6f07 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -248,7 +248,8 @@ describe('table', () => { app: { $refs } } = window const vm = $refs.table_paginated - const ariaLabel = vm.labelSortDesc + const ariaLabelDesc = vm.labelSortDesc + const ariaLabelClear = vm.labelSortClear const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') expect(thead).toBeDefined() @@ -256,10 +257,10 @@ describe('table', () => { const tr = [...thead.children].find(el => el && el.tagName === 'TR') expect(tr).toBeDefined() if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabel) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabel) - expect(tr.children[2].getAttribute('aria-label')).toBe(null) - expect(tr.children[3].getAttribute('aria-label')).toBe(null) + expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) + expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) } } }) @@ -269,18 +270,19 @@ describe('table', () => { app: { $refs } } = window const vm = $refs.table_paginated - const ariaLabel = vm.labelSortDesc + const ariaLabelDesc = vm.labelSortDesc + const ariaLabelClear = vm.labelSortClear - const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') + const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') expect(tfoot).toBeDefined() if (tfoot) { const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') expect(tr).toBeDefined() if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabel) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabel) - expect(tr.children[2].getAttribute('aria-label')).toBe(null) - expect(tr.children[3].getAttribute('aria-label')).toBe(null) + expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) + expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) } } }) From 4985f0370b831a2ca3529f5ee785428fa1941b2d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:25:57 -0300 Subject: [PATCH 044/159] Update table-thead-events.spec.js --- .../table/table-thead-events.spec.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/components/table/table-thead-events.spec.js b/src/components/table/table-thead-events.spec.js index bd5c8de46e8..14cbc662b4e 100644 --- a/src/components/table/table-thead-events.spec.js +++ b/src/components/table/table-thead-events.spec.js @@ -5,11 +5,40 @@ const testItems = [{ a: 1, b: 2, c: 3 }] const testFields = [{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }, { key: 'c', label: 'C' }] describe('table thead events', () => { + it('should not emit head-clicked event when a head cell is clicked and no head-clicked listener', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems + }, + listeners: { + // head-clicked will only be emitted if there is a registered listener + 'head-clicked': () => {} + } + }) + expect(wrapper).toBeDefined() + const $rows = wrapper.findAll('thead > tr') + expect($rows.length).toBe(1) + const $ths = wrapper.findAll('thead > tr > th') + expect($ths.length).toBe(testFields.length) + expect(wrapper.emitted('head-clicked')).not.toBeDefined() + $ths.at(0).trigger('click') + expect(wrapper.emitted('head-clicked')).not.toBeDefined() + $ths.at(1).trigger('click') + expect(wrapper.emitted('head-clicked')).not.toBeDefined() + $ths.at(2).trigger('click') + expect(wrapper.emitted('head-clicked')).not.toBeDefined() + }) + it('should emit head-clicked event when a head cell is clicked', async () => { const wrapper = mount(Table, { propsData: { fields: testFields, items: testItems + }, + listeners: { + // head-clicked will only be emitted if there is a registered listener + 'head-clicked': () => {} } }) expect(wrapper).toBeDefined() @@ -42,6 +71,10 @@ describe('table thead events', () => { fields: testFields, items: testItems, busy: true + }, + listeners: { + // head-clicked will only be emitted if there is a registered listener + 'head-clicked': () => {} } }) expect(wrapper).toBeDefined() @@ -59,6 +92,10 @@ describe('table thead events', () => { propsData: { fields: testFields, items: testItems + }, + listeners: { + // head-clicked will only be emitted if there is a registered listener + 'head-clicked': () => {} } }) wrapper.setData({ @@ -80,6 +117,10 @@ describe('table thead events', () => { fields: testFields, items: testItems }, + listeners: { + // head-clicked will only be emitted if there is a registered listener + 'head-clicked': () => {} + }, slots: { // in Vue 2.6x, slots get translated into scopedSlots HEAD_a: '', From ac1dea50bd26231763f5729fb20e6cd7c589d9df Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:27:31 -0300 Subject: [PATCH 045/159] Update table-tfoot-events.spec.js --- src/components/table/table-tfoot-events.spec.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/components/table/table-tfoot-events.spec.js b/src/components/table/table-tfoot-events.spec.js index afdec6a389a..4cd1b8d3e59 100644 --- a/src/components/table/table-tfoot-events.spec.js +++ b/src/components/table/table-tfoot-events.spec.js @@ -11,6 +11,10 @@ describe('table tfoot events', () => { fields: testFields, items: testItems, footClone: true + }, + listeners: { + // head-clicked will not be emitted unless there is a registered head-clicked listener + 'head-clicked': () => {} } }) expect(wrapper).toBeDefined() @@ -44,6 +48,10 @@ describe('table tfoot events', () => { items: testItems, footClone: true, busy: true + }, + listeners: { + // head-clicked will not be emitted unless there is a registered head-clicked listener + 'head-clicked': () => {} } }) expect(wrapper).toBeDefined() @@ -62,6 +70,10 @@ describe('table tfoot events', () => { fields: testFields, items: testItems, footClone: true + }, + listeners: { + // head-clicked will not be emitted unless there is a registered head-clicked listener + 'head-clicked': () => {} } }) wrapper.setData({ @@ -84,6 +96,10 @@ describe('table tfoot events', () => { items: testItems, footClone: true }, + listeners: { + // head-clicked will not be emitted unless there is a registered head-clicked listener + 'head-clicked': () => {} + }, slots: { // in Vue 2.6x, slots get translated into scopedSlots FOOT_a: '', From e45966cef37c9712250c61909ba7caab2216f914 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:29:38 -0300 Subject: [PATCH 046/159] Update table-thead-events.spec.js --- src/components/table/table-thead-events.spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/table/table-thead-events.spec.js b/src/components/table/table-thead-events.spec.js index 14cbc662b4e..6aeb3254cdb 100644 --- a/src/components/table/table-thead-events.spec.js +++ b/src/components/table/table-thead-events.spec.js @@ -12,8 +12,6 @@ describe('table thead events', () => { items: testItems }, listeners: { - // head-clicked will only be emitted if there is a registered listener - 'head-clicked': () => {} } }) expect(wrapper).toBeDefined() From faa9f8b4133af5b5ec22cdc1f9e237bd551a8beb Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:31:21 -0300 Subject: [PATCH 047/159] Update table-thead-events.spec.js --- src/components/table/table-thead-events.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/table/table-thead-events.spec.js b/src/components/table/table-thead-events.spec.js index 6aeb3254cdb..805bb4d54a7 100644 --- a/src/components/table/table-thead-events.spec.js +++ b/src/components/table/table-thead-events.spec.js @@ -11,8 +11,7 @@ describe('table thead events', () => { fields: testFields, items: testItems }, - listeners: { - } + listeners: {} }) expect(wrapper).toBeDefined() const $rows = wrapper.findAll('thead > tr') From f3886065ffc450c88b6fadaa13f067ba5ba86400 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:36:38 -0300 Subject: [PATCH 048/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index aafa2ae8205..14b44599a53 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -135,13 +135,13 @@ export default { // Handlers // Need to move from thead-mixin handleSort(key, field, evt, isFoot) { - if (isFoot && this.noFoooterSorting) { - return - } if (!this.isSortable) { /* istanbul ignore next */ return } + if (isFoot && this.noFooterSorting) { + return + } // TODO: make this tri-state sorting // cycle desc => asc => none => desc => ... let sortChanged = false From 64b2cd76ee36aa315947c7283dacd852d72eef3a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:43:36 -0300 Subject: [PATCH 049/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 14b44599a53..7d29c518c69 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -197,10 +197,12 @@ export default { ? this.localSortDesc && this.localSortBy === key ? this.labelSortAsc : this.labelSortDesc - : this.labelSortClear - // Assemble the aria-label - ariaLabel = [ariaLabel, ariaLabelSorting].filter(Boolean).join(': ') - // Assemble the aria-sort + : this.noSortReset + ? '' + : this.labelSortClear + // Assemble the aria-label attribute value + ariaLabel = [ariaLabel.trim(), ariaLabelSorting.trim()].filter(Boolean).join(': ') + // Assemble the aria-sort attribute value const ariaSort = sortable && this.localSortBy === key ? this.localSortDesc From 8bf74a250a496f0554ca39a1782705066b06e75a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:49:34 -0300 Subject: [PATCH 050/159] Update table-sort.spec.js --- src/components/table/table-sort.spec.js | 192 ++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/src/components/table/table-sort.spec.js b/src/components/table/table-sort.spec.js index 677a5e7dc97..650fd5c6531 100644 --- a/src/components/table/table-sort.spec.js +++ b/src/components/table/table-sort.spec.js @@ -265,4 +265,196 @@ describe('table sorting', () => { wrapper.destroy() }) + + it('should sort columns when clicking footers', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + footClone: true + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + let $rows + let columnA + let columnB + + // Should not be sorted + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('sort-changed')).not.toBeDefined() + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + // Sort by first column + wrapper + .findAll('tfoot > tr > th') + .at(0) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed')).toBeDefined() + expect(wrapper.emitted('sort-changed').length).toBe(1) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('1') + expect(columnA[1]).toBe('2') + expect(columnA[2]).toBe('3') + + // Click first column header again to reverse sort + wrapper + .findAll('foot > tr > th') + .at(0) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed').length).toBe(2) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('2') + expect(columnA[2]).toBe('1') + + // Click second column header to sort by it (by using keydown.enter) + wrapper + .findAll('tfoot > tr > th') + .at(1) + .trigger('keydown.enter') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed').length).toBe(3) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnB = $rows.map(row => { + return row + .findAll('td') + .at(1) + .text() + }) + expect(columnB[0]).toBe('a') + expect(columnB[1]).toBe('b') + expect(columnB[2]).toBe('c') + + // Click third column header to clear sort + wrapper + .findAll('tfoot > tr > th') + .at(2) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed').length).toBe(4) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + wrapper.destroy() + }) + + it('should not sort columns when clicking footers and no-footer-sorting set', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + footClone: true, + noFooterSorting: true + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + let $rows + let columnA + let columnB + + // Should not be sorted + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('sort-changed')).not.toBeDefined() + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + // Click first column + wrapper + .findAll('tfoot > tr > th') + .at(0) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed')).not.toBeDefined() + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + // Click third column header + wrapper + .findAll('tfoot > tr > th') + .at(2) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed')).not.toBeDefined() + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + wrapper.destroy() + }) }) From a395bae3184aa6a0f87d15b5e1a166b8cb74b9ee Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:54:01 -0300 Subject: [PATCH 051/159] Update table-sort.spec.js --- src/components/table/table-sort.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/table/table-sort.spec.js b/src/components/table/table-sort.spec.js index 650fd5c6531..70ccc20097a 100644 --- a/src/components/table/table-sort.spec.js +++ b/src/components/table/table-sort.spec.js @@ -396,7 +396,6 @@ describe('table sorting', () => { expect(wrapper.findAll('tbody > tr').length).toBe(3) let $rows let columnA - let columnB // Should not be sorted await wrapper.vm.$nextTick() From fa75c110b372627b96ba4cbca3397879356e7de6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 21:55:01 -0300 Subject: [PATCH 052/159] Update table-sort.spec.js --- src/components/table/table-sort.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-sort.spec.js b/src/components/table/table-sort.spec.js index 70ccc20097a..248159967a1 100644 --- a/src/components/table/table-sort.spec.js +++ b/src/components/table/table-sort.spec.js @@ -321,7 +321,7 @@ describe('table sorting', () => { // Click first column header again to reverse sort wrapper - .findAll('foot > tr > th') + .findAll('tfoot > tr > th') .at(0) .trigger('click') await wrapper.vm.$nextTick() From cea5ff3b6435673405c07cc58e3af02142b43579 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 20 Mar 2019 22:00:06 -0300 Subject: [PATCH 053/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 7d29c518c69..9e6ba32fa03 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -90,7 +90,7 @@ export default { } }, watch: { - isSortable(newVal, oldVal) { + isSortable(newVal, oldVal) /* istanbul ignore next: pain in the butt to test */ { if (newVal) { if (this.isSortable) { this.$on('head-clicked', this.handleSort) From 8340577e3b2cc80cece3ee5c1e3f686f32cffa08 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 00:17:18 -0300 Subject: [PATCH 054/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 9e6ba32fa03..4375ebf3bcc 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -15,6 +15,10 @@ export default { default: false }, sortDirection: { + // This prop is named incorrectly. + // It should be initialSortDirection + // As it is a bit misleading (not to mention screws up + // the Aria Label on the headers) type: String, default: 'asc', validator: direction => arrayIncludes(['asc', 'desc', 'last'], direction) @@ -24,6 +28,10 @@ export default { default: null }, noSortReset: { + // Another prop that should have had a better name. + // It should be noSortClear (on non-sortable headers). + // We will need to make sure the documentation is clear on what + // this prop does (as well as in the code for future reference) type: Boolean, default: false }, @@ -147,10 +155,23 @@ export default { let sortChanged = false const toggleLocalSortDesc = () => { const sortDirection = field.sortDirection || this.sortDirection + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // This prop/property needs better documentation as to what it does + // (especially here in the code). It appears to be an initial sort + // direction for a column. But it has conflicts under certain + // situations causing the Aria Labels for a column to get messed up + // giving the opposite label as to what the click actually does. + // The property, if kept, should be called initiaSortDirection + // to make it clear as to what it is (and that it doesn't change + // sorting direction on the fly) + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (sortDirection === 'asc') { this.localSortDesc = false } else if (sortDirection === 'desc') { this.localSortDesc = true + } else { + // sortDirection === 'last' + // Leave at last sort direction from previous column } } if (field.sortable) { @@ -160,6 +181,7 @@ export default { } else { // Start sorting this column ascending this.localSortBy = key + // this.localSortDesc = false toggleLocalSortDesc() } sortChanged = true @@ -189,17 +211,36 @@ export default { let ariaLabel = '' if (!field.label.trim() && !field.headerTitle) { // In case field's label and title are empty/blank, we need to - // add a hint about what the column is about for non-sighted users + // add a hint about what the column is about for non-sighted users. + // This is dulicated code from tbody-row mixin, but we need it + // here as well, since we overwrite the original aria-label. /* istanbul ignore next */ ariaLabel = startCase(key) } - const ariaLabelSorting = sortable - ? this.localSortDesc && this.localSortBy === key - ? this.labelSortAsc - : this.labelSortDesc - : this.noSortReset - ? '' - : this.labelSortClear + // The correctness of these labels is very important for screen-reader users. + // Currently the field.sortDirection property complicates the following: + let ariaLabelSorting = '' + if (sortable) { + if (this.localSortBy === key) { + // currently sorted sortable column. + ariaLabelSorting = this.localSortDesc ? this.labelSortAsc : this.labelSortDesc + } else { + // Not currently sorted sortable column. + // Have to add special tests to handle this.sortDirection and field.sortDirection. + // Not using nested ternary's here for clarity/readability + const sortDirection = this.sortDirection || field.sortDirection + if (sortDirection === 'asc') { + ariaLabelSorting = this.labelSortAsc + } else if (sortDirection === 'desc') { + ariaLabelSorting = this.labelSortDesc + } else { // 'last' + ariaLabelSorting = this.localSortDesc ? this.labelSortDesc : this.labelSortAsc + } + } + } else if (!this.noSortReset) { + // Non sortable column + ariaLabelSorting = this.labelSortClear + } // Assemble the aria-label attribute value ariaLabel = [ariaLabel.trim(), ariaLabelSorting.trim()].filter(Boolean).join(': ') // Assemble the aria-sort attribute value @@ -212,6 +253,7 @@ export default { ? 'none' : null // Return the attributes + // (All the above just to get these two values) return { 'aria-label': ariaLabel || null, 'aria-sort': ariaSort From 06ba8b7b642619c05a707cf61844c9ca674fbf30 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 00:24:02 -0300 Subject: [PATCH 055/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 4375ebf3bcc..706c1ffdf90 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -233,7 +233,8 @@ export default { ariaLabelSorting = this.labelSortAsc } else if (sortDirection === 'desc') { ariaLabelSorting = this.labelSortDesc - } else { // 'last' + } else { + // sortDirection === 'last' ariaLabelSorting = this.localSortDesc ? this.labelSortDesc : this.labelSortAsc } } From 6206ae87609c5d477b275754211b4b16094f433b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 00:32:05 -0300 Subject: [PATCH 056/159] Update table.spec.js --- src/components/table/table.spec.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index b1cbfde6f07..6f20cb24352 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -248,17 +248,21 @@ describe('table', () => { app: { $refs } } = window const vm = $refs.table_paginated - const ariaLabelDesc = vm.labelSortDesc + const ariaLabelAsc = vm.labelSortAsc const ariaLabelClear = vm.labelSortClear + // No columns sorted + expect(vm.localSortDesc).toBe(false) + expect(vm.localSortBy).toEqual(null) + const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') expect(thead).toBeDefined() if (thead) { const tr = [...thead.children].find(el => el && el.tagName === 'TR') expect(tr).toBeDefined() if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelDesc) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelAsc) + expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelAsc) expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) } @@ -270,17 +274,21 @@ describe('table', () => { app: { $refs } } = window const vm = $refs.table_paginated - const ariaLabelDesc = vm.labelSortDesc + const ariaLabelAsc = vm.labelSortAsc const ariaLabelClear = vm.labelSortClear + // No columns sorted + expect(vm.localSortDesc).toBe(false) + expect(vm.localSortBy).toEqual(null) + const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') expect(tfoot).toBeDefined() if (tfoot) { const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') expect(tr).toBeDefined() if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelDesc) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelDesc) + expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelAsc) + expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelAsc) expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) } From bd8e61d0de5c1f98bb404ad0dfddaa4a578045e2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 00:58:17 -0300 Subject: [PATCH 057/159] Update table.spec.js --- src/components/table/table.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 6f20cb24352..5921a2ad2c5 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -253,7 +253,7 @@ describe('table', () => { // No columns sorted expect(vm.localSortDesc).toBe(false) - expect(vm.localSortBy).toEqual(null) + expect(vm.localSortBy).toEqual('') const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') expect(thead).toBeDefined() @@ -279,7 +279,7 @@ describe('table', () => { // No columns sorted expect(vm.localSortDesc).toBe(false) - expect(vm.localSortBy).toEqual(null) + expect(vm.localSortBy).toEqual('') const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') expect(tfoot).toBeDefined() From 27797f193f440c4733b601301bc2fe3c538f3680 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 01:14:29 -0300 Subject: [PATCH 058/159] Update and rename table-sort.spec.js to table-sorting.spec.js --- ...ble-sort.spec.js => table-sorting.spec.js} | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) rename src/components/table/{table-sort.spec.js => table-sorting.spec.js} (86%) diff --git a/src/components/table/table-sort.spec.js b/src/components/table/table-sorting.spec.js similarity index 86% rename from src/components/table/table-sort.spec.js rename to src/components/table/table-sorting.spec.js index 248159967a1..3f5bf466b08 100644 --- a/src/components/table/table-sort.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -71,6 +71,23 @@ describe('table sorting', () => { expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('3') + let $ths = wrapper.findAll('thead > tr > th') + + // currently sorted as ascending + expect($ths.at(0).attributes('aria-sort')).toBe('ascending') + // for switching to descending + expect($ths.at(0).attributes('aria-label')).toBe(wrapper.vm.labelSortDesc) + + // not sorted by this column + expect($ths.at(1).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not a sortable column + expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() + // for clearing sorting + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + // Change sort direction wrapper.setProps({ sortDesc: true @@ -90,6 +107,23 @@ describe('table sorting', () => { expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('1') + $ths = wrapper.findAll('thead > tr > th') + + // currently sorted as descending + expect($ths.at(0).attributes('aria-sort')).toBe('descending') + // for switching to ascending + expect($ths.at(0).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not sorted by this column + expect($ths.at(1).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not a sortable column + expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() + // for clearing sorting + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + // Clear sort wrapper.setProps({ sortBy: null, @@ -110,6 +144,23 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + $ths = wrapper.findAll('thead > tr > th') + + // currently not sorted + expect($ths.at(0).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(0).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not sorted by this column + expect($ths.at(1).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not a sortable column + expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() + // for clearing sorting + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + wrapper.destroy() }) From 952fce41e59d6ae5621a53dc09fdc175cebbcbb2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 01:20:08 -0300 Subject: [PATCH 059/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 3f5bf466b08..c60f31c23a3 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -40,7 +40,7 @@ describe('table sorting', () => { wrapper.destroy() }) - it('should sort column descending when sortBy set and sortDesc changed', async () => { + it('should sort column descending when sortBy set and sortDesc changed, with proper attributes', async () => { const wrapper = mount(Table, { propsData: { fields: testFields, @@ -72,7 +72,7 @@ describe('table sorting', () => { expect(columnA[2]).toBe('3') let $ths = wrapper.findAll('thead > tr > th') - + // currently sorted as ascending expect($ths.at(0).attributes('aria-sort')).toBe('ascending') // for switching to descending @@ -86,7 +86,7 @@ describe('table sorting', () => { // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() // for clearing sorting - expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) // Change sort direction wrapper.setProps({ @@ -122,7 +122,7 @@ describe('table sorting', () => { // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() // for clearing sorting - expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) // Clear sort wrapper.setProps({ @@ -159,7 +159,7 @@ describe('table sorting', () => { // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() // for clearing sorting - expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) wrapper.destroy() }) From b6b419b216858496410ec0d380a970727a0c14c2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 01:30:50 -0300 Subject: [PATCH 060/159] Update table.spec.js --- src/components/table/table.spec.js | 263 +---------------------------- 1 file changed, 2 insertions(+), 261 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 5921a2ad2c5..26b8f2606cb 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -166,135 +166,6 @@ describe('table', () => { }) }) - it('all examples have sortable & unsortable headers', async () => { - const { - app: { $refs } - } = window - - const tables = ['table_basic', 'table_paginated', 'table_dark'] - // const sortables = [true, true, false, false] - - tables.forEach(table => { - const vm = $refs[table] - const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - const fieldKeys = Object.keys(vm.fields) - const ths = [...tr.children] - expect(ths.length).toBe(fieldKeys.length) - ths.forEach((th, idx) => { - expect(th.hasAttribute('aria-sort')).toBe(vm.fields[fieldKeys[idx]].sortable || false) - }) - } - } - }) - }) - - it('table_paginated has sortable & unsortable footers', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const fieldKeys = Object.keys(vm.fields) - - const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - if (tfoot) { - const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - const ths = [...tr.children] - expect(ths.length).toBe(fieldKeys.length) - ths.forEach((th, idx) => { - expect(th.hasAttribute('aria-sort')).toBe(vm.fields[fieldKeys[idx]].sortable || false) - }) - } - } - }) - - it('all example tables should have attribute aria-busy="false" when busy is false', async () => { - const { app } = window - - const tables = ['table_basic', 'table_paginated', 'table_dark'] - - await setData(app, 'isBusy', false) - await nextTick() - - tables.forEach(table => { - expect(app.$refs[table].$el.getAttribute('aria-busy')).toBe('false') - }) - }) - - it('table_paginated should have attribute aria-busy="true" when busy is true', async () => { - const { - app: { $refs } - } = window - const app = window.app - - await setData(app, 'isBusy', true) - await nextTick() - expect($refs.table_paginated.$el.getAttribute('aria-busy')).toBe('true') - - await setData(app, 'isBusy', false) - await nextTick() - expect($refs.table_paginated.$el.getAttribute('aria-busy')).toBe('false') - }) - - it('sortable columns should have ARIA labels in thead', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const ariaLabelAsc = vm.labelSortAsc - const ariaLabelClear = vm.labelSortClear - - // No columns sorted - expect(vm.localSortDesc).toBe(false) - expect(vm.localSortBy).toEqual('') - - const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelAsc) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelAsc) - expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) - expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) - } - } - }) - - it('sortable columns should have ARIA labels in tfoot', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const ariaLabelAsc = vm.labelSortAsc - const ariaLabelClear = vm.labelSortClear - - // No columns sorted - expect(vm.localSortDesc).toBe(false) - expect(vm.localSortBy).toEqual('') - - const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - if (tfoot) { - const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].getAttribute('aria-label')).toBe(ariaLabelAsc) - expect(tr.children[1].getAttribute('aria-label')).toBe(ariaLabelAsc) - expect(tr.children[2].getAttribute('aria-label')).toBe(ariaLabelClear) - expect(tr.children[3].getAttribute('aria-label')).toBe(ariaLabelClear) - } - } - }) - it('all examples should have variant "success" on 1st row', async () => { const { app: { $refs } @@ -400,86 +271,6 @@ describe('table', () => { } }) - it('sortable header th should emit a sort-changed event with context when clicked and sort changed', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const spy = jest.fn() - const fieldKeys = Object.keys(vm.fields) - - vm.$on('sort-changed', spy) - const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - let sortBy = null - const ths = [...tr.children] - expect(ths.length).toBe(fieldKeys.length) - ths.forEach((th, idx) => { - th.click() - if (vm.fields[fieldKeys[idx]].sortable) { - expect(spy).toHaveBeenCalledWith(vm.context) - expect(vm.context.sortBy).toBe(fieldKeys[idx]) - sortBy = vm.context.sortBy - } else { - if (sortBy) { - expect(spy).toHaveBeenCalledWith(vm.context) - expect(vm.context.sortBy).toBe(null) - sortBy = null - } else { - expect(spy).not.toHaveBeenCalled() - expect(vm.context.sortBy).toBe(null) - } - } - spy.mockClear() - }) - } - } - }) - - it('sortable footer th should emit a sort-changed event with context when clicked and sort changed', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const spy = jest.fn() - const fieldKeys = Object.keys(vm.fields) - - vm.$on('sort-changed', spy) - const tfoot = [...vm.$el.children].find(el => el && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - if (tfoot) { - const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - let sortBy = null - const ths = [...tr.children] - expect(ths.length).toBe(fieldKeys.length) - ths.forEach((th, idx) => { - th.click() - if (vm.fields[fieldKeys[idx]].sortable) { - expect(spy).toHaveBeenCalledWith(vm.context) - expect(vm.context.sortBy).toBe(fieldKeys[idx]) - sortBy = vm.context.sortBy - } else { - if (sortBy) { - expect(spy).toHaveBeenCalledWith(vm.context) - expect(vm.context.sortBy).toBe(null) - sortBy = null - } else { - expect(spy).not.toHaveBeenCalled() - expect(vm.context.sortBy).toBe(null) - } - } - spy.mockClear() - }) - } - } - }) - it('non-sortable header th should not emit a sort-changed event when clicked and prop no-sort-reset is set', async () => { const { app: { $refs } @@ -634,59 +425,9 @@ describe('table', () => { } }) - it('table_provider should emit a refreshed event for providerArray', async () => { - const { app } = window - const vm = app.$refs.table_provider - const spy = jest.fn() - - await setData(app, 'providerType', 'array') - await nextTick() - await sleep(100) - - vm.$on('refreshed', spy) - vm.refresh() - await nextTick() - await sleep(100) - - expect(spy).toHaveBeenCalled() - // expect(vm.value.length).toBe(app.items.length) - }) - - it('table_provider should emit a refreshed event for providerCallback', async () => { - const { app } = window - const vm = app.$refs.table_provider - const spy = jest.fn() - - await setData(app, 'providerType', 'callback') - await nextTick() - await sleep(100) - - vm.$on('refreshed', spy) - vm.refresh() - await nextTick() - await sleep(100) - - expect(spy).toHaveBeenCalled() - }) - - it('table_provider should emit a refreshed event for providerPromise', async () => { - const { app } = window - const vm = app.$refs.table_provider - const spy = jest.fn() - - await setData(app, 'providerType', 'promise') - await nextTick() - await sleep(100) - - vm.$on('refreshed', spy) - vm.refresh() - await nextTick() - await sleep(100) - - expect(spy).toHaveBeenCalled() - }) - it('should render stacked table', async () => { + // This test should be a bit more intesive, looking for the data- attributes + // and cell inner div wrapper const { app } = window const vm = app.$refs.table_stacked From e8d2fed73df058fba9e54210c3cadeafa5aec01b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 01:55:11 -0300 Subject: [PATCH 061/159] Update README.md --- src/components/table/README.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/components/table/README.md b/src/components/table/README.md index 47406c46df2..1e1fcfec227 100644 --- a/src/components/table/README.md +++ b/src/components/table/README.md @@ -323,14 +323,14 @@ 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` from as an array of objects. | +| `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 `` data ``/`` heading ``/`` field `` | | `dark` | Boolean | Invert the colors — with light text on dark backgrounds (equivalent to Bootstrap V4 class `.table-dark`) | -| `fixed` | Boolean | Generate a table with equal fixed-width columns (`table-layout: fixed`) | +| `fixed` | Boolean | Generate a table with equal fixed-width columns (`table-layout: fixed;`) | | `foot-clone` | Boolean | Turns on the table footer, and defaults with the same contents a the table header | | `no-footer-sorting` | Boolean | When `foot-clone` is true and the table is sortable, disables the sorting icons and click behaviour on the footer heading cells. Refer to the [**Sorting**](#sorting) section below for more details. | | `responsive` | Boolean or String | Generate a responsive table to make it scroll horizontally. Set to `true` for an always responsive table, or set it to one of the breakpoints `'sm'`, `'md'`, `'lg'`, or `'xl'` to make the table responsive (horizontally scroll) only on screens smaller than the breakpoint. See [**Responsive tables**](#responsive-tables) below for details. | @@ -1309,10 +1309,20 @@ pre-specify the column to be sorted, set the `sort-by` prop to the field's key. direction by setting `sort-desc` to either `true` (for descending) or `false` (for ascending, the default). +- **Ascending**: Items are sorted lowest to highest (i.e. `A` to `Z`) and will be displayed with the + lowest value in the first row with progressively higher values in the following rows. The header + indicator arrow will point in the direction of lowest to highest. (i.e. down for ascending). +- **Descending**: Items are sorted highest to lowest (i.e. `Z` to `A`) and will be displayed with + the highest value in the first row with progressively lower values in the following rows. The + header indicator arrow will point in the direction of lowest to highest (i.e. up for descending). + The props `sort-by` and `sort-desc` can be turned into _two-way_ (syncable) props by adding the `.sync` modifier. Your bound variables will then be updated accordingly based on the current sort criteria. See the [Vue docs](http://vuejs.org/v2/guide/components.html#sync-Modifier) for details on -the `.sync` prop modifier +the `.sync` prop modifier. + +Setting `sort-by` to a column that is not defined in the fields as `sortable` will result in the +table not being sorted. When the prop `foot-clone` is set, the footer headings will also allow sorting by clicking, even if you have custom formatted footer field headers. To disable the sort icons and sorting via heading @@ -1427,16 +1437,19 @@ with a single argument containing the context object of ``. See the [Detection of sorting change](#detection-of-sorting-change) section below for details about the sort-changed event and the context object. -### Change sort direction +### Change initial sort direction Control the order in which ascending and descending sorting is applied when a sortable column header is clicked, by using the `sort-direction` prop. The default value `'asc'` applies ascending sort -first. To reverse the behavior and sort in descending direction first, set it to `'desc'`. +first (when a column is not currently sorted). To reverse the behavior and sort in descending +direction first, set it to `'desc'`. -If you don't want the sorting direction to change at all when clicking another sortable column -header, set `sort-direction` to `'last'`. +If you don't want the current sorting direction to change when clicking another sortable column +header, set `sort-direction` to `'last'`. This will maintain the sorting direction of the previously +sorted column. -For individual column sort directions, specify the property `sortDirection` in `fields`. See the +For individual column initial sort direction (which applies when the column transitions from unsorted +to sorted), specify the property `sortDirection` in `fields`. See the [Complete Example](#complete-example) below for an example of using this feature. ## Filtering From 52eb8e05e9da6991f22a1b3076bb567b7d33eea5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 01:56:18 -0300 Subject: [PATCH 062/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 26b8f2606cb..8d5fc81e83b 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -1,4 +1,4 @@ -import { loadFixture, testVM, setData, nextTick, sleep } from '../../../tests/utils' +import { loadFixture, testVM, setData, nextTick } from '../../../tests/utils' describe('table', () => { beforeEach(loadFixture(__dirname, 'table')) From 5783cef0fe1d44a8ff7f1a90a5690f71ea48451f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:02:50 -0300 Subject: [PATCH 063/159] Update utils.js --- tests/utils.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/utils.js b/tests/utils.js index bd6a436d641..b66a507441b 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -14,7 +14,7 @@ Vue.config.devtools = false window.Vue = Vue Vue.use(BootstrapVue) -export function loadFixture(dirName, name) { +export function loadFixture(dirName, name) /* istanbul ignore next */ { const fixtureBase = resolve(dirName, 'fixtures') const template = readFileSync(resolve(fixtureBase, name + '.html'), 'UTF-8') const js = readFileSync(resolve(fixtureBase, name + '.js'), 'UTF-8') @@ -33,25 +33,31 @@ export function loadFixture(dirName, name) { } export async function testVM() { + /* istanbul ignore next */ it(`vm mounts`, async () => { return expect(window.app.$el).toBeDefined() }) } export function nextTick() { + /* istanbul ignore next */ return new Promise((resolve, reject) => { Vue.nextTick(resolve) }) } export async function setData(app, key, value) { + /* istanbul ignore next */ app[key] = value + /* istanbul ignore next */ await nextTick() } // Usage: await sleep(1000); export function sleep(ms) { + /* istanbul ignore next */ ms = ms || 0 + /* istanbul ignore next */ return new Promise((resolve, reject) => setTimeout(resolve, ms)) } @@ -76,13 +82,14 @@ const throwIfNotHTMLElement = el => { } } +/* istanbul ignore next */ const throwIfNotArray = array => { - /* istanbul ignore next */ if (!Array.isArray(array)) { throw new TypeError(`The matcher requires an array. Given ${typeof array}`) } } +/* istanbul ignore next */ const vmHasClass = (vm, className) => { throwIfNotVueInstance(vm) return vm.$el._prevClass.indexOf(className) !== -1 @@ -93,6 +100,7 @@ const vmHasClass = (vm, className) => { * @param {string} className * @return {boolean} */ +/* istanbul ignore next */ const elHasClass = (el, className) => { throwIfNotHTMLElement(el) return el.classList.contains(className) @@ -103,14 +111,19 @@ const elHasClass = (el, className) => { * @param {string} className * @return {boolean} */ +/* istanbul ignore next */ const hasClass = (node, className) => isVueInstance(node) ? vmHasClass(node, className) : elHasClass(node, className) +/* istanbul ignore next */ const getVmTag = vm => vm.$options._componentTag +/* istanbul ignore next */ const getHTMLTag = el => String(el.tagName).toLowerCase() +/* istanbul ignore next */ const getTagName = node => (isVueInstance(node) ? getVmTag(node) : getHTMLTag(node)) // Extend Jest marchers +/* istanbul ignore next */ expect.extend({ toHaveClass(node, className) { /* istanbul ignore next */ From 679b75170be6e570f714bab9c695c21ae5eff69f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:04:18 -0300 Subject: [PATCH 064/159] Update utils.js --- tests/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils.js b/tests/utils.js index b66a507441b..97114b32258 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,3 +1,4 @@ +/* istanbul ignore file */ import { readFileSync } from 'fs' import { resolve } from 'path' import BootstrapVue from '../src' From e7b6a4737211aa476f6a07c831e706af9e90f4fd Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:08:39 -0300 Subject: [PATCH 065/159] Update mixin-items.js --- src/components/table/helpers/mixin-items.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/table/helpers/mixin-items.js b/src/components/table/helpers/mixin-items.js index 332c0941fb0..c919933c5da 100644 --- a/src/components/table/helpers/mixin-items.js +++ b/src/components/table/helpers/mixin-items.js @@ -44,6 +44,7 @@ export default { }, watch: { items(newItems) { + /* istanbul ignore else */ if (isArray(newItems)) { // Set localItems/filteredItems to a copy of the provided array this.localItems = newItems.slice() From bc6ec25ac446b0b690238eacf67eda79f27a9f02 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:29:08 -0300 Subject: [PATCH 066/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 106 +++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index c60f31c23a3..a03e0c825c4 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -348,6 +348,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + // Should have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) // Sort by first column wrapper @@ -369,6 +372,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('1') expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('3') + // Should have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) // Click first column header again to reverse sort wrapper @@ -389,6 +395,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('1') + // Should have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) // Click second column header to sort by it (by using keydown.enter) wrapper @@ -429,6 +438,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + // Should have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) wrapper.destroy() }) @@ -464,6 +476,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + // Shouldn't have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) // Click first column wrapper @@ -484,6 +499,9 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + // Shouldn't have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) // Click third column header wrapper @@ -504,6 +522,94 @@ describe('table sorting', () => { expect(columnA[0]).toBe('3') expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') + // Shouldn't have aria-* labels + expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) + + wrapper.destroy() + }) + + it('should sort column descending first, when sort-direction=desc', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + sortDesc: false, + sortDirection: 'desc' + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + let $rows + let columnA + + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('input').length).toBe(1) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + let $ths = wrapper.findAll('thead > tr > th') + + // currently not sorted + expect($ths.at(0).attributes('aria-sort')).toBe('none') + // for switching to descending + expect($ths.at(0).attributes('aria-label')).toBe(wrapper.vm.labelSortDesc) + + // not sorted by this column + expect($ths.at(1).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortDesc) + + // not a sortable column + expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() + // for clearing sorting + expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + + // Change sort direction (should be descending first) + wrapper.findAll('thead > tr > th').at(0).trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input').length).toBe(2) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('2') + expect(columnA[2]).toBe('1') + + $ths = wrapper.findAll('thead > tr > th') + + // currently sorted as descending + expect($ths.at(0).attributes('aria-sort')).toBe('descending') + // for switching to ascending + expect($ths.at(0).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not sorted by this column + expect($ths.at(1).attributes('aria-sort')).toBe('none') + // for sorting by ascending + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + + // not a sortable column + expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() + // for clearing sorting + expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) wrapper.destroy() }) From 92b737be71099003b6632bab3c77022fb8bc4009 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:33:45 -0300 Subject: [PATCH 067/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index a03e0c825c4..e08ff3f8bbe 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -349,8 +349,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') // Should have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) // Sort by first column wrapper @@ -373,8 +373,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('3') // Should have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) // Click first column header again to reverse sort wrapper @@ -396,8 +396,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('2') expect(columnA[2]).toBe('1') // Should have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) // Click second column header to sort by it (by using keydown.enter) wrapper @@ -439,8 +439,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') // Should have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) wrapper.destroy() }) @@ -477,8 +477,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') // Shouldn't have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(0) // Click first column wrapper @@ -500,8 +500,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') // Shouldn't have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(0) // Click third column header wrapper @@ -523,8 +523,8 @@ describe('table sorting', () => { expect(columnA[1]).toBe('1') expect(columnA[2]).toBe('2') // Shouldn't have aria-* labels - expect(wrapper.find('tfoot > tr > th[aria-sort]').length).toBe(0) - expect(wrapper.find('tfoot > tr > th[aria-label]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(0) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(0) wrapper.destroy() }) From deb74245b8ea4d9c8d97dedbb72c59a7f05fe8c5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:36:46 -0300 Subject: [PATCH 068/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 706c1ffdf90..035c3669fec 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -241,6 +241,8 @@ export default { } else if (!this.noSortReset) { // Non sortable column ariaLabelSorting = this.labelSortClear + // TODO: this should be: (no label when no localSortBy + ariaLabelSorting = this.loclSortBy ? this.labelSortClear : null } // Assemble the aria-label attribute value ariaLabel = [ariaLabel.trim(), ariaLabelSorting.trim()].filter(Boolean).join(': ') From b43823158b2dc8777957ad69bcb81f4368cd1a83 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:38:02 -0300 Subject: [PATCH 069/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index e08ff3f8bbe..e1244daf1cb 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -578,7 +578,10 @@ describe('table sorting', () => { expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) // Change sort direction (should be descending first) - wrapper.findAll('thead > tr > th').at(0).trigger('click') + wrapper + .findAll('thead > tr > th') + .at(0) + .trigger('click') await wrapper.vm.$nextTick() expect(wrapper.emitted('input').length).toBe(2) $rows = wrapper.findAll('tbody > tr').wrappers From 243803feb6f9461cbacfdc1d90c0975ce57a3e0c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:40:20 -0300 Subject: [PATCH 070/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index e1244daf1cb..f06e4478b7e 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -545,8 +545,6 @@ describe('table sorting', () => { let columnA await wrapper.vm.$nextTick() - expect(wrapper.emitted('input')).toBeDefined() - expect(wrapper.emitted('input').length).toBe(1) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the first column text value @@ -583,7 +581,7 @@ describe('table sorting', () => { .at(0) .trigger('click') await wrapper.vm.$nextTick() - expect(wrapper.emitted('input').length).toBe(2) + $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the first column text value From 0e7665ced58a421f112c20172c539cbb5ca8f862 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:46:03 -0300 Subject: [PATCH 071/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 035c3669fec..b885932006b 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -155,16 +155,6 @@ export default { let sortChanged = false const toggleLocalSortDesc = () => { const sortDirection = field.sortDirection || this.sortDirection - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // This prop/property needs better documentation as to what it does - // (especially here in the code). It appears to be an initial sort - // direction for a column. But it has conflicts under certain - // situations causing the Aria Labels for a column to get messed up - // giving the opposite label as to what the click actually does. - // The property, if kept, should be called initiaSortDirection - // to make it clear as to what it is (and that it doesn't change - // sorting direction on the fly) - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (sortDirection === 'asc') { this.localSortDesc = false } else if (sortDirection === 'desc') { @@ -209,7 +199,7 @@ export default { } const sortable = field.sortable let ariaLabel = '' - if (!field.label.trim() && !field.headerTitle) { + if ((!field.label || !field.label.trim()) && !field.headerTitle) { // In case field's label and title are empty/blank, we need to // add a hint about what the column is about for non-sighted users. // This is dulicated code from tbody-row mixin, but we need it @@ -218,7 +208,6 @@ export default { ariaLabel = startCase(key) } // The correctness of these labels is very important for screen-reader users. - // Currently the field.sortDirection property complicates the following: let ariaLabelSorting = '' if (sortable) { if (this.localSortBy === key) { @@ -240,9 +229,7 @@ export default { } } else if (!this.noSortReset) { // Non sortable column - ariaLabelSorting = this.labelSortClear - // TODO: this should be: (no label when no localSortBy - ariaLabelSorting = this.loclSortBy ? this.labelSortClear : null + ariaLabelSorting = this.localSortBy ? this.labelSortClear : '' } // Assemble the aria-label attribute value ariaLabel = [ariaLabel.trim(), ariaLabelSorting.trim()].filter(Boolean).join(': ') From 030a4d179d33b1601df6b56b2154be68f1c21564 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:50:27 -0300 Subject: [PATCH 072/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index f06e4478b7e..8bac6bdd274 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -159,7 +159,7 @@ describe('table sorting', () => { // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() // for clearing sorting - expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + expect($ths.at(2).attributes('aria-label')).not.toBeDefined() wrapper.destroy() }) @@ -350,7 +350,7 @@ describe('table sorting', () => { expect(columnA[2]).toBe('2') // Should have aria-* labels expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(2) // Sort by first column wrapper @@ -573,7 +573,7 @@ describe('table sorting', () => { // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() // for clearing sorting - expect($ths.at(2).attributes('aria-label')).toBe(wrapper.vm.labelSortClear) + expect($ths.at(2).attributes('aria-label')).not.toBeDefined() // Change sort direction (should be descending first) wrapper From 372ee9b95d00142cafac374f6fb07c0988e52341 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 02:53:47 -0300 Subject: [PATCH 073/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 8bac6bdd274..6e33efb5906 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -440,7 +440,7 @@ describe('table sorting', () => { expect(columnA[2]).toBe('2') // Should have aria-* labels expect(wrapper.findAll('tfoot > tr > th[aria-sort]').length).toBe(2) - expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(3) + expect(wrapper.findAll('tfoot > tr > th[aria-label]').length).toBe(2) wrapper.destroy() }) @@ -605,7 +605,7 @@ describe('table sorting', () => { // not sorted by this column expect($ths.at(1).attributes('aria-sort')).toBe('none') // for sorting by ascending - expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortAsc) + expect($ths.at(1).attributes('aria-label')).toBe(wrapper.vm.labelSortDesc) // not a sortable column expect($ths.at(2).attributes('aria-sort')).not.toBeDefined() From f5c3deced5bb7370351e6a981fcd3c874651b821 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 09:58:36 -0300 Subject: [PATCH 074/159] Update table-provider.spec.js --- src/components/table/table-provider.spec.js | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/components/table/table-provider.spec.js b/src/components/table/table-provider.spec.js index 22b89f5717b..228b2ec706b 100644 --- a/src/components/table/table-provider.spec.js +++ b/src/components/table/table-provider.spec.js @@ -263,4 +263,54 @@ describe('b-table provider functions', () => { wrapper.destroy() }) + + it('reacts to items provider function change', async () => { + function provider1(ctx) { + return testItems.slice() + } + + function provider2(ctx) { + return testItems.slice(testItems.length - 1) + } + + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: provider1 + } + }) + expect(wrapper).toBeDefined() + + await Vue.nextTick() + + expect(wrapper.emitted('update:busy')).toBeDefined() + expect(wrapper.emitted('input')).toBeDefined() + + expect(wrapper.find('tbody').exists()).toBe(true) + expect( + wrapper + .find('tbody') + .findAll('tr') + .exists() + ).toBe(true) + expect(wrapper.find('tbody').findAll('tr').length).toBe(testItems.length) + + wrapper.setProps({ + items: provider2 + }) + + await Vue.nextTick() + + expect(wrapper.find('tbody').exists()).toBe(true) + expect( + wrapper + .find('tbody') + .findAll('tr') + .exists() + ).toBe(true) + + expect(wrapper.find('tbody').findAll('tr').length).toBe(1) + + wrapper.destroy() + }) }) From 1b4473ab0236245c7d3cbc41cebc01d416f55d2d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:13:20 -0300 Subject: [PATCH 075/159] Update table-row-details.spec.js --- .../table/table-row-details.spec.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/components/table/table-row-details.spec.js b/src/components/table/table-row-details.spec.js index 6c865418d0c..4e5f270e297 100644 --- a/src/components/table/table-row-details.spec.js +++ b/src/components/table/table-row-details.spec.js @@ -94,6 +94,67 @@ describe('table row details', () => { wrapper.destroy() }) + it('should show details slot when slot method toggleDetails() called', async () => { + const testItems = [ + { a: 1, b: 2, c: 3, _showDetails: true }, + ] + const testFields = ['a', 'b', 'c'] + let scopeDetails = null + let scopeField = null + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems + }, + scopedSlots: { + 'row-details': function(scope) { + scopeDetails = scope + return '
foobar
' + }, + 'a': function(scope) { + scopeSlot = scope + return '
AAA
' + } + } + }) + let $trs + expect(wrapper).toBeDefined() + expect(wrapper.find('tbody').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(2) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(2) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + expect($trs.at(1).is('tr.b-table-details')).toBe(true) + expect($trs.at(1).text()).toBe('foobar') + + // Toggle scoped records via details slot + expect(scopeDetails).not.toBe(null) + scopeDetails.toggleDetails() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(1) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + + // Toggle scoped records via field slot + expect(scopeField).not.toBe(null) + scopeField.toggleDetails() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(2) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(2) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + expect($trs.at(1).is('tr.b-table-details')).toBe(true) + expect($trs.at(1).text()).toBe('foobar') + + wrapper.destroy() + }) + it('should hide details slot when _showDetails changed', async () => { const testItems = [ { a: 1, b: 2, c: 3, _showDetails: true }, From cc4343c9da4043b1403299697d2c3e6f6854786b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:19:59 -0300 Subject: [PATCH 076/159] Update table-row-details.spec.js --- src/components/table/table-row-details.spec.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/table/table-row-details.spec.js b/src/components/table/table-row-details.spec.js index 4e5f270e297..7f0a9599f03 100644 --- a/src/components/table/table-row-details.spec.js +++ b/src/components/table/table-row-details.spec.js @@ -95,9 +95,7 @@ describe('table row details', () => { }) it('should show details slot when slot method toggleDetails() called', async () => { - const testItems = [ - { a: 1, b: 2, c: 3, _showDetails: true }, - ] + const testItems = [{ a: 1, b: 2, c: 3, _showDetails: true }] const testFields = ['a', 'b', 'c'] let scopeDetails = null let scopeField = null @@ -111,8 +109,8 @@ describe('table row details', () => { scopeDetails = scope return '
foobar
' }, - 'a': function(scope) { - scopeSlot = scope + a: function(scope) { + scopeField = scope return '
AAA
' } } From 4e63aae643bb969e5ffb9cd1e8544000538fbd59 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:28:36 -0300 Subject: [PATCH 077/159] Update table-row-details.spec.js --- .../table/table-row-details.spec.js | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/components/table/table-row-details.spec.js b/src/components/table/table-row-details.spec.js index 7f0a9599f03..03e69b5878b 100644 --- a/src/components/table/table-row-details.spec.js +++ b/src/components/table/table-row-details.spec.js @@ -94,65 +94,6 @@ describe('table row details', () => { wrapper.destroy() }) - it('should show details slot when slot method toggleDetails() called', async () => { - const testItems = [{ a: 1, b: 2, c: 3, _showDetails: true }] - const testFields = ['a', 'b', 'c'] - let scopeDetails = null - let scopeField = null - const wrapper = mount(Table, { - propsData: { - fields: testFields, - items: testItems - }, - scopedSlots: { - 'row-details': function(scope) { - scopeDetails = scope - return '
foobar
' - }, - a: function(scope) { - scopeField = scope - return '
AAA
' - } - } - }) - let $trs - expect(wrapper).toBeDefined() - expect(wrapper.find('tbody').exists()).toBe(true) - expect(wrapper.findAll('tbody > tr').length).toBe(2) - - $trs = wrapper.findAll('tbody > tr') - expect($trs.length).toBe(2) - expect($trs.at(0).is('tr.b-table-details')).toBe(false) - expect($trs.at(1).is('tr.b-table-details')).toBe(true) - expect($trs.at(1).text()).toBe('foobar') - - // Toggle scoped records via details slot - expect(scopeDetails).not.toBe(null) - scopeDetails.toggleDetails() - await wrapper.vm.$nextTick() - - expect(wrapper.findAll('tbody > tr').length).toBe(1) - - $trs = wrapper.findAll('tbody > tr') - expect($trs.length).toBe(1) - expect($trs.at(0).is('tr.b-table-details')).toBe(false) - - // Toggle scoped records via field slot - expect(scopeField).not.toBe(null) - scopeField.toggleDetails() - await wrapper.vm.$nextTick() - - expect(wrapper.findAll('tbody > tr').length).toBe(2) - - $trs = wrapper.findAll('tbody > tr') - expect($trs.length).toBe(2) - expect($trs.at(0).is('tr.b-table-details')).toBe(false) - expect($trs.at(1).is('tr.b-table-details')).toBe(true) - expect($trs.at(1).text()).toBe('foobar') - - wrapper.destroy() - }) - it('should hide details slot when _showDetails changed', async () => { const testItems = [ { a: 1, b: 2, c: 3, _showDetails: true }, @@ -229,4 +170,63 @@ describe('table row details', () => { wrapper.destroy() }) + + it('should show details slot when slot method toggleDetails() called', async () => { + const testItems = [{ a: 1, b: 2, c: 3, _showDetails: true }] + const testFields = ['a', 'b', 'c'] + let scopeDetails = null + let scopeField = null + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems + }, + scopedSlots: { + 'row-details': function(scope) { + scopeDetails = scope + return 'foobar' + }, + a: function(scope) { + scopeField = scope + return 'AAA' + } + } + }) + let $trs + expect(wrapper).toBeDefined() + expect(wrapper.find('tbody').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(2) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(2) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + expect($trs.at(1).is('tr.b-table-details')).toBe(true) + expect($trs.at(1).text()).toBe('foobar') + + // Toggle details via details slot + expect(scopeDetails).not.toBe(null) + scopeDetails.toggleDetails() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(1) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + + // Toggle details via field slot + expect(scopeField).not.toBe(null) + scopeField.toggleDetails() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(2) + + $trs = wrapper.findAll('tbody > tr') + expect($trs.length).toBe(2) + expect($trs.at(0).is('tr.b-table-details')).toBe(false) + expect($trs.at(1).is('tr.b-table-details')).toBe(true) + expect($trs.at(1).text()).toBe('foobar') + + wrapper.destroy() + }) }) From 63b64f7db52f5616cc9907dbb1ed8fb98e592fa6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:40:48 -0300 Subject: [PATCH 078/159] Update mixin-empty.js --- src/components/table/helpers/mixin-empty.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/helpers/mixin-empty.js b/src/components/table/helpers/mixin-empty.js index 10bcdacb076..dcb396ba006 100644 --- a/src/components/table/helpers/mixin-empty.js +++ b/src/components/table/helpers/mixin-empty.js @@ -54,7 +54,7 @@ export default { { attrs: { colspan: String(this.computedFields.length), - role: this.isStacked ? 'cell' : null + role: 'cell' } }, [h('div', { attrs: { role: 'alert', 'aria-live': 'polite' } }, [$empty])] @@ -69,7 +69,7 @@ export default { ? this.tbodyTrClass(null, 'row-empty') : this.tbodyTrClass ], - attrs: this.isStacked ? { role: 'row' } : {} + attrs: { role: 'row' } }, [$empty] ) From 617d21c58ee2a13b1392dfe47531ce0e80ad419b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:44:12 -0300 Subject: [PATCH 079/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 904ad49b87d..4c3f516125f 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -189,4 +189,38 @@ describe('table > filtering', () => { wrapper.destroy() }) + + it('should be filtered with no rows when no matches', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + filter: 'ZZZZZZZZ' + } + }) + expect(wrapper).toBeDefined() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(0) + + wrapper.destroy() + }) + + it('should show empty filtered message when no matches and show-empty=true', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems, + filter: 'ZZZZZZZZ', + showEmpty: true + } + }) + expect(wrapper).toBeDefined() + await wrapper.vm.$nextTick() + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.find('tbody > tr').text()).toBe(wrapper.vm.emptyFilteredText) + + wrapper.destroy() + }) }) From 2beab5a12b6ff6577fb35c5f02cd4f49f910b43f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:48:05 -0300 Subject: [PATCH 080/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 4c3f516125f..8c1a426ecad 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -220,6 +220,11 @@ describe('table > filtering', () => { expect(wrapper.findAll('tbody > tr').length).toBe(1) expect(wrapper.find('tbody > tr').text()).toBe(wrapper.vm.emptyFilteredText) + expect(wrapper.find('tbody > tr').classes()).toContain('b-table-empty-row') + expect(wrapper.find('tbody > tr').attributes('role')).toBe('row') + expect(wrapper.find('tbody > tr > td').attributes('role')).toBe('cell') + expect(wrapper.find('tbody > tr > td > div').attributes('role')).toBe('alert') + expect(wrapper.find('tbody > tr > td > div').attributes('aria-live')).toBe('polite') wrapper.destroy() }) From b4d846838e2786cea909aa035ffb1db3e78a43ea Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 10:54:43 -0300 Subject: [PATCH 081/159] Update table-filtering.spec.js --- src/components/table/table-filtering.spec.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/table/table-filtering.spec.js b/src/components/table/table-filtering.spec.js index 8c1a426ecad..2f8b67562d8 100644 --- a/src/components/table/table-filtering.spec.js +++ b/src/components/table/table-filtering.spec.js @@ -211,12 +211,18 @@ describe('table > filtering', () => { propsData: { fields: testFields, items: testItems, - filter: 'ZZZZZZZZ', + filter: '', showEmpty: true } }) expect(wrapper).toBeDefined() await wrapper.vm.$nextTick() + expect(wrapper.findAll('tbody > tr').length).toBe(testItems.length) + + wrapper.setProps({ + filter: 'ZZZZZZ' + }) + await wrapper.vm.$nextTick() expect(wrapper.findAll('tbody > tr').length).toBe(1) expect(wrapper.find('tbody > tr').text()).toBe(wrapper.vm.emptyFilteredText) From 00782aec40b2252620ce793acb9bed128a1cfd14 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:01:10 -0300 Subject: [PATCH 082/159] Update table.spec.js --- src/components/table/table.spec.js | 48 ------------------------------ 1 file changed, 48 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 8d5fc81e83b..a20723db2a3 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -340,54 +340,6 @@ describe('table', () => { } }) - it('table_paginated filtering works', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const app = window.app - const spyInput = jest.fn() - const spyFiltered = jest.fn() - - expect(vm.showEmpty).toBe(true) - expect(app.items.length > 10).toBe(true) - expect(app.items.length < 15).toBe(true) - - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - expect(app.items.length > 1).toBe(true) - - vm.$on('input', spyInput) - - // Set page size to max number of items - await setData(app, 'currentPage', 1) - await setData(app, 'perPage', 15) - await nextTick() - expect(vm.value.length).toBe(app.items.length) - expect(tbody.children.length).toBe(app.items.length) - - // Apply Fiter - await setData(app, 'filter', String(app.items[0].name.last)) - await nextTick() - expect(vm.value.length < app.items.length).toBe(true) - expect(tbody.children.length < app.items.length).toBe(true) - - // Empty filter alert - vm.$on('filtered', spyFiltered) - await setData(app, 'filter', 'ZZZZZZZZZZZZZZZZZzzzzzzzzzzzzzzzzz........') - await nextTick() - - expect(spyFiltered).toHaveBeenCalled() - - expect(vm.value.length).toBe(0) - expect(tbody.children.length).toBe(1) - expect(tbody.children[0].children[0].textContent).toContain(vm.emptyFilteredText) - - expect(spyInput).toHaveBeenCalled() - } - }) - it('table_paginated shows empty message when no items', async () => { const { app: { $refs } From 507c06ab7657cc697b40168f58df70911214e645 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:23:01 -0300 Subject: [PATCH 083/159] Update mixin-filtering.js --- src/components/table/helpers/mixin-filtering.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index 5298c6217d9..03c8b67a8ed 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -117,6 +117,13 @@ export default { } } }, + created() { + // Set the initial filtered state. + // In a nextTick so that we trigger a filtered event if needed + this.$nextTick(() => { + this.isFiltered = Boolean(this.localFilter) + }) + }, methods: { // Filter Function factories filterFnFactory(filterFn, criteria) { From 2a633adb9dd321445004644cf3ef4df7506630cb Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:25:51 -0300 Subject: [PATCH 084/159] Update table-provider.spec.js --- src/components/table/table-provider.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-provider.spec.js b/src/components/table/table-provider.spec.js index 228b2ec706b..82a7722d6b8 100644 --- a/src/components/table/table-provider.spec.js +++ b/src/components/table/table-provider.spec.js @@ -12,7 +12,7 @@ const testItems = [ const testFields = Object.keys(testItems[0]).sort() -describe('b-table provider functions', () => { +describe('table > provider functions', () => { it('syncronous items provider works', async () => { function provider(ctx) { return testItems.slice() From 2b617db5f0b3c902a6b6014555675dcaf0f3ad0c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:26:12 -0300 Subject: [PATCH 085/159] Update table-row-details.spec.js --- src/components/table/table-row-details.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-row-details.spec.js b/src/components/table/table-row-details.spec.js index 03e69b5878b..6d3b4a83618 100644 --- a/src/components/table/table-row-details.spec.js +++ b/src/components/table/table-row-details.spec.js @@ -1,7 +1,7 @@ import Table from './table' import { mount } from '@vue/test-utils' -describe('table row details', () => { +describe('table > row details', () => { it('does not show details if slot row-details not defined', async () => { const testItems = [ { a: 1, b: 2, c: 3, _showDetails: true }, From dbfa04d9428087038478e16c87591ce6a6d5e806 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:27:08 -0300 Subject: [PATCH 086/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 6e33efb5906..0ebaed45ae8 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -9,7 +9,7 @@ const testFields = [ { key: 'c', label: 'C', sortable: false } ] -describe('table sorting', () => { +describe('table > sorting', () => { it('should not be sorted by default', async () => { const wrapper = mount(Table, { propsData: { From ec9bdada31978363802d70e80bd903e13d8db1bb Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:27:29 -0300 Subject: [PATCH 087/159] Update table-tfoot-events.spec.js --- src/components/table/table-tfoot-events.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-tfoot-events.spec.js b/src/components/table/table-tfoot-events.spec.js index 4cd1b8d3e59..c00112176d4 100644 --- a/src/components/table/table-tfoot-events.spec.js +++ b/src/components/table/table-tfoot-events.spec.js @@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }] const testFields = [{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }, { key: 'c', label: 'C' }] -describe('table tfoot events', () => { +describe('table > tfoot events', () => { it('should emit head-clicked event when a head cell is clicked', async () => { const wrapper = mount(Table, { propsData: { From b0cbb1a1d32271b9cae4b9b68dddf9a08c87ce39 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:27:48 -0300 Subject: [PATCH 088/159] Update table-thead-events.spec.js --- src/components/table/table-thead-events.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-thead-events.spec.js b/src/components/table/table-thead-events.spec.js index 805bb4d54a7..1ed2b0a2bbc 100644 --- a/src/components/table/table-thead-events.spec.js +++ b/src/components/table/table-thead-events.spec.js @@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }] const testFields = [{ key: 'a', label: 'A' }, { key: 'b', label: 'B' }, { key: 'c', label: 'C' }] -describe('table thead events', () => { +describe('table > thead events', () => { it('should not emit head-clicked event when a head cell is clicked and no head-clicked listener', async () => { const wrapper = mount(Table, { propsData: { From e60afce6749c6c90ed061fe8932f74b28b65ac6b Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:28:47 -0300 Subject: [PATCH 089/159] Update table-busy.spec.js --- src/components/table/table-busy.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-busy.spec.js b/src/components/table/table-busy.spec.js index 1e3153f1ef1..a9d6607d27c 100644 --- a/src/components/table/table-busy.spec.js +++ b/src/components/table/table-busy.spec.js @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] -describe('b-table > busy state', () => { +describe('table > busy state', () => { it('default should have attribute aria-busy=false', async () => { const wrapper = mount(Table, { propsData: { From 859275aa02dba7a2dafa4a5f60a5de4e9770ff91 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:29:26 -0300 Subject: [PATCH 090/159] Update table-caption.spec.js --- src/components/table/table-caption.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-caption.spec.js b/src/components/table/table-caption.spec.js index fa2f1ba9520..b4821129d07 100644 --- a/src/components/table/table-caption.spec.js +++ b/src/components/table/table-caption.spec.js @@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table caption', () => { +describe('table > caption', () => { it('should not have caption by default', async () => { const wrapper = mount(Table, { propsData: { From 7abade79401ed9a7215b7dfa2bf029bb12e54b15 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:29:51 -0300 Subject: [PATCH 091/159] Update table-colgroup.spec.js --- src/components/table/table-colgroup.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-colgroup.spec.js b/src/components/table/table-colgroup.spec.js index 4ddcbfdba52..77bc43ff967 100644 --- a/src/components/table/table-colgroup.spec.js +++ b/src/components/table/table-colgroup.spec.js @@ -5,7 +5,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table colgroup', () => { +describe('table > colgroup', () => { it('should not have colgroup by default', async () => { const wrapper = mount(Table, { propsData: { From 50ec49c035e183017db48c601c9a30f910bc2179 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:30:27 -0300 Subject: [PATCH 092/159] Update table-primarykey.spec.js --- src/components/table/table-primarykey.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-primarykey.spec.js b/src/components/table/table-primarykey.spec.js index fafbf9e00b3..e02300d3731 100644 --- a/src/components/table/table-primarykey.spec.js +++ b/src/components/table/table-primarykey.spec.js @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] -describe('b-table primary key', () => { +describe('table > primary key', () => { it('default should not have ids on table rows', async () => { const wrapper = mount(Table, { propsData: { From ae87ae5d76a2669d025e7e07cb49ce03afe9cccc Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:31:03 -0300 Subject: [PATCH 093/159] Update table-selectable.spec.js --- src/components/table/table-selectable.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-selectable.spec.js b/src/components/table/table-selectable.spec.js index b495d528900..5601c128bb7 100644 --- a/src/components/table/table-selectable.spec.js +++ b/src/components/table/table-selectable.spec.js @@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }] const testFields = [{ key: 'a', sortable: true }] -describe('table row select', () => { +describe('table > row select', () => { it('should not emit row-selected event default', async () => { const wrapper = mount(Table, { propsData: { From a0a55178e23962419b636af050c4a4270967ace6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:31:38 -0300 Subject: [PATCH 094/159] Update table-tbody-row-events.spec.js --- src/components/table/table-tbody-row-events.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-tbody-row-events.spec.js b/src/components/table/table-tbody-row-events.spec.js index 4ea42025199..9ea365c71f2 100644 --- a/src/components/table/table-tbody-row-events.spec.js +++ b/src/components/table/table-tbody-row-events.spec.js @@ -4,7 +4,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table tbody row events', () => { +describe('table > tbody row events', () => { it('should emit row-clicked event when a row is clicked', async () => { const wrapper = mount(Table, { propsData: { From 9ecec77352218687b64e3c8f0e9880c2e28b53ad Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:32:10 -0300 Subject: [PATCH 095/159] Update table-tbody-transition.spec.js --- src/components/table/table-tbody-transition.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-tbody-transition.spec.js b/src/components/table/table-tbody-transition.spec.js index f7f391b21d0..7075af0daea 100644 --- a/src/components/table/table-tbody-transition.spec.js +++ b/src/components/table/table-tbody-transition.spec.js @@ -4,7 +4,7 @@ import { mount, TransitionGroupStub } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table body transition', () => { +describe('table > tbody transition', () => { it('tbody should not be a transition-group component by default', async () => { const wrapper = mount(Table, { attachToDocument: true, From 175cfac3e359222745435a29c42e9f33f8cdbfe3 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:32:50 -0300 Subject: [PATCH 096/159] Update table-thead-top.spec.js --- src/components/table/table-thead-top.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-thead-top.spec.js b/src/components/table/table-thead-top.spec.js index e8d020c45ac..aca91ddefe4 100644 --- a/src/components/table/table-thead-top.spec.js +++ b/src/components/table/table-thead-top.spec.js @@ -5,7 +5,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table thead-top slot', () => { +describe('table > thead thead-top slot', () => { it('should not have thead-top row by default', async () => { const wrapper = mount(Table, { propsData: { From 9c6aee96c1db53436ef95c1c2414dfdab9286111 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:33:23 -0300 Subject: [PATCH 097/159] Update table-top-row.spec.js --- src/components/table/table-top-row.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-top-row.spec.js b/src/components/table/table-top-row.spec.js index aa56b8099be..73398c79933 100644 --- a/src/components/table/table-top-row.spec.js +++ b/src/components/table/table-top-row.spec.js @@ -5,7 +5,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table top-row', () => { +describe('table > tbody top-row slot', () => { it('should not have top row by default', async () => { const wrapper = mount(Table, { propsData: { From 25937bb4c4ab43c9ea16f1e1a2028e8e6e413344 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:33:55 -0300 Subject: [PATCH 098/159] Update and rename table-bottom-row.spec.js to table-tbody-bottom-row.spec.js --- ...{table-bottom-row.spec.js => table-tbody-bottom-row.spec.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/components/table/{table-bottom-row.spec.js => table-tbody-bottom-row.spec.js} (98%) diff --git a/src/components/table/table-bottom-row.spec.js b/src/components/table/table-tbody-bottom-row.spec.js similarity index 98% rename from src/components/table/table-bottom-row.spec.js rename to src/components/table/table-tbody-bottom-row.spec.js index 6e2c04e2360..8f8bb731705 100644 --- a/src/components/table/table-bottom-row.spec.js +++ b/src/components/table/table-tbody-bottom-row.spec.js @@ -5,7 +5,7 @@ import { mount } from '@vue/test-utils' const testItems = [{ a: 1, b: 2, c: 3 }, { a: 5, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }] const testFields = ['a', 'b', 'c'] -describe('table > bottom-row slot', () => { +describe('table > tbody bottom-row slot', () => { it('should not have bottom row by default', async () => { const wrapper = mount(Table, { propsData: { From 29e78ba0e479653a658e8d029c71372118776015 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 11:34:22 -0300 Subject: [PATCH 099/159] Rename table-top-row.spec.js to table-tbody-top-row.spec.js --- .../table/{table-top-row.spec.js => table-tbody-top-row.spec.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/table/{table-top-row.spec.js => table-tbody-top-row.spec.js} (100%) diff --git a/src/components/table/table-top-row.spec.js b/src/components/table/table-tbody-top-row.spec.js similarity index 100% rename from src/components/table/table-top-row.spec.js rename to src/components/table/table-tbody-top-row.spec.js From 9abb2dc1729d117f76f68f4a3ab7306573cfba02 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 12:17:02 -0300 Subject: [PATCH 100/159] Create table-pagination.spec.js --- src/components/table/table-pagination.spec.js | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/components/table/table-pagination.spec.js diff --git a/src/components/table/table-pagination.spec.js b/src/components/table/table-pagination.spec.js new file mode 100644 index 00000000000..c03e9f7223b --- /dev/null +++ b/src/components/table/table-pagination.spec.js @@ -0,0 +1,72 @@ +import Table from './table' +import { mount } from '@vue/test-utils' + +const testItems = [ + { a: 1, b: 2, c: 3 }, + { a: 5, b: 5, c: 6 }, + { a: 7, b: 8, c: 9 }, + { a: 10, b: 11, c: 12 }, + { a: 13, b: 14, c: 15 } +] + +describe('table > pagination', () => { + it('default should not be paginated', async () => { + const wrapper = mount(Table, { + propsData: { + items: testItems + } + }) + expect(wrapper.findAll('tbody > tr').length).toBe(5) + + wrapper.destroy() + }) + + it('should have 3 rows when per-page=3', async () => { + const wrapper = mount(Table, { + propsData: { + items: testItems, + perPage: 3, + currentPage: 1 + } + }) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + const $trs = wrapper.findAll('tbody > tr') + expect($trs.at(0).find('td').text()).toBe('1') + expect($trs.at(1).find('td').text()).toBe('4') + expect($trs.at(2).find('td').text()).toBe('7') + + wrapper.destroy() + }) + + it('changing pages should update rows', async () => { + const wrapper = mount(Table, { + propsData: { + items: testItems, + perPage: 3, + currentPage: 1 + } + }) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + const $trs = wrapper.findAll('tbody > tr') + expect($trs.at(0).find('td').text()).toBe('1') + expect($trs.at(1).find('td').text()).toBe('4') + expect($trs.at(2).find('td').text()).toBe('7') + + wrapper.setProps({ + currentPage: 2 + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(2) + const $trs = wrapper.findAll('tbody > tr') + expect($trs.at(0).find('td').text()).toBe('10') + expect($trs.at(1).find('td').text()).toBe('13') + + wrapper.setProps({ + currentPage: 3 + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(0) + + wrapper.destroy() + }) +}) From e2302bb430d228b4f8605bc919efa3e01ecc653c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 12:25:01 -0300 Subject: [PATCH 101/159] Update table-pagination.spec.js --- src/components/table/table-pagination.spec.js | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/components/table/table-pagination.spec.js b/src/components/table/table-pagination.spec.js index c03e9f7223b..e56e423b9bd 100644 --- a/src/components/table/table-pagination.spec.js +++ b/src/components/table/table-pagination.spec.js @@ -31,9 +31,24 @@ describe('table > pagination', () => { }) expect(wrapper.findAll('tbody > tr').length).toBe(3) const $trs = wrapper.findAll('tbody > tr') - expect($trs.at(0).find('td').text()).toBe('1') - expect($trs.at(1).find('td').text()).toBe('4') - expect($trs.at(2).find('td').text()).toBe('7') + expect( + $trs + .at(0) + .find('td') + .text() + ).toBe('1') + expect( + $trs + .at(1) + .find('td') + .text() + ).toBe('4') + expect( + $trs + .at(2) + .find('td') + .text() + ).toBe('7') wrapper.destroy() }) @@ -47,19 +62,44 @@ describe('table > pagination', () => { } }) expect(wrapper.findAll('tbody > tr').length).toBe(3) - const $trs = wrapper.findAll('tbody > tr') - expect($trs.at(0).find('td').text()).toBe('1') - expect($trs.at(1).find('td').text()).toBe('4') - expect($trs.at(2).find('td').text()).toBe('7') + let $trs = wrapper.findAll('tbody > tr') + expect( + $trs + .at(0) + .find('td') + .text() + ).toBe('1') + expect( + $trs + .at(1) + .find('td') + .text() + ).toBe('4') + expect( + $trs + .at(2) + .find('td') + .text() + ).toBe('7') wrapper.setProps({ currentPage: 2 }) expect(wrapper.findAll('tbody > tr').length).toBe(2) - const $trs = wrapper.findAll('tbody > tr') - expect($trs.at(0).find('td').text()).toBe('10') - expect($trs.at(1).find('td').text()).toBe('13') + $trs = wrapper.findAll('tbody > tr') + expect( + $trs + .at(0) + .find('td') + .text() + ).toBe('10') + expect( + $trs + .at(1) + .find('td') + .text() + ).toBe('13') wrapper.setProps({ currentPage: 3 From 1f3caa71e4cb0db158ed6e61ba822b39ce9de736 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 12:27:13 -0300 Subject: [PATCH 102/159] Update table-pagination.spec.js --- src/components/table/table-pagination.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table-pagination.spec.js b/src/components/table/table-pagination.spec.js index e56e423b9bd..8ad15d7c8ac 100644 --- a/src/components/table/table-pagination.spec.js +++ b/src/components/table/table-pagination.spec.js @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils' const testItems = [ { a: 1, b: 2, c: 3 }, - { a: 5, b: 5, c: 6 }, + { a: 4, b: 5, c: 6 }, { a: 7, b: 8, c: 9 }, { a: 10, b: 11, c: 12 }, { a: 13, b: 14, c: 15 } From 509603de547f5bb570d7ccc5b97b68e463e3ffb3 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 13:15:03 -0300 Subject: [PATCH 103/159] Update table-pagination.spec.js --- src/components/table/table-pagination.spec.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/components/table/table-pagination.spec.js b/src/components/table/table-pagination.spec.js index 8ad15d7c8ac..d42b264d023 100644 --- a/src/components/table/table-pagination.spec.js +++ b/src/components/table/table-pagination.spec.js @@ -109,4 +109,31 @@ describe('table > pagination', () => { wrapper.destroy() }) + + it('setting current-page to more than pages shows empty row when show-empty=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: testItems, + perPage: 3, + currentPage: 1, + showEmpty: true + } + }) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + + wrapper.setProps({ + currentPage: 10 + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + const $tr = wrapper.find('tbody > tr') + expect($tr.text()).toBe(wrapper.vm.emptyText) + expect($tr.classes()).toContain('b-table-empty-row') + expect($tr.attributes('role')).toBe('row') + expect(wrapper.find('tbody > tr > td').attributes('role')).toBe('cell') + expect(wrapper.find('tbody > tr > td > div').attributes('role')).toBe('alert') + expect(wrapper.find('tbody > tr > td > div').attributes('aria-live')).toBe('polite') + + wrapper.destroy() + }) }) From fc64a0e6abc77576645d809c43291d17b0ddbc7d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 13:19:12 -0300 Subject: [PATCH 104/159] Update table.spec.js --- src/components/table/table.spec.js | 128 ----------------------------- 1 file changed, 128 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index a20723db2a3..e2861940cc4 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -54,23 +54,6 @@ describe('table', () => { expect(tr.children.length).toBe(Object.keys(table.items[0]).length - 1) }) - it('table_basic should have thead and tbody', async () => { - const { - app: { $refs } - } = window - - const parts = [...$refs.table_basic.$el.children] - - const thead = parts.find(el => el.tagName && el.tagName === 'THEAD') - expect(thead).toBeDefined() - - const tbody = parts.find(el => el.tagName && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - - const tfoot = parts.find(el => el.tagName && el.tagName === 'TFOOT') - expect(tfoot).not.toBeDefined() - }) - it('table_paginated should have thead, tbody and tfoot', async () => { const { app: { $refs } @@ -127,45 +110,6 @@ describe('table', () => { } }) - it('all examples have correct number of columns', async () => { - const { - app: { $refs } - } = window - - const tables = ['table_basic', 'table_paginated', 'table_dark'] - - tables.forEach((table, idx) => { - const vm = $refs[table] - const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children.length).toBe(Object.keys(vm.fields).length) - } - } - }) - }) - - it('all examples should show the correct number of visible rows', async () => { - const { - app: { $refs } - } = window - const app = window.app - - const tables = ['table_basic', 'table_paginated', 'table_dark'] - - tables.forEach((table, idx) => { - const vm = $refs[table] - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - expect(tbody.children.length).toBe(vm.perPage || app.items.length) - } - }) - }) - it('all examples should have variant "success" on 1st row', async () => { const { app: { $refs } @@ -305,78 +249,6 @@ describe('table', () => { } }) - it('table_paginated pagination works', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const app = window.app - const spy = jest.fn() - - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - // We need between 11 and 14 ites for this test - expect(app.items.length > 10).toBe(true) - expect(app.items.length < 15).toBe(true) - - vm.$on('input', spy) - - // Page size to be less then number of items - await setData(app, 'currentPage', 1) - await setData(app, 'perPage', 10) - await nextTick() - expect(vm.perPage).toBe(10) - expect(vm.value.length).toBe(10) - expect(tbody.children.length).toBe(10) - - // Goto page 2, should have length 1 - await setData(app, 'currentPage', 2) - await nextTick() - expect(vm.value.length).toBe(app.items.length - 10) - expect(tbody.children.length).toBe(app.items.length - 10) - - expect(spy).toHaveBeenCalled() - } - }) - - it('table_paginated shows empty message when no items', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_paginated - const app = window.app - const spy = jest.fn() - - expect(vm.showEmpty).toBe(true) - - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - expect(app.items.length > 10).toBe(true) - expect(app.items.length < 15).toBe(true) - - vm.$on('input', spy) - - // Set page size to show all items - await setData(app, 'currentPage', 1) - await setData(app, 'perPage', 15) - await nextTick() - expect(vm.value.length).toBe(app.items.length) - expect(tbody.children.length).toBe(app.items.length) - - // Set items to empty list - await setData(app, 'items', []) - await nextTick() - expect(app.items.length).toBe(0) - expect(vm.value.length).toBe(0) - expect(tbody.children.length).toBe(1) - expect(tbody.textContent).toContain(vm.emptyText) - - expect(spy).toHaveBeenCalled() - } - }) - it('should render stacked table', async () => { // This test should be a bit more intesive, looking for the data- attributes // and cell inner div wrapper From 5c389ea10ecbd5d65b905af86dd3644894a1503f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 13:30:54 -0300 Subject: [PATCH 105/159] Update form-select.spec.js --- .../form-select/form-select.spec.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/components/form-select/form-select.spec.js b/src/components/form-select/form-select.spec.js index dcc693582ff..27bd8a9924d 100644 --- a/src/components/form-select/form-select.spec.js +++ b/src/components/form-select/form-select.spec.js @@ -433,6 +433,35 @@ describe('form-select', () => { wrapper.destroy() }) + it('updating v-model (value) when selects correct option', async () => { + const wrapper = mount(Select, { + propsData: { + options: ['one', 'two', { text: 'three', value: { three: 3 } }], + value: 'one' + } + }) + const $options = wrapper.findAll('option') + expect($options.length).toBe(3) + + expect($options.at(0).element.selected).toBe(true) + + // select 2nd option + wrapper.setProps({ + value: 'two' + }) + + expect($options.at(1).element.selected).toBe(true) + + // select 3rd option + wrapper.setProps({ + value: { three: 3 } + }) + + expect($options.at(2).element.selected).toBe(true) + + wrapper.destroy() + }) + it('updates v-model when option selected in single mode with complex values', async () => { const wrapper = mount(Select, { propsData: { From 5ec70ad1574f77ad3acdbde9b9ec6659db385e5c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 13:38:12 -0300 Subject: [PATCH 106/159] Update pagination.spec.js --- src/components/pagination/pagination.spec.js | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/components/pagination/pagination.spec.js b/src/components/pagination/pagination.spec.js index 4c4f5a2c5c9..092b74f1f25 100644 --- a/src/components/pagination/pagination.spec.js +++ b/src/components/pagination/pagination.spec.js @@ -134,6 +134,35 @@ describe('pagination', () => { wrapper.destroy() }) + it('renders corerct number of elements when total-rows changes', async () => { + const wrapper = mount(Pagination, { + propsData: { + size: 'sm', + totalRows: 1, + perPage: 1, + limit: 10 + } + }) + expect(wrapper.is('ul')).toBe(true) + expect(wrapper.findAll('li').length).toBe(5) + + wrapper.setProps({ + totalRows: 4 + }) + + expect(wrapper.is('ul')).toBe(true) + expect(wrapper.findAll('li').length).toBe(8) + + wrapper.setProps({ + perPage: 2 + }) + + expect(wrapper.is('ul')).toBe(true) + expect(wrapper.findAll('li').length).toBe(6) + + wrapper.destroy() + }) + it('has class "pagination-sm" when prop size="sm"', async () => { const wrapper = mount(Pagination, { propsData: { From 21d834e27c86c746ee761492ee99f057d70c3bc3 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 13:41:41 -0300 Subject: [PATCH 107/159] Rename table.spec.js to table.old.spec.js --- src/components/table/{table.spec.js => table.old.spec.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/components/table/{table.spec.js => table.old.spec.js} (100%) diff --git a/src/components/table/table.spec.js b/src/components/table/table.old.spec.js similarity index 100% rename from src/components/table/table.spec.js rename to src/components/table/table.old.spec.js From cdb31dc64785a9add377a0aa3521b0f22b8babc2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:08:22 -0300 Subject: [PATCH 108/159] Create table.spec.js --- src/components/table/table.spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/components/table/table.spec.js diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js new file mode 100644 index 00000000000..35ff5ce4a54 --- /dev/null +++ b/src/components/table/table.spec.js @@ -0,0 +1,23 @@ +import Table from './table' +import { mount } from '@vue/test-utils' + +const items1 = [{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }] +const fields1 = ['a', 'b', 'c'] + +describe('table', () => { + it('has expected default classes', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1 + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(2) + }) +}) From 756add96cd602b7c2c41eb067412a90607621f8a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:29:19 -0300 Subject: [PATCH 109/159] Update table.spec.js --- src/components/table/table.spec.js | 218 +++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 35ff5ce4a54..3a824620f06 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -20,4 +20,222 @@ describe('table', () => { expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(2) }) + + it('has class "table-striped" when striped=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + striped: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-striped') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-bordered" when bordered=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + bordered: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-bordered') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-borderless" when borderless=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + borderless: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-borderless') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-hover" when hover=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + hover: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-hover') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-sm" when small=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + small: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-sm') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-dark" when dark=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + dark: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('table-dark') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "border" when outlined=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + outlined: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('border') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "b-table-fixed" when fixed=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + fixed: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('b-table-fixed') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "b-table-stacked" when stacked=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + stacked: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('b-table-stacked') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "b-table-stacked-md" when stacked=md', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + stacked: 'md' + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).toContain('b-table-stacked-md') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) + + it('has class "table-responsive" when responsive=true', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + responsive: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('div')).toBe(true) + expect(wrapper.classes()).toContain('table-responsive') + expect(wrapper.classes().length).toBe(1) + expect(wrapper.find('table').classes()).toContain('table') + expect(wrapper.find('table').classes()).toContain('b-table') + expect(wrapper.find('table').classes().length).toBe(3) + }) + + it('has class "table-responsive-md" when responsive=md', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + responsive: 'md' + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('div')).toBe(true) + expect(wrapper.classes()).toContain('table-responsive-md') + expect(wrapper.classes().length).toBe(1) + expect(wrapper.find('table').classes()).toContain('table') + expect(wrapper.find('table').classes()).toContain('b-table') + expect(wrapper.find('table').classes().length).toBe(3) + }) }) From 10039613e746c9c5228aadb3a4bf056a52961e97 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:32:16 -0300 Subject: [PATCH 110/159] Update table.spec.js --- src/components/table/table.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 3a824620f06..6d0e00acb9e 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -217,7 +217,7 @@ describe('table', () => { expect(wrapper.classes().length).toBe(1) expect(wrapper.find('table').classes()).toContain('table') expect(wrapper.find('table').classes()).toContain('b-table') - expect(wrapper.find('table').classes().length).toBe(3) + expect(wrapper.find('table').classes().length).toBe(2) }) it('has class "table-responsive-md" when responsive=md', async () => { @@ -236,6 +236,6 @@ describe('table', () => { expect(wrapper.classes().length).toBe(1) expect(wrapper.find('table').classes()).toContain('table') expect(wrapper.find('table').classes()).toContain('b-table') - expect(wrapper.find('table').classes().length).toBe(3) + expect(wrapper.find('table').classes().length).toBe(2) }) }) From b1ec6b30655afd7b1868bcfb9681a89dc84666b3 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:36:17 -0300 Subject: [PATCH 111/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 114 ------------------------- 1 file changed, 114 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index e2861940cc4..37faae12359 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -4,90 +4,6 @@ describe('table', () => { beforeEach(loadFixture(__dirname, 'table')) testVM() - it('all example tables should contain class names', async () => { - const { - app: { $refs } - } = window - - expect($refs.table_basic).toHaveAllClasses(['table', 'b-table', 'table-striped', 'table-hover']) - - expect($refs.table_paginated).toHaveAllClasses([ - 'table', - 'b-table', - 'table-sm', - 'table-striped', - 'table-bordered', - 'table-hover' - ]) - - expect($refs.table_dark).toHaveAllClasses([ - 'table', - 'b-table', - 'table-sm', - 'table-bordered', - 'table-dark' - ]) - }) - - it('table_responsive should be wrapped in a div', async () => { - const { - app: { $refs } - } = window - const table = $refs.table_responsive - - expect(table.$el.tagName).toBe('DIV') - expect(table).toHaveAllClasses(['table-responsive']) - expect(table.$el.children.length).toBe(1) - expect(table.$el.children[0].tagName).toBe('TABLE') - }) - - it('should generate fields automatically from the first item', async () => { - const { - app: { $refs } - } = window - const table = $refs.table_without_fields - const thead = $refs.table_without_fields.$el.children[0] - const tr = thead.children[0] - - // The row should be equal to the items without any of Bootstrap Vue's - // utility fields, like _rowVariant, or _cellVariants - expect(tr.children.length).toBe(Object.keys(table.items[0]).length - 1) - }) - - it('table_paginated should have thead, tbody and tfoot', async () => { - const { - app: { $refs } - } = window - - const parts = [...$refs.table_paginated.$el.children] - - const thead = parts.find(el => el.tagName && el.tagName === 'THEAD') - expect(thead).toBeDefined() - - const tbody = parts.find(el => el.tagName && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - - const tfoot = parts.find(el => el.tagName && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - }) - - it('table_dark should have thead and tbody', async () => { - const { - app: { $refs } - } = window - - const parts = [...$refs.table_dark.$el.children] - - const thead = parts.find(el => el.tagName && el.tagName === 'THEAD') - expect(thead).toBeDefined() - - const tbody = parts.find(el => el.tagName && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - - const tfoot = parts.find(el => el.tagName && el.tagName === 'TFOOT') - expect(tfoot).not.toBeDefined() - }) - it('table_paginated thead should contain class thead-dark', async () => { const { app: { $refs } @@ -154,27 +70,6 @@ describe('table', () => { } }) - it('table_paginated should contain custom formatted columns', async () => { - const { app } = window - const vm = app.$refs.table_basic - - const tbody = [...app.$refs.table_paginated.$el.children].find( - el => el && el.tagName === 'TBODY' - ) - expect(tbody).toBeDefined() - if (tbody) { - const tr = [...tbody.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].textContent).toContain( - vm.items[0].name.first + ' ' + vm.items[0].name.last - ) - expect(tr.children[1].textContent).toContain(String(vm.items[0].age)) - expect(tr.children[3].children[0].tagName).toBe('INPUT') - } - } - }) - it('table_paginated should contain custom formatted headers', async () => { const { app: { $refs } @@ -249,15 +144,6 @@ describe('table', () => { } }) - it('should render stacked table', async () => { - // This test should be a bit more intesive, looking for the data- attributes - // and cell inner div wrapper - const { app } = window - const vm = app.$refs.table_stacked - - expect(vm).toHaveAllClasses(['b-table-stacked']) - }) - it('all example tables should have custom formatted cells', async () => { const { app: { $refs } From e629ea3c5d309b2dbaf505540403f8ec5603d319 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:41:43 -0300 Subject: [PATCH 112/159] Update table.spec.js --- src/components/table/table.spec.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 6d0e00acb9e..daf39a0a53b 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -238,4 +238,24 @@ describe('table', () => { expect(wrapper.find('table').classes()).toContain('b-table') expect(wrapper.find('table').classes().length).toBe(2) }) + + it('stacked has precedence over responsive', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + stacked: true, + responsive: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.is(Table)).toBe(true) + expect(wrapper.is('table')).toBe(true) + expect(wrapper.classes()).not.toContain('table-responsive') + expect(wrapper.classes()).toContain('b-table-stacked') + expect(wrapper.classes()).toContain('table') + expect(wrapper.classes()).toContain('b-table') + expect(wrapper.classes().length).toBe(3) + }) }) From 53bc416537fa836e2794cf4bd07baca86e307473 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:52:17 -0300 Subject: [PATCH 113/159] Update table.spec.js --- src/components/table/table.spec.js | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index daf39a0a53b..4ee5ddc172f 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -258,4 +258,48 @@ describe('table', () => { expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) }) + + it('item _rowVariant works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, _rowVariant: 'primary'}], + fields: ['a'], + dark: false + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.find('tbody > tr').classes()).toContain('table-primary') + + wrapper.setProps({ + dark: true + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.find('tbody > tr').classes()).toContain('bg-primary') + }) + + it('item _cellVariants works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, _cellVariants: { a: 'info' } }], + fields: ['a'], + dark: false + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(1) + expect(wrapper.find('tbody > tr > td').classes()).toContain('table-info') + + wrapper.setProps({ + dark: true + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(1) + expect(wrapper.find('tbody > tr > td').classes()).toContain('bg-info') + }) }) From 3516d26f4e7e2c3328374afaff0e4ed0b702e8a6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:54:09 -0300 Subject: [PATCH 114/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 4ee5ddc172f..c573ac4df7f 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -262,7 +262,7 @@ describe('table', () => { it('item _rowVariant works', async () => { const wrapper = mount(Table, { propsData: { - items: [{ a: 1, _rowVariant: 'primary'}], + items: [{ a: 1, _rowVariant: 'primary' }], fields: ['a'], dark: false } From 10694c2317c64cd61a9c13f9d95287802bab0783 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:58:40 -0300 Subject: [PATCH 115/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 37faae12359..6d59b001401 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -26,31 +26,6 @@ describe('table', () => { } }) - it('all examples should have variant "success" on 1st row', async () => { - const { - app: { $refs } - } = window - const app = window.app - - const tables = ['table_basic', 'table_paginated', 'table_dark'] - - const items = app.items.slice() - items[0]._rowVariant = 'success' - await setData(app, 'items', items) - await nextTick() - - tables.forEach((table, idx) => { - const vm = $refs[table] - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - const tr = tbody.children[0] - const variant = vm.dark ? 'bg-success' : 'table-success' - expect(Boolean(tr) && Boolean(tr.classList) && tr.classList.contains(variant)).toBe(true) - } - }) - }) - it('table_basic should contain custom formatted columns', async () => { const { app } = window const vm = app.$refs.table_basic From 264fe075c467ddd9a771b266f3f1c2cdb793a041 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 14:59:56 -0300 Subject: [PATCH 116/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 6d59b001401..6cab4390a78 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -1,4 +1,4 @@ -import { loadFixture, testVM, setData, nextTick } from '../../../tests/utils' +import { loadFixture, testVM, nextTick } from '../../../tests/utils' describe('table', () => { beforeEach(loadFixture(__dirname, 'table')) From 8f8dd7c9a1744f56b368fc2b38cebd70ee75fd86 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:15:11 -0300 Subject: [PATCH 117/159] Update table.spec.js --- src/components/table/table.spec.js | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index c573ac4df7f..52a0c37e5b1 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -302,4 +302,36 @@ describe('table', () => { expect(wrapper.findAll('tbody > tr > td').length).toBe(1) expect(wrapper.find('tbody > tr > td').classes()).toContain('bg-info') }) + + it('tbody-tr-class works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, b: 2 }, { a: 3, b: 4 }], + fields: ['a', 'b'], + tbodyTrClass: 'foobar' + } + }) + + expect(wrapper).toBeDefined() + + // prop as a string + expect(wrapper.findAll('tbody > tr').length).toBe(2) + const $trs = wrapper.find('tbody > tr') + expect($trs.at(0).classes()).toContain('foobar') + expect($trs.at(1).classes()).toContain('foobar') + + // As a function + wrapper.setProps({ + tbodyTrClass: (item) => { + return item.a === 1 ? 'foo' : 'bar' + } + }) + + expect(wrapper.findAll('tbody > tr').length).toBe(2) + const $trs = wrapper.find('tbody > tr') + expect($trs.at(0).classes()).toContain('foo') + expect($trs.at(0).classes()).not.toContain('bar') + expect($trs.at(1).classes()).toContain('bar') + expect($trs.at(1).classes()).not.toContain('foo') + }) }) From 1d505169a5623b7234cad27ae1437dccad5410a4 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:17:34 -0300 Subject: [PATCH 118/159] Update table.spec.js --- src/components/table/table.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 52a0c37e5b1..cee8597e8f7 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -316,19 +316,19 @@ describe('table', () => { // prop as a string expect(wrapper.findAll('tbody > tr').length).toBe(2) - const $trs = wrapper.find('tbody > tr') + let $trs = wrapper.find('tbody > tr') expect($trs.at(0).classes()).toContain('foobar') expect($trs.at(1).classes()).toContain('foobar') // As a function wrapper.setProps({ - tbodyTrClass: (item) => { + tbodyTrClass: item => { return item.a === 1 ? 'foo' : 'bar' } }) expect(wrapper.findAll('tbody > tr').length).toBe(2) - const $trs = wrapper.find('tbody > tr') + $trs = wrapper.find('tbody > tr') expect($trs.at(0).classes()).toContain('foo') expect($trs.at(0).classes()).not.toContain('bar') expect($trs.at(1).classes()).toContain('bar') From e8c2c40d246123e37726cf85c2200765231d6d99 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:19:34 -0300 Subject: [PATCH 119/159] Update table.spec.js --- src/components/table/table.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index cee8597e8f7..444a2ac76d5 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -316,7 +316,7 @@ describe('table', () => { // prop as a string expect(wrapper.findAll('tbody > tr').length).toBe(2) - let $trs = wrapper.find('tbody > tr') + let $trs = wrapper.findAll('tbody > tr') expect($trs.at(0).classes()).toContain('foobar') expect($trs.at(1).classes()).toContain('foobar') @@ -328,7 +328,7 @@ describe('table', () => { }) expect(wrapper.findAll('tbody > tr').length).toBe(2) - $trs = wrapper.find('tbody > tr') + $trs = wrapper.findAll('tbody > tr') expect($trs.at(0).classes()).toContain('foo') expect($trs.at(0).classes()).not.toContain('bar') expect($trs.at(1).classes()).toContain('bar') From 5ca5163e806deb2be356db82dab63077f6a642c4 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:24:27 -0300 Subject: [PATCH 120/159] Update table.spec.js --- src/components/table/table.spec.js | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 444a2ac76d5..7226fb18d4f 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -19,6 +19,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(2) + + wrapper.destroy() }) it('has class "table-striped" when striped=true', async () => { @@ -37,6 +39,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-bordered" when bordered=true', async () => { @@ -55,6 +59,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-borderless" when borderless=true', async () => { @@ -73,6 +79,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-hover" when hover=true', async () => { @@ -91,6 +99,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-sm" when small=true', async () => { @@ -109,6 +119,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-dark" when dark=true', async () => { @@ -127,6 +139,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "border" when outlined=true', async () => { @@ -145,6 +159,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "b-table-fixed" when fixed=true', async () => { @@ -163,6 +179,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "b-table-stacked" when stacked=true', async () => { @@ -181,6 +199,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "b-table-stacked-md" when stacked=md', async () => { @@ -199,6 +219,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('has class "table-responsive" when responsive=true', async () => { @@ -218,6 +240,8 @@ describe('table', () => { expect(wrapper.find('table').classes()).toContain('table') expect(wrapper.find('table').classes()).toContain('b-table') expect(wrapper.find('table').classes().length).toBe(2) + + wrapper.destroy() }) it('has class "table-responsive-md" when responsive=md', async () => { @@ -237,6 +261,8 @@ describe('table', () => { expect(wrapper.find('table').classes()).toContain('table') expect(wrapper.find('table').classes()).toContain('b-table') expect(wrapper.find('table').classes().length).toBe(2) + + wrapper.destroy() }) it('stacked has precedence over responsive', async () => { @@ -257,6 +283,8 @@ describe('table', () => { expect(wrapper.classes()).toContain('table') expect(wrapper.classes()).toContain('b-table') expect(wrapper.classes().length).toBe(3) + + wrapper.destroy() }) it('item _rowVariant works', async () => { @@ -278,6 +306,8 @@ describe('table', () => { expect(wrapper.findAll('tbody > tr').length).toBe(1) expect(wrapper.find('tbody > tr').classes()).toContain('bg-primary') + + wrapper.destroy() }) it('item _cellVariants works', async () => { @@ -301,6 +331,28 @@ describe('table', () => { expect(wrapper.findAll('tbody > tr').length).toBe(1) expect(wrapper.findAll('tbody > tr > td').length).toBe(1) expect(wrapper.find('tbody > tr > td').classes()).toContain('bg-info') + + wrapper.destroy() + }) + + it('changing items array works', async () => { + const items1 = [{ a: 1, b: 2 }, { a: 3, b: 4 }] + const items2 = [{ a: 3, b: 4 }] + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: ['a', 'b'] + } + }) + expect(wrapper).toBeDefined() + + expect(wrapper.findAll('tbody > tr').length).toBe(2) + wrapper.setProps({ + items: items2 + }) + expect(wrapper.findAll('tbody > tr').length).toBe(1) + + wrapper.destroy() }) it('tbody-tr-class works', async () => { @@ -333,5 +385,7 @@ describe('table', () => { expect($trs.at(0).classes()).not.toContain('bar') expect($trs.at(1).classes()).toContain('bar') expect($trs.at(1).classes()).not.toContain('foo') + + wrapper.destroy() }) }) From ceb7e2b1894d373d46820646e8f6866d5f0a998a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:26:41 -0300 Subject: [PATCH 121/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 6cab4390a78..4c8d6717df9 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -124,7 +124,7 @@ describe('table', () => { app: { $refs } } = window - const tables = ['table_basic', 'table_paginated', 'table_dark'] + const tables = ['table_basic', 'table_paginated'] await nextTick() tables.forEach((table, idx) => { @@ -174,27 +174,4 @@ describe('table', () => { } }) }) - - it('should set row classes', async () => { - // Classes that children rows must contain - const classesTest = { - 'tr-start-with-l': [1, 7], - 'tr-last-name-macdonald': [0, 6] - } - const { app } = window - const vm = app.$refs.table_style_row - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - for (const className in classesTest) { - const children = classesTest[className] - for (let childIndex = 0, len = tbody.children.length - 1; childIndex < len; ++childIndex) { - const hasClass = children.indexOf(childIndex) >= 0 - expect( - Boolean(tbody.children[childIndex]) && - Boolean(tbody.children[childIndex].classList) && - tbody.children[childIndex].classList.contains(className) - ).toBe(hasClass) - } - } - }) }) From 771c712cfc3332eaef8f73e36c1810cdff5fcf57 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:27:52 -0300 Subject: [PATCH 122/159] Update table.html --- src/components/table/fixtures/table.html | 76 ------------------------ 1 file changed, 76 deletions(-) diff --git a/src/components/table/fixtures/table.html b/src/components/table/fixtures/table.html index e81446a62eb..8e0cf15751b 100644 --- a/src/components/table/fixtures/table.html +++ b/src/components/table/fixtures/table.html @@ -13,24 +13,6 @@

Basic table

- - - - - - - - - - - - Table Caption - - - -

Paginated Table

@@ -87,62 +69,4 @@

Paginated Table

-

Dark Table

-
-
- -
-
- - - - - - -

Provider Test Table

-
- - - -
- - - - - - - From 651f1ea1285a04def5e1f4c22f155011a9ac8fb2 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 15:36:56 -0300 Subject: [PATCH 123/159] Create startcase.spec.js --- src/utils/startcase.spec.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/utils/startcase.spec.js diff --git a/src/utils/startcase.spec.js b/src/utils/startcase.spec.js new file mode 100644 index 00000000000..0c17bdd78c9 --- /dev/null +++ b/src/utils/startcase.spec.js @@ -0,0 +1,11 @@ +import startCase from './startcase' + +describe('utils/startcase', () => { + it('works', async () => { + expect(startCase('foobar')).toBe('Foobar') + expect(startCase('Foobar')).toBe('Foobar') + expect(startCase('foo_bar')).toBe('Foo Bar') + expect(startCase('foo bar')).toBe('Foo Bar') + expect(startCase('fooBar')).toBe('Foo Bar') + }) +}) From 7ad6f5e9c7926731a768162bb14b645000eeff6e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:32:45 -0300 Subject: [PATCH 124/159] Update table.spec.js --- src/components/table/table.spec.js | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 7226fb18d4f..51aa685c520 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -388,4 +388,55 @@ describe('table', () => { wrapper.destroy() }) + + it('thead and tfoot variant and classes work', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, b: 2 }], + fields: ['a', 'b'], + foodClone: true + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('thead > tr').length).toBe(1) + expect(wrapper.findAll('tfoot > tr').length).toBe(1) + + expect(wrapper.find('thead').classes().length).toBe(0) + expect(wrapper.find('tfoot').classes().length).toBe(0) + + wrapper.setProps({ + headVariant: 'light' + }) + + expect(wrapper.find('thead').classes()).toContain('thead-light') + expect(wrapper.find('tfoot').classes()).toContain('thead-light') + + wrapper.setProps({ + footVariant: 'dark' + }) + + expect(wrapper.find('thead').classes()).toContain('thead-light') + expect(wrapper.find('tfoot').classes()).toContain('thead-dark') + + wrapper.setProps({ + theadClass: 'foo', + tfootClass: 'bar' + }) + + expect(wrapper.find('thead').classes()).toContain('thead-light') + expect(wrapper.find('thead').classes()).toContain('foo') + expect(wrapper.find('tfoot').classes()).toContain('thead-dark') + expect(wrapper.find('tfoot').classes()).toContain('bar') + + wrapper.setProps({ + theadTrClass: 'willie', + tfoorTrClass: 'wonka' + }) + + expect(wrapper.find('thead > tr').classes()).toContain('willy') + expect(wrapper.find('tfoor > tr').classes()).toContain('wonka') + + wrapper.destroy() + }) }) From b7baa0f556fddde47136a8aa4f3cd5a7dea4be5d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:37:36 -0300 Subject: [PATCH 125/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 51aa685c520..18036cc152c 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -394,7 +394,7 @@ describe('table', () => { propsData: { items: [{ a: 1, b: 2 }], fields: ['a', 'b'], - foodClone: true + footClone: true } }) From 935806baf333e2e9ecb2d77a7f9e52a02e9a27c6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:40:27 -0300 Subject: [PATCH 126/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 18036cc152c..8e438a767e3 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -430,7 +430,7 @@ describe('table', () => { expect(wrapper.find('tfoot').classes()).toContain('bar') wrapper.setProps({ - theadTrClass: 'willie', + theadTrClass: 'willy', tfoorTrClass: 'wonka' }) From 4225a5b6bd5a0197d40861858d200ed37307661e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:42:02 -0300 Subject: [PATCH 127/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 8e438a767e3..6d2346a954d 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -435,7 +435,7 @@ describe('table', () => { }) expect(wrapper.find('thead > tr').classes()).toContain('willy') - expect(wrapper.find('tfoor > tr').classes()).toContain('wonka') + expect(wrapper.find('tfoot > tr').classes()).toContain('wonka') wrapper.destroy() }) From 31087c5b94eb0690cdcbfc469d32fb943aa2d9f5 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:42:28 -0300 Subject: [PATCH 128/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 4c8d6717df9..017f41e8e2c 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -4,28 +4,6 @@ describe('table', () => { beforeEach(loadFixture(__dirname, 'table')) testVM() - it('table_paginated thead should contain class thead-dark', async () => { - const { - app: { $refs } - } = window - const thead = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - expect(thead.classList.contains('thead-dark')).toBe(true) - } - }) - - it('table_paginated tfoot should contain class thead-light', async () => { - const { - app: { $refs } - } = window - const tfoot = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - if (tfoot) { - expect(tfoot.classList.contains('thead-light')).toBe(true) - } - }) - it('table_basic should contain custom formatted columns', async () => { const { app } = window const vm = app.$refs.table_basic From dd0042a015da94f05fc8074fe1c4257acd8fa198 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:50:23 -0300 Subject: [PATCH 129/159] Update mixin-thead.js --- src/components/table/helpers/mixin-thead.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/helpers/mixin-thead.js b/src/components/table/helpers/mixin-thead.js index ff03ecbd9e0..b342252d77d 100644 --- a/src/components/table/helpers/mixin-thead.js +++ b/src/components/table/helpers/mixin-thead.js @@ -117,7 +117,7 @@ export default { // Genrate the row(s) const $trs = [] if (isFoot) { - $trs.push(h('tr', { class: this.tfootTrClass }, $cells)) + $trs.push(h('tr', { class: this.tfootTrClass, attrs: { role: 'row' } }, $cells)) } else { const scope = { columns: fields.length, From b55acc84d08a936680e713cef79c0dcfc06828ac Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:52:46 -0300 Subject: [PATCH 130/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 6d2346a954d..a3976af66df 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -431,7 +431,7 @@ describe('table', () => { wrapper.setProps({ theadTrClass: 'willy', - tfoorTrClass: 'wonka' + tfootTrClass: 'wonka' }) expect(wrapper.find('thead > tr').classes()).toContain('willy') From f5a02e231e0740251e96534917f6c83c11e280f6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 16:56:37 -0300 Subject: [PATCH 131/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 0ebaed45ae8..04132966ac8 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -241,6 +241,7 @@ describe('table > sorting', () => { await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed')).toBeDefined() expect(wrapper.emitted('sort-changed').length).toBe(1) + expect(wrapper.emitted('sort-changed')[0][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -261,6 +262,7 @@ describe('table > sorting', () => { .trigger('click') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(2) + expect(wrapper.emitted('sort-changed')[1][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -281,6 +283,7 @@ describe('table > sorting', () => { .trigger('keydown.enter') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(3) + expect(wrapper.emitted('sort-changed')[2][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -301,6 +304,7 @@ describe('table > sorting', () => { .trigger('click') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(4) + expect(wrapper.emitted('sort-changed')[3][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -360,6 +364,7 @@ describe('table > sorting', () => { await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed')).toBeDefined() expect(wrapper.emitted('sort-changed').length).toBe(1) + expect(wrapper.emitted('sort-changed')[0][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -383,6 +388,7 @@ describe('table > sorting', () => { .trigger('click') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(2) + expect(wrapper.emitted('sort-changed')[1][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -406,6 +412,7 @@ describe('table > sorting', () => { .trigger('keydown.enter') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(3) + expect(wrapper.emitted('sort-changed')[2][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value @@ -426,6 +433,7 @@ describe('table > sorting', () => { .trigger('click') await wrapper.vm.$nextTick() expect(wrapper.emitted('sort-changed').length).toBe(4) + expect(wrapper.emitted('sort-changed')[3][0]).toEqual(wrapper.vm.context) $rows = wrapper.findAll('tbody > tr').wrappers expect($rows.length).toBe(3) // Map the rows to the column text value From 2c6d7e6c77e477fd45bd0fe1bf334efb758a09ea Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:04:31 -0300 Subject: [PATCH 132/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 04132966ac8..4436e6b90bf 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -622,4 +622,78 @@ describe('table > sorting', () => { wrapper.destroy() }) + + it('non-sortable header th should not emit a sort-changed event when clicked and prop no-sort-reset is set', async () => { + const wrapper = mount(Table, { + propsData: { + fields: testFields, + items: testItems + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').exists()).toBe(true) + expect(wrapper.findAll('tbody > tr').length).toBe(3) + let $rows + let columnA + + // Should not be sorted + await wrapper.vm.$nextTick() + expect(wrapper.emitted('input')).toBeDefined() + expect(wrapper.emitted('sort-changed')).not.toBeDefined() + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the first column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('3') + expect(columnA[1]).toBe('1') + expect(columnA[2]).toBe('2') + + // Click first column to sort + wrapper + .findAll('thead > tr > th') + .at(0) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed')).toBeDefined() + expect(wrapper.emitted('sort-changed').length).toBe(1) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('1') + expect(columnA[1]).toBe('2') + expect(columnA[2]).toBe('3') + + // Click third column header (should not clear sorting) + wrapper + .findAll('thead > tr > th') + .at(2) + .trigger('click') + await wrapper.vm.$nextTick() + expect(wrapper.emitted('sort-changed').length).toBe(1) + $rows = wrapper.findAll('tbody > tr').wrappers + expect($rows.length).toBe(3) + // Map the rows to the column text value + columnA = $rows.map(row => { + return row + .findAll('td') + .at(0) + .text() + }) + expect(columnA[0]).toBe('1') + expect(columnA[1]).toBe('2') + expect(columnA[2]).toBe('3') + + wrapper.destroy() + }) }) From e6a8b339514b1cb18123943daadb0b85e3a996e6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:05:05 -0300 Subject: [PATCH 133/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 34 -------------------------- 1 file changed, 34 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 017f41e8e2c..60fdc8a4ef3 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -63,40 +63,6 @@ describe('table', () => { } }) - it('non-sortable header th should not emit a sort-changed event when clicked and prop no-sort-reset is set', async () => { - const { - app: { $refs } - } = window - const vm = $refs.table_no_sort_reset - const spy = jest.fn() - const fieldKeys = Object.keys(vm.fields) - - vm.$on('sort-changed', spy) - const thead = [...vm.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - let sortBy = null - const ths = [...tr.children] - expect(ths.length).toBe(fieldKeys.length) - ths.forEach((th, idx) => { - th.click() - if (vm.fields[fieldKeys[idx]].sortable) { - expect(spy).toHaveBeenCalledWith(vm.context) - expect(vm.context.sortBy).toBe(fieldKeys[idx]) - sortBy = vm.context.sortBy - } else { - expect(spy).not.toHaveBeenCalled() - expect(vm.context.sortBy).toBe(sortBy) - } - spy.mockClear() - }) - } - } - }) - it('all example tables should have custom formatted cells', async () => { const { app: { $refs } From e521494c793e87f8c4c0971d4fa21adeac15cdc4 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:06:13 -0300 Subject: [PATCH 134/159] Update table.html --- src/components/table/fixtures/table.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/table/fixtures/table.html b/src/components/table/fixtures/table.html index 8e0cf15751b..8d6d27d3fa0 100644 --- a/src/components/table/fixtures/table.html +++ b/src/components/table/fixtures/table.html @@ -13,8 +13,6 @@

Basic table

- -

Paginated Table

From 6208aabac6ce04ff4d5484ae7937ca478eece9bd Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:07:15 -0300 Subject: [PATCH 135/159] Update table-sorting.spec.js --- src/components/table/table-sorting.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/table/table-sorting.spec.js b/src/components/table/table-sorting.spec.js index 4436e6b90bf..eeadee28d84 100644 --- a/src/components/table/table-sorting.spec.js +++ b/src/components/table/table-sorting.spec.js @@ -627,7 +627,8 @@ describe('table > sorting', () => { const wrapper = mount(Table, { propsData: { fields: testFields, - items: testItems + items: testItems, + noSortReset: true } }) expect(wrapper).toBeDefined() From 64b8b085cbe2be8643766267db4dafe7b52b1b08 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:18:02 -0300 Subject: [PATCH 136/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 96 -------------------------- 1 file changed, 96 deletions(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index 60fdc8a4ef3..b75cedcaab5 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -22,100 +22,4 @@ describe('table', () => { } } }) - - it('table_paginated should contain custom formatted headers', async () => { - const { - app: { $refs } - } = window - - const thead = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === 'THEAD') - expect(thead).toBeDefined() - if (thead) { - const tr = [...thead.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].textContent).toContain('Person Full name') - expect(tr.children[1].textContent).toContain('Person age') - expect(tr.children[2].textContent).toContain('is Active') - expect(tr.children[3].textContent).toContain('Select') - } - } - }) - - it('table_paginated should contain custom formatted footers', async () => { - const { - app: { $refs } - } = window - - await nextTick() - - const tfoot = [...$refs.table_paginated.$el.children].find(el => el && el.tagName === 'TFOOT') - expect(tfoot).toBeDefined() - if (tfoot) { - const tr = [...tfoot.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].textContent).toContain('Showing 5 People') - expect(tr.children[1].textContent).toContain('Person age') - expect(tr.children[2].textContent).toContain('is Active') - expect(tr.children[3].textContent).toContain('Selected: 0') - } - } - }) - - it('all example tables should have custom formatted cells', async () => { - const { - app: { $refs } - } = window - - const tables = ['table_basic', 'table_paginated'] - await nextTick() - - tables.forEach((table, idx) => { - const vm = $refs[table] - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - const tr = tbody.children[0] - expect(tr).toBeDefined() - expect( - Boolean(tr.children[0]) && - Boolean(tr.children[0].classList) && - tr.children[0].classList.contains('bg-primary') - ).toBe(true) - expect( - Boolean(tr.children[1]) && - Boolean(tr.children[1].classList) && - tr.children[1].classList.contains('bg-primary') && - tr.children[1].classList.contains('text-dark') - ).toBe(true) - expect( - Boolean(tr.children[2]) && - Boolean(tr.children[2].classList) && - tr.children[2].classList.contains('bg-danger') - ).toBe(true) - expect( - Boolean(tr.children[3]) && - Boolean(tr.children[3].classList) && - tr.children[3].classList.contains('bg-primary') && - tr.children[3].classList.contains('text-light') - ).toBe(true) - expect( - Boolean(tr.children[0]) && - Boolean(tr.children[0].attributes) && - tr.children[0].getAttribute('title') === 'Person Full name' - ).toBe(true) - expect( - Boolean(tr.children[2]) && - Boolean(tr.children[2].attributes) && - tr.children[2].getAttribute('title') === 'is Active' - ).toBe(true) - expect( - Boolean(tr.children[3]) && - Boolean(tr.children[3].attributes) && - tr.children[3].getAttribute('title') === 'Actions' - ).toBe(true) - } - }) - }) }) From c8d4dfc9a7d6acf0279bb628bdf99b65a0587f29 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:18:35 -0300 Subject: [PATCH 137/159] Update table.html --- src/components/table/fixtures/table.html | 55 ------------------------ 1 file changed, 55 deletions(-) diff --git a/src/components/table/fixtures/table.html b/src/components/table/fixtures/table.html index 8d6d27d3fa0..0afb35ba1ed 100644 --- a/src/components/table/fixtures/table.html +++ b/src/components/table/fixtures/table.html @@ -12,59 +12,4 @@

Basic table

Details - -

Paginated Table

-
-
- - - - -
-
- - - -
-
- -
-
- - - - - - - - -
From 4aab83b5a1505b39c56ae24b4b044260d6254331 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:20:18 -0300 Subject: [PATCH 138/159] Update table.old.spec.js --- src/components/table/table.old.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js index b75cedcaab5..f51b601f8ea 100644 --- a/src/components/table/table.old.spec.js +++ b/src/components/table/table.old.spec.js @@ -1,4 +1,4 @@ -import { loadFixture, testVM, nextTick } from '../../../tests/utils' +import { loadFixture, testVM } from '../../../tests/utils' describe('table', () => { beforeEach(loadFixture(__dirname, 'table')) From e0086bd4a533f8f1d85a7548a37c1f5846fd0981 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 17:47:18 -0300 Subject: [PATCH 139/159] Delete table.old.spec.js --- src/components/table/table.old.spec.js | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/components/table/table.old.spec.js diff --git a/src/components/table/table.old.spec.js b/src/components/table/table.old.spec.js deleted file mode 100644 index f51b601f8ea..00000000000 --- a/src/components/table/table.old.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -import { loadFixture, testVM } from '../../../tests/utils' - -describe('table', () => { - beforeEach(loadFixture(__dirname, 'table')) - testVM() - - it('table_basic should contain custom formatted columns', async () => { - const { app } = window - const vm = app.$refs.table_basic - - const tbody = [...vm.$el.children].find(el => el && el.tagName === 'TBODY') - expect(tbody).toBeDefined() - if (tbody) { - const tr = [...tbody.children].find(el => el && el.tagName === 'TR') - expect(tr).toBeDefined() - if (tr) { - expect(tr.children[0].textContent).toContain( - vm.items[0].name.first + ' ' + vm.items[0].name.last - ) - expect(tr.children[1].textContent).toContain(String(vm.items[0].age)) - expect(tr.children[3].children[0].tagName).toBe('BUTTON') - } - } - }) -}) From a6028935f49542513e4306c4c5e1440ad1e3c400 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 18:39:51 -0300 Subject: [PATCH 140/159] Update table.spec.js --- src/components/table/table.spec.js | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index a3976af66df..3c1b71e23d5 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -439,4 +439,57 @@ describe('table', () => { wrapper.destroy() }) + + it('item field isRowHeader works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, b: 2 }], + fields: [ { key: 'a', isRowHeader: true }, 'b'] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > *').length).toBe(2) + + expect( + wrapper + .findAll('tbody > tr > *') + .at(0) + .is('th') + ).toBe(true) + expect( + wrapper + .findAll('tbody > tr > *') + .at(0) + .attributes('role') + ).toBe('rowheader') + expect( + wrapper + .findAll('tbody > tr > *') + .at(0) + .attributes('scope') + ).toBe('row') + + expect( + wrapper + .findAll('tbody > tr > *') + .at(1) + .is('td') + ).toBe(true) + expect( + wrapper + .findAll('tbody > tr > *') + .at(1) + .attributes('role') + ).toBe('cell') + expect( + wrapper + .findAll('tbody > tr > *') + .at(1) + .attributes('scope') + ).not.toBeDefined() + + wrapper.destroy() + }) }) From 0cf495ddec9faa2d53aa99bcbbba8bbd4a61733e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 18:48:56 -0300 Subject: [PATCH 141/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 3c1b71e23d5..50dac69ebc8 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -444,7 +444,7 @@ describe('table', () => { const wrapper = mount(Table, { propsData: { items: [{ a: 1, b: 2 }], - fields: [ { key: 'a', isRowHeader: true }, 'b'] + fields: [{ key: 'a', isRowHeader: true }, 'b'] } }) From a0b467fe8f04cea4e47dc58bde7fc24c48f60beb Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 18:55:43 -0300 Subject: [PATCH 142/159] Update table.spec.js --- src/components/table/table.spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 50dac69ebc8..150de84dc01 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -492,4 +492,27 @@ describe('table', () => { wrapper.destroy() }) + + it('item field formatter works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, b: 2 }], + fields: [ + { + key: 'a', + formatter(value, key, item) { return item.a + item.b } + }, + 'b' + ] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + const $tds = wrapper.findAll('tbody > tr > td') + expect($tds.at(0).text()).toBe('3') + expect($tds.at(0).text()).toBe('2') + + wrapper.destroy() }) From 201e05d99d224eb91fe64fe35346dbb6a72fb7e1 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 19:04:50 -0300 Subject: [PATCH 143/159] Update table.spec.js --- src/components/table/table.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 150de84dc01..7bb99e4c410 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -515,4 +515,5 @@ describe('table', () => { expect($tds.at(0).text()).toBe('2') wrapper.destroy() + }) }) From 41aeff6628c80458bac5b55c2e5041e617781267 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 19:09:04 -0300 Subject: [PATCH 144/159] Update table.spec.js --- src/components/table/table.spec.js | 34 +++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 7bb99e4c410..5e84eb73893 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -493,14 +493,16 @@ describe('table', () => { wrapper.destroy() }) - it('item field formatter works', async () => { + it('item field formatter as function works', async () => { const wrapper = mount(Table, { propsData: { items: [{ a: 1, b: 2 }], fields: [ { key: 'a', - formatter(value, key, item) { return item.a + item.b } + formatter(value, key, item) { + return item.a + item.b + } }, 'b' ] @@ -512,7 +514,33 @@ describe('table', () => { expect(wrapper.findAll('tbody > tr > td').length).toBe(2) const $tds = wrapper.findAll('tbody > tr > td') expect($tds.at(0).text()).toBe('3') - expect($tds.at(0).text()).toBe('2') + expect($tds.at(1).text()).toBe('2') + + wrapper.destroy() + }) + + it('item field formatter as string works', async () => { + const Parent = { + methods: { + formatter(value, key, item) { + return item.a + item.b + } + } + } + const wrapper = mount(Table, { + parentComponent: Parent, + propsData: { + items: [{ a: 1, b: 2 }], + fields: [{ key: 'a', formatter: 'formatter' }, 'b'] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + const $tds = wrapper.findAll('tbody > tr > td') + expect($tds.at(0).text()).toBe('3') + expect($tds.at(1).text()).toBe('2') wrapper.destroy() }) From 8b3af5796f06b2670a629ab833e94c99b2a9c822 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 19:11:42 -0300 Subject: [PATCH 145/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 5e84eb73893..451de442b86 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -498,7 +498,7 @@ describe('table', () => { propsData: { items: [{ a: 1, b: 2 }], fields: [ - { + { key: 'a', formatter(value, key, item) { return item.a + item.b From 2703c1d0c9a5ce8c9e8d647c44e5d8d346180575 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 19:22:31 -0300 Subject: [PATCH 146/159] Update mixin-tbody-row.js --- .../table/helpers/mixin-tbody-row.js | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index 08db431058d..da9ba89be42 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -194,39 +194,23 @@ export default { this.$set(item, '_showDetails', !item._showDetails) } } - let $childNodes - - if ($scoped[field.key]) { - // Has scoped field slot - $childNodes = [ - $scoped[field.key]({ - item: item, - index: rowIndex, - field: field, - unformatted: get(item, field.key, ''), - value: formatted, - toggleDetails: toggleDetailsFn, - detailsShowing: Boolean(item._showDetails), - rowSelected: Boolean(rowSelected) - }) - ] - if (this.isStacked) { - // We wrap in a DIV to ensure rendered as a single cell when visually stacked! - $childNodes = [h('div', {}, [$childNodes])] - } - } else { - // No scoped field slot - if (this.isStacked) { - // We wrap in a DIV to ensure rendered as a single cell when visually stacked! - $childNodes = [h('div', toString(formatted))] - } else { - // Non stacked - $childNodes = toString(formatted) - } + const slotScope = { + item: item, + index: rowIndex, + field: field, + unformatted: get(item, field.key, ''), + value: formatted, + toggleDetails: toggleDetailsFn, + detailsShowing: Boolean(item._showDetails), + rowSelected: Boolean(rowSelected) + } + let $childNodes = $scoped[field.key] ? $scoped[field.key](slotScope) : toString(formatted) + if (this.isStacked) { + // We wrap in a DIV to ensure rendered as a single cell when visually stacked! + $childNodes = [h('div', {}, [$childNodes])] } - // Render either a td or th cell - return h(field.isRowHeader ? 'th' : 'td', data, $childNodes) + return h(field.isRowHeader ? 'th' : 'td', data, [$childNodes]) }, renderTbodyRow(item, rowIndex) { // Renders an item's row (or rows if details supported) From d5760385e6f8cd011d632343e885085914053472 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 19:58:23 -0300 Subject: [PATCH 147/159] Update table.spec.js --- src/components/table/table.spec.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 451de442b86..87180db8e37 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -493,6 +493,32 @@ describe('table', () => { wrapper.destroy() }) + it('item field tdAttr and tdClass works', async () => { + const wrapper = mount(Table, { + propsData: { + items: [{ a: 1, b: 2 }], + fields: [ + { key: 'a', tdAttr: { 'data-foo': 'bar' } }, + { key: 'b', tdClass: () => 'fobar' } + ] + } + }) + + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + + const $tds = wrapper.findAll('tbody > tr > td') + + expect($tds.at(0).attributes('data-foo')).toBe('bar') + expect($tds.at(0).classes().length).toBe(0) + + expect($tds.at(0).attributes('data-foo')).not.toBeDefined() + expect($tds.at(0).classes()).toContain('foobar') + + wrapper.destroy() + }) + it('item field formatter as function works', async () => { const wrapper = mount(Table, { propsData: { From 21c59d83f04b85f7a3b6b7146f48b7be014ac127 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:04:52 -0300 Subject: [PATCH 148/159] Update table.spec.js --- src/components/table/table.spec.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 87180db8e37..11739daaea8 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -497,10 +497,7 @@ describe('table', () => { const wrapper = mount(Table, { propsData: { items: [{ a: 1, b: 2 }], - fields: [ - { key: 'a', tdAttr: { 'data-foo': 'bar' } }, - { key: 'b', tdClass: () => 'fobar' } - ] + fields: [{ key: 'a', tdAttr: { 'data-foo': 'bar' } }, { key: 'b', tdClass: () => 'fobar' }] } }) @@ -513,8 +510,8 @@ describe('table', () => { expect($tds.at(0).attributes('data-foo')).toBe('bar') expect($tds.at(0).classes().length).toBe(0) - expect($tds.at(0).attributes('data-foo')).not.toBeDefined() - expect($tds.at(0).classes()).toContain('foobar') + expect($tds.at(1).attributes('data-foo')).not.toBeDefined() + expect($tds.at(1).classes()).toContain('foobar') wrapper.destroy() }) From c2b9c945de4dd02349c783cc8447c3affedbf138 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:09:39 -0300 Subject: [PATCH 149/159] Delete table.html --- src/components/table/fixtures/table.html | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/components/table/fixtures/table.html diff --git a/src/components/table/fixtures/table.html b/src/components/table/fixtures/table.html deleted file mode 100644 index 0afb35ba1ed..00000000000 --- a/src/components/table/fixtures/table.html +++ /dev/null @@ -1,15 +0,0 @@ -
- -

Basic table

- - - - - -
From 2b84c15c8b2486735b00fae8410c1dc6492ef65a Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:09:49 -0300 Subject: [PATCH 150/159] Delete table.js --- src/components/table/fixtures/table.js | 175 ------------------------- 1 file changed, 175 deletions(-) delete mode 100644 src/components/table/fixtures/table.js diff --git a/src/components/table/fixtures/table.js b/src/components/table/fixtures/table.js deleted file mode 100644 index 15a3a3c3207..00000000000 --- a/src/components/table/fixtures/table.js +++ /dev/null @@ -1,175 +0,0 @@ -window.app = new Vue({ - el: '#app', - data: { - fields: { - name: { - label: 'Person Full name', - sortable: true, - tdClass: 'bg-primary', - tdAttr: { title: 'Person Full name' } - }, - age: { - label: 'Person age', - sortable: true, - formatter: 'formatAge', - tdClass: ['bg-primary', 'text-dark'] - }, - isActive: { - label: 'is Active', - tdClass: (value, key, item) => { - return 'bg-danger' - }, - tdAttr: (value, key, item) => { - return { title: 'is Active' } - } - }, - actions: { - label: 'Actions', - tdClass: 'formatCell', - tdAttr: 'formatCellAttrs' - } - }, - currentPage: 1, - perPage: 5, - filter: null, - selectedRecords: [], - visibleRecords: [], - isBusy: false, - providerType: 'array', - secondaryItems: [ - { - isActive: false, - age: 26, - _rowVariant: 'success', - name: 'Mitzi' - } - ], - items: [ - { - isActive: true, - age: 40, - name: { first: 'Dickerson', last: 'Macdonald' } - }, - { - isActive: false, - age: 21, - name: { first: 'Larsen', last: 'Shaw' } - }, - { - isActive: false, - age: 26, - _rowVariant: 'success', - name: { first: 'Mitzi', last: 'Navarro' } - }, - { - isActive: false, - age: 22, - name: { first: 'Geneva', last: 'Wilson' } - }, - { - isActive: true, - age: 38, - name: { first: 'Jami', last: 'Carney' } - }, - { - isActive: false, - age: 27, - name: { first: 'Essie', last: 'Dunlap' } - }, - { - isActive: true, - age: 65, - name: { first: 'Alfred', last: 'Macdonald' } - }, - { - isActive: false, - age: 21, - name: { first: 'Lauren', last: 'Shaw' } - }, - { - isActive: false, - age: 29, - name: { first: 'Mini', last: 'Navarro' } - }, - { - isActive: false, - age: 22, - name: { first: 'Frank', last: 'Wilson' } - }, - { - isActive: true, - age: 38, - name: { first: 'Jami-Lee', last: 'Curtis' } - }, - { - isActive: false, - age: 72, - name: { first: 'Elsie', last: 'Dunlap' } - } - ] - }, - computed: { - provider() { - // we are using provider wrappers here to trigger a reload - switch (this.providerType) { - case 'promise': - return this._promiseProvider - case 'callback': - return this._callbackProvider - default: - return this._arrayProvider - } - } - }, - methods: { - details(item) { - /* eslint-disable no-alert */ - alert(JSON.stringify(item)) - }, - _arrayProvider(ctx) { - return this._provider(ctx) - }, - _callbackProvider(ctx, cb) { - return this._provider(ctx, cb) - }, - _promiseProvider(ctx) { - return this._provider(ctx) - }, - _provider(ctx, cb) { - const items = this.items.slice() - - switch (this.providerType) { - case 'callback': - setTimeout(() => { - cb(items) - }, 1) - return - case 'promise': - const p = new Promise(resolve => setTimeout(resolve, 1)) - return p.then(() => { - return items - }) - default: - return items - } - }, - formatAge(value) { - return `${value} years old` - }, - formatCell(value, key, item) { - return ['bg-primary', 'text-light'] - }, - formatCellAttrs(value, key, item) { - return { title: 'Actions' } - }, - styleRow(item) { - if (!item) { - return - } - return { - 'tr-start-with-l': item.name.first.charAt(0) === 'L', - 'tr-last-name-macdonald': item.name.last === 'Macdonald' - } - } - } -}) From 15ca18de74e339fa6e0a341b305671e7ab63a015 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:14:49 -0300 Subject: [PATCH 151/159] Update table.spec.js --- src/components/table/table.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 11739daaea8..788dcb0b494 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -497,7 +497,7 @@ describe('table', () => { const wrapper = mount(Table, { propsData: { items: [{ a: 1, b: 2 }], - fields: [{ key: 'a', tdAttr: { 'data-foo': 'bar' } }, { key: 'b', tdClass: () => 'fobar' }] + fields: [{ key: 'a', tdAttr: { 'data-foo': 'bar' } }, { key: 'b', tdClass: () => 'baz' }] } }) @@ -511,7 +511,7 @@ describe('table', () => { expect($tds.at(0).classes().length).toBe(0) expect($tds.at(1).attributes('data-foo')).not.toBeDefined() - expect($tds.at(1).classes()).toContain('foobar') + expect($tds.at(1).classes()).toContain('baz') wrapper.destroy() }) From dae0ff816f5f158c5ccc80c0ece2fc2847592ddb Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:23:22 -0300 Subject: [PATCH 152/159] Update table.spec.js --- src/components/table/table.spec.js | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 788dcb0b494..88013dcad33 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -287,6 +287,61 @@ describe('table', () => { wrapper.destroy() }) + it('stacked has data-label attribute on all tbody > tr td', async () => { + const wrapper = mount(Table, { + propsData: { + items: items1, + fields: fields1, + stacked: true, + } + }) + expect(wrapper).toBeDefined() + expect(wrapper.findAll('tbody > tr').length).toBe(1) + const $trs = wrapper.findAll('tbody > tr').wrappers + + // Labels will have run through startCase + expect( + $trs[0] + .findAll('td') + .at(0) + .attributes('data-label') + ).toBe('A') + expect( + $trs[1] + .findAll('td') + .at(0) + .attributes('data-label') + ).toBe('A') + + expect( + $trs[0] + .findAll('td') + .at(1) + .attributes('data-label') + ).toBe('B') + expect( + $trs[1] + .findAll('td') + .at(1) + .attributes('data-label') + ).toBe('B') + + expect( + $trs[0] + .findAll('td') + .at(2) + .attributes('data-label') + ).toBe('C') + expect( + $trs[1] + .findAll('td') + .at(2) + .attributes('data-label') + ).toBe('C') + + wrapper.destroy() + }) + it('item _rowVariant works', async () => { const wrapper = mount(Table, { propsData: { From c43871a61af0b7698fac58efd47ec7cb4cbcc9f6 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 20:25:48 -0300 Subject: [PATCH 153/159] Update table.spec.js --- src/components/table/table.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 88013dcad33..1f23a5a6f12 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -292,11 +292,11 @@ describe('table', () => { propsData: { items: items1, fields: fields1, - stacked: true, + stacked: true } }) expect(wrapper).toBeDefined() - expect(wrapper.findAll('tbody > tr').length).toBe(1) + expect(wrapper.findAll('tbody > tr').length).toBe(2) const $trs = wrapper.findAll('tbody > tr').wrappers // Labels will have run through startCase From 60934fbcc3d8e7087f487e9adecd6dcab7282a6d Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:15:35 -0300 Subject: [PATCH 154/159] Update README.md --- src/components/table/README.md | 160 +++++++++++++++++---------------- 1 file changed, 83 insertions(+), 77 deletions(-) diff --git a/src/components/table/README.md b/src/components/table/README.md index 1e1fcfec227..6ed400ea9fe 100644 --- a/src/components/table/README.md +++ b/src/components/table/README.md @@ -522,7 +522,7 @@ You can also style every row using the `tbody-tr-class` prop ``` -## Responsive tables +### Responsive tables Responsive tables allow tables to be scrolled horizontally with ease. Make any table responsive across all viewports by setting the prop `responsive` to `true`. Or, pick a maximum breakpoint with @@ -594,7 +594,7 @@ values: `sm`, `md`, `lg`, or `xl`. clips off any content that goes beyond the bottom or top edges of the table. In particular, this may clip off dropdown menus and other third-party widgets. -## Stacked tables +### Stacked tables An alternative to responsive tables, BootstrapVue includes the stacked table option (using custom SCSS/CSS), which allow tables to be rendered in a visually stacked format. Make any table stacked @@ -647,7 +647,78 @@ The prop `stacked` takes precedence over the `responsive` prop. - In an always stacked table, the table header and footer, and the fixed top and bottom row slots will not be rendered. -## Table caption +### Table busy state + +`` provides a `busy` prop that will flag the table as busy, which you can set to `true` +just before you update your items, and then set it to `false` once you have your items. When in the +busy state, the table will have the attribute `aria-busy="true"`. + +During the busy state, the table will be rendered in a "muted" look (`opacity: 0.6`), using the +following custom CSS: + +```css +/* Busy table styling */ +table.b-table[aria-busy='false'] { + opacity: 1; +} +table.b-table[aria-busy='true'] { + opacity: 0.6; +} +``` + +You can override this styling using your own CSS. + +You may optionally provide a `table-busy` slot to show a custom loading message or spinner whenever +the table's busy state is `true`. The slot will be placed in a `
` element with class +`b-table-busy-slot`, which has one single `` and `` elements for optional grouping and styling of table columns. Note the styles available via `` elements are limited. @@ -726,77 +797,6 @@ Slot `table-colgroup` can be optionally scoped, receiving an object with the fol | `columns` | Number | The number of columns in the rendered table | | `fields` | Array | Array of field defintion objects (normalized to the array of objects format) | -## Table busy state - -`` provides a `busy` prop that will flag the table as busy, which you can set to `true` -just before you update your items, and then set it to `false` once you have your items. When in the -busy state, the table will have the attribute `aria-busy="true"`. - -During the busy state, the table will be rendered in a "muted" look (`opacity: 0.6`), using the -following custom CSS: - -```css -/* Busy table styling */ -table.b-table[aria-busy='false'] { - opacity: 1; -} -table.b-table[aria-busy='true'] { - opacity: 0.6; -} -``` - -You can override this styling using your own CSS. - -You may optionally provide a `table-busy` slot to show a custom loading message or spinner whenever -the table's busy state is `true`. The slot will be placed in a `` element with class -`b-table-busy-slot`, which has one single `
` 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, can be used instead of (or in conjunction with) slots for real table fields (i.e. fields, that have corresponding data at items array). 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 | Change sort direction on this column. Refer to the [**Change sort direction**](#change-sort-direction) 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. | | `tdClass` | String or Array or Function | Class name (or array of class names) to add to `
` 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 `
` cell. | | `thStyle` | Object | JavaScript object representing CSS styles you would like to apply to the table `
`. | @@ -413,7 +413,7 @@ place a unique `:key` on your element/components in your custom formatted field | `small` | Boolean | To make tables more compact by cutting cell padding in half. | | `hover` | Boolean | To enable a hover highlighting state on table rows within a `
` with a `colspan` set to the number of fields. + +**Example of `table-busy` slot usage:** + +```html + + + + + +``` + +Also see the [**Using Items Provider Functions**](#using-items-provider-functions) below for +additional information on the `busy` state. + +**Note:** All click related and hover events, and sort-changed events will **not** be emitted when +the table is in the `busy` state. + +### Table caption Add an optional caption to your table via the prop `caption` or the named slot `table-caption` (the slot takes precedence over the prop). The default Bootstrap V4 styling places the caption at the @@ -712,7 +783,7 @@ You can have the caption placed at the top of the table by setting the `caption- You can also use [custom CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/caption-side) to control the caption positioning. -## Table colgroup +### Table colgroup Use the named slot `table-colgroup` to specify `
` with a `colspan` set to the number of fields. - -**Example of `table-busy` slot usage:** - -```html - - - - - -``` - -Also see the [**Using Items Provider Functions**](#using-items-provider-functions) below for -additional information on the `busy` state. - -**Note:** All click related and hover events, and sort-changed events will **not** be emitted when -the table is in the `busy` state. - ## Custom Data Rendering Custom rendering for each data field in a row is possible using either @@ -915,8 +915,14 @@ scoped field slot ``` -**Warning:** Be cautious of using this to display user supplied content, **as script tags could be -injected into your page!** +

+ Warning: Be cautious of using the v-html method to display user + supplied content, as it may make your application vulnerable to + + XSS attacks, if you do not first + sanitize the + user supplied string. + ### Formatter callback From 1dbcb4a8d5bc42efbcac06c179ac79e9eac80c99 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:29:11 -0300 Subject: [PATCH 155/159] Update table.spec.js --- src/components/table/table.spec.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 1f23a5a6f12..2b6933d508b 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -549,24 +549,41 @@ describe('table', () => { }) it('item field tdAttr and tdClass works', async () => { + const Parent = { + methods: { + parentTdAttrs(value, key, item) { + return { 'data-parent': 'parent' } + } + } + } const wrapper = mount(Table, { propsData: { - items: [{ a: 1, b: 2 }], - fields: [{ key: 'a', tdAttr: { 'data-foo': 'bar' } }, { key: 'b', tdClass: () => 'baz' }] + items: [{ a: 1, b: 2, c: 3 }], + fields: [ + { key: 'a', tdAttr: { 'data-foo': 'bar' } }, + { key: 'b', tdClass: () => 'baz' }, + { key: 'b', tdAttr: 'parentTdAttrs' } + ] } }) expect(wrapper).toBeDefined() expect(wrapper.findAll('tbody > tr').length).toBe(1) - expect(wrapper.findAll('tbody > tr > td').length).toBe(2) + expect(wrapper.findAll('tbody > tr > td').length).toBe(3) const $tds = wrapper.findAll('tbody > tr > td') expect($tds.at(0).attributes('data-foo')).toBe('bar') + expect($tds.at(0).attributes('data-parent')).not.toBeDefined() expect($tds.at(0).classes().length).toBe(0) - expect($tds.at(1).attributes('data-foo')).not.toBeDefined() expect($tds.at(1).classes()).toContain('baz') + expect($tds.at(1).attributes('data-foo')).not.toBeDefined() + expect($tds.at(1).attributes('data-parent')).not.toBeDefined() + + expect($tds.at(2).attributes('data-parent')).toBe('parent') + expect($tds.at(2).attributes('data-foo')).not.toBeDefined() + expect($tds.at(2).classes().length).toBe(0) wrapper.destroy() }) From 44600bf114b8292710e6bd80db5d4f3137d4a061 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:31:00 -0300 Subject: [PATCH 156/159] Update mixin-tbody-row.js --- src/components/table/helpers/mixin-tbody-row.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index da9ba89be42..4be4107feb9 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -149,6 +149,7 @@ export default { return } else if (filterEvent(e)) { // clicked on a non-disabled control so ignore + /* istanbul ignore next: event filtering already tested via click handler */ return } this.$emit('row-dblclicked', item, index, e) From 209815ca83908ca717d0be92ea9deecac361f555 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:32:25 -0300 Subject: [PATCH 157/159] Update table.spec.js --- src/components/table/table.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 2b6933d508b..5e71038f3a1 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -557,6 +557,7 @@ describe('table', () => { } } const wrapper = mount(Table, { + parentComponent: Parent, propsData: { items: [{ a: 1, b: 2, c: 3 }], fields: [ From 8ee31853d83d2f100105c00aa28f21fc81b4139c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:36:46 -0300 Subject: [PATCH 158/159] Update mixin-sorting.js --- src/components/table/helpers/mixin-sorting.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index b885932006b..06b617e9707 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -215,16 +215,15 @@ export default { ariaLabelSorting = this.localSortDesc ? this.labelSortAsc : this.labelSortDesc } else { // Not currently sorted sortable column. - // Have to add special tests to handle this.sortDirection and field.sortDirection. // Not using nested ternary's here for clarity/readability + // Default for ariaLabel + ariaLabelSorting = this.localSortDesc ? this.labelSortDesc : this.labelSortAsc + // Handle sortDirection setting const sortDirection = this.sortDirection || field.sortDirection if (sortDirection === 'asc') { ariaLabelSorting = this.labelSortAsc } else if (sortDirection === 'desc') { ariaLabelSorting = this.labelSortDesc - } else { - // sortDirection === 'last' - ariaLabelSorting = this.localSortDesc ? this.labelSortDesc : this.labelSortAsc } } } else if (!this.noSortReset) { From f9aea011c801f3b5aa967aaed8be79b2ef39508e Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 21 Mar 2019 21:38:02 -0300 Subject: [PATCH 159/159] Update table.spec.js --- src/components/table/table.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/table/table.spec.js b/src/components/table/table.spec.js index 5e71038f3a1..e67e06ad135 100644 --- a/src/components/table/table.spec.js +++ b/src/components/table/table.spec.js @@ -563,7 +563,7 @@ describe('table', () => { fields: [ { key: 'a', tdAttr: { 'data-foo': 'bar' } }, { key: 'b', tdClass: () => 'baz' }, - { key: 'b', tdAttr: 'parentTdAttrs' } + { key: 'c', tdAttr: 'parentTdAttrs' } ] } })