From 39e308c7e1070995f30d041a406deb5970c95178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Mon, 30 Nov 2020 23:53:02 +0100 Subject: [PATCH 01/16] fix(b-form-input/b-form-textarea): v-model handling --- src/mixins/form-text.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 39f3c4e9bb1..9799008a279 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -123,6 +123,15 @@ export default { mounted() { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) + // Preset the internal state + const { value } = this + const stringifyValue = toString(value) + const modifiedValue = this.modifyValue(value) + /* istanbul ignore next */ + if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { + this.localValue = stringifyValue + this.vModelValue = modifiedValue + } }, methods: { clearDebounce() { From 5a7571e843331b30e527ff0e39c241c35b9e4084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:00:57 +0100 Subject: [PATCH 02/16] Update form-text.js --- src/mixins/form-text.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 9799008a279..c9dfb236d29 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -107,7 +107,7 @@ export default { value(newValue) { const stringifyValue = toString(newValue) const modifiedValue = this.modifyValue(newValue) - if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { + if (stringifyValue !== this.localValue && modifiedValue !== this.vModelValue) { // Clear any pending debounce timeout, as we are overwriting the user input this.clearDebounce() // Update the local values @@ -128,7 +128,7 @@ export default { const stringifyValue = toString(value) const modifiedValue = this.modifyValue(value) /* istanbul ignore next */ - if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { + if (stringifyValue !== this.localValue && modifiedValue !== this.vModelValue) { this.localValue = stringifyValue this.vModelValue = modifiedValue } From 99859b77f8c346e669b21e8c78687e5fb9d8edae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:07:37 +0100 Subject: [PATCH 03/16] Update form-text.js --- src/mixins/form-text.js | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index c9dfb236d29..0fd3d99256f 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -106,13 +106,12 @@ export default { watch: { value(newValue) { const stringifyValue = toString(newValue) - const modifiedValue = this.modifyValue(newValue) - if (stringifyValue !== this.localValue && modifiedValue !== this.vModelValue) { + if (stringifyValue !== this.localValue && newValue !== this.vModelValue) { // Clear any pending debounce timeout, as we are overwriting the user input this.clearDebounce() // Update the local values this.localValue = stringifyValue - this.vModelValue = modifiedValue + this.vModelValue = this.modifyValue(newValue) } } }, @@ -123,15 +122,6 @@ export default { mounted() { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) - // Preset the internal state - const { value } = this - const stringifyValue = toString(value) - const modifiedValue = this.modifyValue(value) - /* istanbul ignore next */ - if (stringifyValue !== this.localValue && modifiedValue !== this.vModelValue) { - this.localValue = stringifyValue - this.vModelValue = modifiedValue - } }, methods: { clearDebounce() { From 9e8e6438f882a1afc11adf375b62a6708a2658cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:14:25 +0100 Subject: [PATCH 04/16] Update form-text.js --- src/mixins/form-text.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 0fd3d99256f..181a4996b4d 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -136,7 +136,6 @@ export default { return value }, modifyValue(value) { - value = toString(value) // Emulate `.trim` modifier behaviour if (this.trim) { value = value.trim() From efbfd37f58d35c8a9ca73c41d822d71d72022278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:27:36 +0100 Subject: [PATCH 05/16] Update form-text.js --- src/mixins/form-text.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 181a4996b4d..065064890d8 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -75,7 +75,7 @@ export default { const { value } = this return { localValue: toString(value), - vModelValue: this.modifyValue(value) + vModelValue: value } }, computed: { @@ -111,7 +111,7 @@ export default { this.clearDebounce() // Update the local values this.localValue = stringifyValue - this.vModelValue = this.modifyValue(newValue) + this.vModelValue = newValue } } }, @@ -136,6 +136,7 @@ export default { return value }, modifyValue(value) { + value = toString(value) // Emulate `.trim` modifier behaviour if (this.trim) { value = value.trim() From 96576516fd7aff1c95f5e2c548b603c70686689a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:38:36 +0100 Subject: [PATCH 06/16] Update form-text.js --- src/mixins/form-text.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 065064890d8..3f8f5c3f819 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -122,6 +122,14 @@ export default { mounted() { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) + // Preset the internal state + const { value } = this + const stringifyValue = toString(value) + /* istanbul ignore next */ + if (stringifyValue !== this.localValue && value !== this.vModelValue) { + this.localValue = stringifyValue + this.vModelValue = value + } }, methods: { clearDebounce() { @@ -136,7 +144,6 @@ export default { return value }, modifyValue(value) { - value = toString(value) // Emulate `.trim` modifier behaviour if (this.trim) { value = value.trim() From a2f4a3ba4087a2f8b6ccd07bc8312596ecca9d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 00:55:22 +0100 Subject: [PATCH 07/16] Update form-text.js --- src/mixins/form-text.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 3f8f5c3f819..23994663b2c 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -1,6 +1,7 @@ import { makePropsConfigurable } from '../utils/config' import { attemptBlur, attemptFocus } from '../utils/dom' import { stopEvent } from '../utils/events' +import { isUndefined } from '../utils/inspect' import { mathMax } from '../utils/math' import { toInteger, toFloat } from '../utils/number' import { toString } from '../utils/string' @@ -72,10 +73,9 @@ export default { }, props, data() { - const { value } = this return { - localValue: toString(value), - vModelValue: value + localValue: toString(this.value), + vModelValue: this.value } }, computed: { @@ -100,18 +100,22 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - return this.formatter.name !== 'default' + let result = null + try { + result = this.formatter() + } catch {} + return !isUndefined(result) } }, watch: { - value(newValue) { - const stringifyValue = toString(newValue) - if (stringifyValue !== this.localValue && newValue !== this.vModelValue) { + value(newVal) { + const stringifyValue = toString(newVal) + if (stringifyValue !== this.localValue && newVal !== this.vModelValue) { // Clear any pending debounce timeout, as we are overwriting the user input this.clearDebounce() // Update the local values this.localValue = stringifyValue - this.vModelValue = newValue + this.vModelValue = newVal } } }, @@ -123,7 +127,7 @@ export default { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) // Preset the internal state - const { value } = this + const value = this.value const stringifyValue = toString(value) /* istanbul ignore next */ if (stringifyValue !== this.localValue && value !== this.vModelValue) { @@ -155,7 +159,7 @@ export default { return value }, updateValue(value, force = false) { - const { lazy } = this + const lazy = this.lazy if (lazy && !force) { return } From 7f13703568492b1accae3fb2fac39c4e3c9cc9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 01:01:00 +0100 Subject: [PATCH 08/16] Update form-text.js --- src/mixins/form-text.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 23994663b2c..2937af7272a 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -1,7 +1,6 @@ import { makePropsConfigurable } from '../utils/config' import { attemptBlur, attemptFocus } from '../utils/dom' import { stopEvent } from '../utils/events' -import { isUndefined } from '../utils/inspect' import { mathMax } from '../utils/math' import { toInteger, toFloat } from '../utils/number' import { toString } from '../utils/string' @@ -100,11 +99,7 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - let result = null - try { - result = this.formatter() - } catch {} - return !isUndefined(result) + return this.formatter.name !== 'default' } }, watch: { From be0239ea49a72f27e05e8821b4c8c9c2c8af5467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 01:10:33 +0100 Subject: [PATCH 09/16] Update form-text.js --- src/mixins/form-text.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 2937af7272a..e69df99ca20 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -99,6 +99,7 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { + console.log(this.formatter.name) return this.formatter.name !== 'default' } }, From 23493a4b0c6ff92456233bf85cb5126f94f9499e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 01:21:53 +0100 Subject: [PATCH 10/16] Update form-text.js --- src/mixins/form-text.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index e69df99ca20..8e55fb07a4d 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -99,8 +99,8 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - console.log(this.formatter.name) - return this.formatter.name !== 'default' + console.log(this.formatter.name, props.formatter.default.name) + return this.formatter.name !== props.formatter.default.name } }, watch: { From 98e9ce1d477dad53f83ab8b368e9cb4745237465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 01:26:14 +0100 Subject: [PATCH 11/16] Update form-text.js --- src/mixins/form-text.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 8e55fb07a4d..25e34257d4c 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -72,9 +72,10 @@ export default { }, props, data() { + const { value } = this return { - localValue: toString(this.value), - vModelValue: this.value + localValue: toString(value), + vModelValue: this.modifyValue(value) } }, computed: { @@ -99,19 +100,19 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - console.log(this.formatter.name, props.formatter.default.name) return this.formatter.name !== props.formatter.default.name } }, watch: { - value(newVal) { - const stringifyValue = toString(newVal) - if (stringifyValue !== this.localValue && newVal !== this.vModelValue) { + value(newValue) { + const stringifyValue = toString(newValue) + const modifiedValue = this.modifyValue(newValue) + if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { // Clear any pending debounce timeout, as we are overwriting the user input this.clearDebounce() // Update the local values this.localValue = stringifyValue - this.vModelValue = newVal + this.vModelValue = modifiedValue } } }, @@ -123,12 +124,13 @@ export default { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) // Preset the internal state - const value = this.value + const { value } = this const stringifyValue = toString(value) + const modifiedValue = this.modifyValue(value) /* istanbul ignore next */ - if (stringifyValue !== this.localValue && value !== this.vModelValue) { + if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { this.localValue = stringifyValue - this.vModelValue = value + this.vModelValue = modifiedValue } }, methods: { @@ -144,6 +146,7 @@ export default { return value }, modifyValue(value) { + value = toString(value) // Emulate `.trim` modifier behaviour if (this.trim) { value = value.trim() @@ -155,7 +158,7 @@ export default { return value }, updateValue(value, force = false) { - const lazy = this.lazy + const { lazy } = this if (lazy && !force) { return } From 347c2a3889a9f22cab1e47dfcdb5c88a6cb4dae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 01:35:02 +0100 Subject: [PATCH 12/16] fix: user supplied prop function detection --- src/components/calendar/calendar.js | 6 +- src/components/form-file/form-file.js | 154 +++++------ .../form-spinbutton/form-spinbutton.js | 4 +- src/components/form-tags/form-tags.js | 242 +++++++++--------- .../table/helpers/mixin-filtering.js | 68 ++--- 5 files changed, 247 insertions(+), 227 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index f531a332bfb..ba0262abdf5 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -342,12 +342,14 @@ export const BCalendar = Vue.extend({ }, computedDateDisabledFn() { const { dateDisabledFn } = this - return dateDisabledFn.name !== 'default' ? dateDisabledFn : () => false + return dateDisabledFn.name !== props.dateDisabledFn.default.name + ? dateDisabledFn + : () => false }, // TODO: Change `dateInfoFn` to handle events and notes as well as classes computedDateInfoFn() { const { dateInfoFn } = this - return dateInfoFn.name !== 'default' ? dateInfoFn : () => ({}) + return dateInfoFn.name !== props.dateInfoFn.default.name ? dateInfoFn : () => ({}) }, calendarLocale() { // This locale enforces the gregorian calendar (for use in formatter functions) diff --git a/src/components/form-file/form-file.js b/src/components/form-file/form-file.js index fd57827d677..52e6d53002d 100644 --- a/src/components/form-file/form-file.js +++ b/src/components/form-file/form-file.js @@ -108,6 +108,83 @@ const getAllFileEntriesInDirectory = (directoryReader, path = '') => readDirectoryEntries() }) +// --- Props --- + +const props = makePropsConfigurable( + { + ...formControlProps, + ...formCustomProps, + ...formStateProps, + ...formSizeProps, + value: { + type: [File, Array], + default: null, + validator(value) { + /* istanbul ignore next */ + if (value === '') { + warn(VALUE_EMPTY_DEPRECATED_MSG, NAME_FORM_FILE) + return true + } + return isUndefinedOrNull(value) || isValidValue(value) + } + }, + accept: { + type: String, + default: '' + }, + // Instruct input to capture from camera + capture: { + type: Boolean, + default: false + }, + placeholder: { + type: String, + default: 'No file chosen' + }, + browseText: { + type: String, + default: 'Browse' + }, + dropPlaceholder: { + type: String, + default: 'Drop files here' + }, + noDropPlaceholder: { + type: String, + default: 'Not allowed' + }, + multiple: { + type: Boolean, + default: false + }, + directory: { + type: Boolean, + default: false + }, + // TODO: + // Should we deprecate this and only support flat file structures? + // Nested file structures are only supported when files are dropped + // A Chromium "bug" prevents `webkitEntries` from being populated + // on the file input's `change` event and is marked as "WontFix" + // Mozilla implemented the behavior the same way as Chromium + // See: https://bugs.chromium.org/p/chromium/issues/detail?id=138987 + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1326031 + noTraverse: { + type: Boolean, + default: false + }, + noDrop: { + type: Boolean, + default: false + }, + fileNameFormatter: { + type: Function + // default: null + } + }, + NAME_FORM_FILE +) + // @vue/component export const BFormFile = /*#__PURE__*/ Vue.extend({ name: NAME_FORM_FILE, @@ -124,80 +201,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ prop: 'value', event: 'input' }, - props: makePropsConfigurable( - { - ...formControlProps, - ...formCustomProps, - ...formStateProps, - ...formSizeProps, - value: { - type: [File, Array], - default: null, - validator(value) { - /* istanbul ignore next */ - if (value === '') { - warn(VALUE_EMPTY_DEPRECATED_MSG, NAME_FORM_FILE) - return true - } - return isUndefinedOrNull(value) || isValidValue(value) - } - }, - accept: { - type: String, - default: '' - }, - // Instruct input to capture from camera - capture: { - type: Boolean, - default: false - }, - placeholder: { - type: String, - default: 'No file chosen' - }, - browseText: { - type: String, - default: 'Browse' - }, - dropPlaceholder: { - type: String, - default: 'Drop files here' - }, - noDropPlaceholder: { - type: String, - default: 'Not allowed' - }, - multiple: { - type: Boolean, - default: false - }, - directory: { - type: Boolean, - default: false - }, - // TODO: - // Should we deprecate this and only support flat file structures? - // Nested file structures are only supported when files are dropped - // A Chromium "bug" prevents `webkitEntries` from being populated - // on the file input's `change` event and is marked as "WontFix" - // Mozilla implemented the behavior the same way as Chromium - // See: https://bugs.chromium.org/p/chromium/issues/detail?id=138987 - // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1326031 - noTraverse: { - type: Boolean, - default: false - }, - noDrop: { - type: Boolean, - default: false - }, - fileNameFormatter: { - type: Function - // default: null - } - }, - NAME_FORM_FILE - ), + props, data() { return { files: [], @@ -269,7 +273,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ }, computedFileNameFormatter() { const { fileNameFormatter } = this - return fileNameFormatter.name !== 'default' + return fileNameFormatter.name !== props.fileNameFormatter.default.name ? fileNameFormatter : this.defaultFileNameFormatter }, diff --git a/src/components/form-spinbutton/form-spinbutton.js b/src/components/form-spinbutton/form-spinbutton.js index b88a09cae04..a3446fd1d7c 100644 --- a/src/components/form-spinbutton/form-spinbutton.js +++ b/src/components/form-spinbutton/form-spinbutton.js @@ -223,7 +223,9 @@ export const BFormSpinbutton = /*#__PURE__*/ Vue.extend({ }, computedFormatter() { const { formatterFn } = this - return formatterFn.name !== 'default' ? formatterFn : this.defaultFormatter + return formatterFn.name !== props.formatterFn.default.name + ? formatterFn + : this.defaultFormatter }, computedAttrs() { return { diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index 3656f53c7b6..68e3c5a78ea 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -63,6 +63,127 @@ const cleanTagsState = () => ({ duplicate: [] }) +// --- Props --- + +const props = makePropsConfigurable( + { + ...formControlProps, + ...formSizeProps, + ...formStateProps, + value: { + // The v-model prop + type: Array, + default: () => [] + }, + placeholder: { + type: String, + default: 'Add tag...' + }, + inputId: { + type: String + // default: null + }, + inputType: { + type: String, + default: 'text', + validator(value) { + return arrayIncludes(TYPES, value) + } + }, + inputClass: { + type: [String, Array, Object] + // default: null + }, + inputAttrs: { + // Additional attributes to add to the input element + type: Object, + default: () => ({}) + }, + addButtonText: { + type: String, + default: 'Add' + }, + addButtonVariant: { + type: String, + default: 'outline-secondary' + }, + tagVariant: { + type: String, + default: 'secondary' + }, + tagClass: { + type: [String, Array, Object] + // default: null + }, + tagPills: { + type: Boolean, + default: false + }, + tagRemoveLabel: { + type: String, + default: 'Remove tag' + }, + tagRemovedLabel: { + type: String, + default: 'Tag removed' + }, + tagValidator: { + type: Function + // default: null + }, + duplicateTagText: { + type: String, + default: 'Duplicate tag(s)' + }, + invalidTagText: { + type: String, + default: 'Invalid tag(s)' + }, + limitTagsText: { + type: String, + default: 'Tag limit reached' + }, + limit: { + type: Number + // default: null + }, + separator: { + // Character (or characters) that trigger adding tags + type: [String, Array] + // default: null + }, + removeOnDelete: { + // Enable deleting last tag in list when CODE_BACKSPACE is + // pressed and input is empty + type: Boolean, + default: false + }, + addOnChange: { + // Enable change event triggering tag addition + // Handy if using as the input - type: Boolean, - default: false - }, - noAddOnEnter: { - // Disable ENTER key from triggering tag addition - type: Boolean, - default: false - }, - noOuterFocus: { - // Disable the focus ring on the root element - type: Boolean, - default: false - }, - ignoreInputFocusSelector: { - // Disable the input focus behavior when clicking - // on element matching the selector (or selectors) - type: [Array, String], - default: () => ['.b-form-tag', 'button', 'input', 'select'] - } - }, - NAME_FORM_TAGS - ), + props, data() { return { hasFocus: false, @@ -531,7 +535,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ }, validateTag(tag) { const { tagValidator } = this - return tagValidator.name !== 'default' ? tagValidator(tag) : true + return tagValidator.name !== props.tagValidator.default.name ? tagValidator(tag) : true }, getInput() { // Returns the input element reference (or null if not found) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index a3c53cf33ca..6519d578717 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -11,39 +11,47 @@ import { escapeRegExp } from '../../../utils/string' import { warn } from '../../../utils/warn' import stringifyRecordValues from './stringify-record-values' +// --- Constants --- + const DEBOUNCE_DEPRECATED_MSG = 'Prop "filter-debounce" is deprecated. Use the debounce feature of "" instead.' -export default { - props: makePropsConfigurable( - { - filter: { - type: [String, RegExp, Object, Array], - default: null - }, - filterFunction: { - type: Function - // default: null - }, - filterIgnoredFields: { - type: Array - // default: undefined - }, - filterIncludedFields: { - type: Array - // default: undefined - }, - filterDebounce: { - type: [Number, String], - deprecated: DEBOUNCE_DEPRECATED_MSG, - default: 0, - validator(value) { - return /^\d+/.test(String(value)) - } - } +// --- Props --- + +export const props = makePropsConfigurable( + { + filter: { + type: [String, RegExp, Object, Array], + default: null + }, + filterFunction: { + type: Function + // default: null }, - NAME_TABLE - ), + filterIgnoredFields: { + type: Array + // default: undefined + }, + filterIncludedFields: { + type: Array + // default: undefined + }, + filterDebounce: { + type: [Number, String], + deprecated: DEBOUNCE_DEPRECATED_MSG, + default: 0, + validator(value) { + return /^\d+/.test(String(value)) + } + } + }, + NAME_TABLE +) + +// --- Mixin --- +// @vue/component +export default { + props, data() { return { // Flag for displaying which empty slot to show and some event triggering @@ -83,7 +91,7 @@ export default { localFilterFn() { // Return `null` to signal to use internal filter function const { filterFunction } = this - return filterFunction.name !== 'default' ? filterFunction : null + return filterFunction.name !== props.filterFunction.default.name ? filterFunction : null }, // Returns the records in `localItems` that match the filter criteria // Returns the original `localItems` array if not sorting From 8ac8e704e194b616fee0d5af5bb7199de427940b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 02:16:14 +0100 Subject: [PATCH 13/16] Update calendar.spec.js --- src/components/calendar/calendar.spec.js | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/components/calendar/calendar.spec.js b/src/components/calendar/calendar.spec.js index f32a655df67..f7c5a70aaba 100644 --- a/src/components/calendar/calendar.spec.js +++ b/src/components/calendar/calendar.spec.js @@ -377,4 +377,70 @@ describe('calendar', () => { expect($buttons.at(3).classes()).toContain('btn-outline-primary') expect($buttons.at(4).classes()).toContain('btn-outline-primary') }) + + it('disables dates based on `date-disabled-fn` prop', async () => { + const wrapper = mount(BCalendar, { + attachTo: createContainer(), + propsData: { + value: '2020-01-01', + dateDisabledFn(ymd) { + return ymd === '2020-01-02' + } + } + }) + + expect(wrapper.vm).toBeDefined() + await waitNT(wrapper.vm) + await waitRAF() + + const $grid = wrapper.find('[role="application"]') + expect($grid.exists()).toBe(true) + + let $cell = $grid.find('[data-date="2020-01-01"]') + expect($cell.exists()).toBe(true) + expect($cell.attributes('aria-disabled')).toBeUndefined() + + $cell = $grid.find('[data-date="2020-01-02"]') + expect($cell.exists()).toBe(true) + expect($cell.attributes('aria-disabled')).toEqual('true') + + $cell = $grid.find('[data-date="2020-01-03"]') + expect($cell.exists()).toBe(true) + expect($cell.attributes('aria-disabled')).toBeUndefined() + + wrapper.destroy() + }) + + it('applies classes on dates based on `date-info-fn` prop', async () => { + const wrapper = mount(BCalendar, { + attachTo: createContainer(), + propsData: { + value: '2020-01-01', + dateInfoFn(ymd) { + return ymd === '2020-01-02' ? 'my-info' : null + } + } + }) + + expect(wrapper.vm).toBeDefined() + await waitNT(wrapper.vm) + await waitRAF() + + const $grid = wrapper.find('[role="application"]') + expect($grid.exists()).toBe(true) + + let $cell = $grid.find('[data-date="2020-01-01"]') + expect($cell.exists()).toBe(true) + expect($cell.classes()).not.toContain('my-info') + + $cell = $grid.find('[data-date="2020-01-02"]') + expect($cell.exists()).toBe(true) + expect($cell.classes()).toContain('my-info') + + $cell = $grid.find('[data-date="2020-01-03"]') + expect($cell.exists()).toBe(true) + expect($cell.classes()).not.toContain('my-info') + + wrapper.destroy() + }) }) From 53fdb1124f8574cb344c07f3239c30ba1eaf26bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 02:21:44 +0100 Subject: [PATCH 14/16] Update form-text.js --- src/mixins/form-text.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index 25e34257d4c..a2871dd2399 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -123,15 +123,6 @@ export default { mounted() { // Set up destroy handler this.$on('hook:beforeDestroy', this.clearDebounce) - // Preset the internal state - const { value } = this - const stringifyValue = toString(value) - const modifiedValue = this.modifyValue(value) - /* istanbul ignore next */ - if (stringifyValue !== this.localValue || modifiedValue !== this.vModelValue) { - this.localValue = stringifyValue - this.vModelValue = modifiedValue - } }, methods: { clearDebounce() { From 86bbb7fa41f2c62756667d095dedae2461170f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 02:25:36 +0100 Subject: [PATCH 15/16] fix: further improve user supplied prop fucntion detection --- src/components/calendar/calendar.js | 6 ++---- src/components/form-file/form-file.js | 2 +- src/components/form-spinbutton/form-spinbutton.js | 4 +--- src/components/form-tags/form-tags.js | 2 +- src/components/table/helpers/mixin-filtering.js | 2 +- src/mixins/form-text.js | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index ba0262abdf5..15989657efc 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -342,14 +342,12 @@ export const BCalendar = Vue.extend({ }, computedDateDisabledFn() { const { dateDisabledFn } = this - return dateDisabledFn.name !== props.dateDisabledFn.default.name - ? dateDisabledFn - : () => false + return dateDisabledFn !== props.dateDisabledFn.default ? dateDisabledFn : () => false }, // TODO: Change `dateInfoFn` to handle events and notes as well as classes computedDateInfoFn() { const { dateInfoFn } = this - return dateInfoFn.name !== props.dateInfoFn.default.name ? dateInfoFn : () => ({}) + return dateInfoFn !== props.dateInfoFn.default ? dateInfoFn : () => ({}) }, calendarLocale() { // This locale enforces the gregorian calendar (for use in formatter functions) diff --git a/src/components/form-file/form-file.js b/src/components/form-file/form-file.js index 52e6d53002d..c645decc66a 100644 --- a/src/components/form-file/form-file.js +++ b/src/components/form-file/form-file.js @@ -273,7 +273,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ }, computedFileNameFormatter() { const { fileNameFormatter } = this - return fileNameFormatter.name !== props.fileNameFormatter.default.name + return fileNameFormatter !== props.fileNameFormatter.default ? fileNameFormatter : this.defaultFileNameFormatter }, diff --git a/src/components/form-spinbutton/form-spinbutton.js b/src/components/form-spinbutton/form-spinbutton.js index a3446fd1d7c..c702b046b17 100644 --- a/src/components/form-spinbutton/form-spinbutton.js +++ b/src/components/form-spinbutton/form-spinbutton.js @@ -223,9 +223,7 @@ export const BFormSpinbutton = /*#__PURE__*/ Vue.extend({ }, computedFormatter() { const { formatterFn } = this - return formatterFn.name !== props.formatterFn.default.name - ? formatterFn - : this.defaultFormatter + return formatterFn !== props.formatterFn.default ? formatterFn : this.defaultFormatter }, computedAttrs() { return { diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index 68e3c5a78ea..ad58a3d0c1b 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -535,7 +535,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ }, validateTag(tag) { const { tagValidator } = this - return tagValidator.name !== props.tagValidator.default.name ? tagValidator(tag) : true + return tagValidator !== props.tagValidator.default ? tagValidator(tag) : true }, getInput() { // Returns the input element reference (or null if not found) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index 6519d578717..9e2e18e2410 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -91,7 +91,7 @@ export default { localFilterFn() { // Return `null` to signal to use internal filter function const { filterFunction } = this - return filterFunction.name !== props.filterFunction.default.name ? filterFunction : null + return filterFunction !== props.filterFunction.default ? filterFunction : null }, // Returns the records in `localItems` that match the filter criteria // Returns the original `localItems` array if not sorting diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index a2871dd2399..abc3c988b64 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -100,7 +100,7 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - return this.formatter.name !== props.formatter.default.name + return this.formatter !== props.formatter.default } }, watch: { From e49047fceef05df6bb6dc93da1edb31b23c1fe3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Tue, 1 Dec 2020 02:34:02 +0100 Subject: [PATCH 16/16] Revert "fix: further improve user supplied prop fucntion detection" This reverts commit 86bbb7fa41f2c62756667d095dedae2461170f13. --- src/components/calendar/calendar.js | 6 ++++-- src/components/form-file/form-file.js | 2 +- src/components/form-spinbutton/form-spinbutton.js | 4 +++- src/components/form-tags/form-tags.js | 2 +- src/components/table/helpers/mixin-filtering.js | 2 +- src/mixins/form-text.js | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index 15989657efc..ba0262abdf5 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -342,12 +342,14 @@ export const BCalendar = Vue.extend({ }, computedDateDisabledFn() { const { dateDisabledFn } = this - return dateDisabledFn !== props.dateDisabledFn.default ? dateDisabledFn : () => false + return dateDisabledFn.name !== props.dateDisabledFn.default.name + ? dateDisabledFn + : () => false }, // TODO: Change `dateInfoFn` to handle events and notes as well as classes computedDateInfoFn() { const { dateInfoFn } = this - return dateInfoFn !== props.dateInfoFn.default ? dateInfoFn : () => ({}) + return dateInfoFn.name !== props.dateInfoFn.default.name ? dateInfoFn : () => ({}) }, calendarLocale() { // This locale enforces the gregorian calendar (for use in formatter functions) diff --git a/src/components/form-file/form-file.js b/src/components/form-file/form-file.js index c645decc66a..52e6d53002d 100644 --- a/src/components/form-file/form-file.js +++ b/src/components/form-file/form-file.js @@ -273,7 +273,7 @@ export const BFormFile = /*#__PURE__*/ Vue.extend({ }, computedFileNameFormatter() { const { fileNameFormatter } = this - return fileNameFormatter !== props.fileNameFormatter.default + return fileNameFormatter.name !== props.fileNameFormatter.default.name ? fileNameFormatter : this.defaultFileNameFormatter }, diff --git a/src/components/form-spinbutton/form-spinbutton.js b/src/components/form-spinbutton/form-spinbutton.js index c702b046b17..a3446fd1d7c 100644 --- a/src/components/form-spinbutton/form-spinbutton.js +++ b/src/components/form-spinbutton/form-spinbutton.js @@ -223,7 +223,9 @@ export const BFormSpinbutton = /*#__PURE__*/ Vue.extend({ }, computedFormatter() { const { formatterFn } = this - return formatterFn !== props.formatterFn.default ? formatterFn : this.defaultFormatter + return formatterFn.name !== props.formatterFn.default.name + ? formatterFn + : this.defaultFormatter }, computedAttrs() { return { diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index ad58a3d0c1b..68e3c5a78ea 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -535,7 +535,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ }, validateTag(tag) { const { tagValidator } = this - return tagValidator !== props.tagValidator.default ? tagValidator(tag) : true + return tagValidator.name !== props.tagValidator.default.name ? tagValidator(tag) : true }, getInput() { // Returns the input element reference (or null if not found) diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index 9e2e18e2410..6519d578717 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -91,7 +91,7 @@ export default { localFilterFn() { // Return `null` to signal to use internal filter function const { filterFunction } = this - return filterFunction !== props.filterFunction.default ? filterFunction : null + return filterFunction.name !== props.filterFunction.default.name ? filterFunction : null }, // Returns the records in `localItems` that match the filter criteria // Returns the original `localItems` array if not sorting diff --git a/src/mixins/form-text.js b/src/mixins/form-text.js index abc3c988b64..a2871dd2399 100644 --- a/src/mixins/form-text.js +++ b/src/mixins/form-text.js @@ -100,7 +100,7 @@ export default { return mathMax(toInteger(this.debounce, 0), 0) }, hasFormatter() { - return this.formatter !== props.formatter.default + return this.formatter.name !== props.formatter.default.name } }, watch: {