From 7a34f737be4825bfc1313463b13e2edb529f9a7c Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 13 Feb 2020 04:34:48 -0400 Subject: [PATCH 01/15] chore: create BVHoverSwap utility helper component (#4759) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: create BVHoverSwap utility helper component * Update bv-hover-swap.js * Update bv-hover-swap.js * Update bv-hover-swap.js * Create bv-hover-swap.spec.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.js * Update bv-hover-swap.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.js * Add missing `@vue/component` comments * Update bv-hover-swap.spec.js * Update bv-hover-swap.js * Merge branch 'hover-swap' of https://github.com/bootstrap-vue/bootstrap-vue into hover-swap * Update bv-hover-swap.spec.js * Update bv-hover-swap.js * Update bv-hover-swap.js * Update bv-hover-swap.js * Update bv-hover-swap.spec.js * Update bv-hover-swap.spec.js Co-authored-by: Jacob Müller --- src/utils/bv-hover-swap.js | 66 ++++++++++++++++++++++ src/utils/bv-hover-swap.spec.js | 99 +++++++++++++++++++++++++++++++++ src/utils/bv-transition.js | 1 + 3 files changed, 166 insertions(+) create mode 100644 src/utils/bv-hover-swap.js create mode 100644 src/utils/bv-hover-swap.spec.js diff --git a/src/utils/bv-hover-swap.js b/src/utils/bv-hover-swap.js new file mode 100644 index 00000000000..b605f17d541 --- /dev/null +++ b/src/utils/bv-hover-swap.js @@ -0,0 +1,66 @@ +import Vue from './vue' +import { eventOn, eventOff } from './dom' + +// --- Constants --- + +const EVENT_OPTIONS = { passive: true } + +// @vue/component +export const BVHoverSwap = /*#__PURE__*/ Vue.extend({ + name: 'BVHoverSwap', + props: { + tag: { + type: String, + default: 'div' + }, + parent: { + type: Boolean, + default: false + } + }, + data() { + return { + isHovered: false + } + }, + watch: { + parent() { + this.listen(true) + } + }, + created() { + // Create non-reactive property + this.$_hoverEl = null + }, + mounted() { + this.$nextTick(() => this.listen(true)) + }, + updated() /* istanbul ignore next */ { + this.$nextTick(() => this.listen(true)) + }, + beforeDestroy() { + this.listen(false) + this.$_hoverEl = null + }, + methods: { + listen(on) { + const el = this.parent ? this.$el.parentElement || this.$el : this.$el + if (on && this.$_hoverEl !== el) { + this.listen(false) + this.$_hoverEl = el + } + const method = on ? eventOn : eventOff + method(this.$_hoverEl, 'mouseenter', this.handleHover, EVENT_OPTIONS) + method(this.$_hoverEl, 'mouseleave', this.handleHover, EVENT_OPTIONS) + }, + handleHover(evt) { + this.isHovered = evt.type === 'mouseenter' + } + }, + render(h) { + const $scoped = this.$scopedSlots + const $default = $scoped.default || (() => h()) + const $hovered = $scoped.hovered || $default + return h(this.tag, [this.isHovered ? $hovered() : $default()]) + } +}) diff --git a/src/utils/bv-hover-swap.spec.js b/src/utils/bv-hover-swap.spec.js new file mode 100644 index 00000000000..484c2554637 --- /dev/null +++ b/src/utils/bv-hover-swap.spec.js @@ -0,0 +1,99 @@ +import { mount } from '@vue/test-utils' +import { waitNT } from '../../tests/utils' +import { BVHoverSwap } from './bv-hover-swap' + +describe('utils/bv-hoverswap', () => { + it('works', async () => { + const wrapper = mount(BVHoverSwap, { + slots: { + default: 'FOO', + hovered: 'BAR' + } + }) + + expect(wrapper.isVueInstance()).toBe(true) + await waitNT(wrapper.vm) + expect(wrapper.is('div')).toBe(true) + expect(wrapper.text()).toBe('FOO') + + wrapper.trigger('mouseenter') + await waitNT(wrapper.vm) + + expect(wrapper.is('div')).toBe(true) + expect(wrapper.text()).toBe('BAR') + + wrapper.trigger('mouseleave') + await waitNT(wrapper.vm) + + expect(wrapper.is('div')).toBe(true) + expect(wrapper.text()).toBe('FOO') + + wrapper.destroy() + }) + + it('works when `parent` is true ', async () => { + const app = { + props: { + parent: { + type: Boolean, + defaut: false + } + }, + methods: { + foo() { + return this.$createElement('span', {}, 'FOO') + }, + bar() { + return this.$createElement('span', {}, 'BAR') + } + }, + render(h) { + const $content = h(BVHoverSwap, { + props: { parent: this.parent }, + scopedSlots: { default: this.foo, hovered: this.bar } + }) + return h('div', {}, [$content]) + } + } + const wrapper = mount(app, { + propsData: { + parent: true + } + }) + + expect(wrapper.isVueInstance()).toBe(true) + await waitNT(wrapper.vm) + expect(wrapper.is('div')).toBe(true) + expect(wrapper.find('div > div').exists()).toBe(true) + expect(wrapper.find('div > div').is(BVHoverSwap)).toBe(true) + expect(wrapper.text()).toBe('FOO') + + wrapper.trigger('mouseenter') + await waitNT(wrapper.vm) + + expect(wrapper.is('div')).toBe(true) + expect(wrapper.text()).toBe('BAR') + + wrapper.trigger('mouseleave') + await waitNT(wrapper.vm) + + expect(wrapper.text()).toBe('FOO') + + wrapper.setProps({ + parent: false + }) + await waitNT(wrapper.vm) + + wrapper.trigger('mouseenter') + await waitNT(wrapper.vm) + + expect(wrapper.text()).toBe('FOO') + + wrapper.find('div > div').trigger('mouseenter') + await waitNT(wrapper.vm) + + expect(wrapper.text()).toBe('BAR') + + wrapper.destroy() + }) +}) diff --git a/src/utils/bv-transition.js b/src/utils/bv-transition.js index 320acb80b7e..b74e2d74f2a 100644 --- a/src/utils/bv-transition.js +++ b/src/utils/bv-transition.js @@ -24,6 +24,7 @@ const FADE_PROPS = { leaveActiveClass: 'fade' } +// @vue/component export const BVTransition = /*#__PURE__*/ Vue.extend({ name: 'BVTransition', functional: true, From 280dec807a715ff268f48c5b232dd18bf1d872cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Thu, 13 Feb 2020 20:33:51 +0100 Subject: [PATCH 02/15] chore: minor tweaks and fixes (#4760) * Remove redundant default values * Seperate events utils, add `eventOnOff()` util and fix `parseEventOptions()` capture option handling * Remove all unused vars * `opts` => `options` * Correct typo in table docs * Update tabs.js * Update bv-hover-swap.js * Update bv-hover-swap.js * Update bv-hover-swap.spec.js * Update jumbotron.js * `hte` => `the` * Update common-props.json * Update events.spec.js Co-authored-by: Troy Morehouse --- .eslintrc.js | 8 ++ docs/common-props.json | 2 +- docs/markdown/reference/images/README.md | 2 +- docs/nuxt.config.js | 2 +- docs/pages/play.vue | 4 +- docs/plugins/play.js | 2 +- docs/utils/index.js | 2 +- nuxt/index.js | 2 +- scripts/postcss.config.js | 2 +- .../button-toolbar/button-toolbar.js | 4 +- .../button-toolbar/button-toolbar.spec.js | 9 +-- src/components/button/button-close.js | 2 +- src/components/button/button.spec.js | 2 +- src/components/carousel/carousel.js | 25 +++--- src/components/carousel/carousel.spec.js | 8 +- src/components/carousel/package.json | 2 +- src/components/collapse/collapse.js | 32 +++----- src/components/collapse/collapse.spec.js | 4 +- src/components/collapse/package.json | 2 +- src/components/dropdown/dropdown-item.spec.js | 2 +- src/components/dropdown/dropdown.spec.js | 4 +- src/components/form-checkbox/form-checkbox.js | 4 +- src/components/form-group/form-group.js | 2 +- src/components/form-input/form-input.js | 19 ++--- src/components/form-input/form-input.spec.js | 2 +- src/components/form-radio/form-radio.js | 2 +- src/components/form-select/form-select.js | 4 +- src/components/form-tags/form-tags.js | 4 +- src/components/form-textarea/form-textarea.js | 2 +- src/components/jumbotron/package.json | 2 +- src/components/link/link.spec.js | 6 +- src/components/media/media.js | 2 +- src/components/modal/README.md | 4 +- src/components/modal/helpers/bv-modal.js | 4 +- src/components/modal/helpers/bv-modal.spec.js | 4 +- src/components/modal/helpers/modal-manager.js | 2 +- src/components/modal/modal.js | 25 ++---- src/components/modal/modal.spec.js | 10 +-- src/components/nav/nav-text.js | 2 +- .../pagination-nav/pagination-nav.js | 6 +- src/components/pagination/pagination.js | 2 +- src/components/table/README.md | 16 ++-- .../table/helpers/mixin-filtering.js | 6 +- .../table/helpers/mixin-provider.js | 2 +- .../table/helpers/mixin-selectable.js | 8 +- src/components/table/helpers/mixin-sorting.js | 6 +- .../table/helpers/mixin-table-renderer.js | 2 +- .../table/helpers/mixin-tbody-row.js | 2 +- src/components/table/table-caption.spec.js | 2 +- src/components/table/table-selectable.spec.js | 2 +- .../table/table-tbody-transition.spec.js | 8 +- src/components/table/tbody.js | 2 +- src/components/table/tfoot.js | 2 +- src/components/table/thead.js | 2 +- src/components/table/tr.js | 4 +- src/components/tabs/tab.js | 2 +- src/components/tabs/tabs.js | 26 +++---- src/components/tabs/tabs.spec.js | 8 +- src/components/toast/README.md | 2 +- src/components/toast/helpers/bv-toast.spec.js | 2 +- src/components/toast/package.json | 2 +- src/components/toast/toast.js | 22 +++--- src/components/tooltip/helpers/bv-tooltip.js | 45 +++++------ src/components/tooltip/tooltip.js | 12 +-- src/directives/modal/modal.js | 26 ++----- src/directives/scrollspy/scrollspy.class.js | 40 +++++----- src/icons/iconstack.spec.js | 2 +- src/mixins/click-out.js | 33 ++++++-- src/mixins/click-out.spec.js | 4 +- src/mixins/focus-in.js | 12 ++- src/mixins/focus-in.spec.js | 4 +- src/mixins/form-radio-check-group.js | 4 +- src/mixins/form-radio-check.js | 2 +- src/mixins/listen-on-document.js | 10 +-- src/mixins/listen-on-document.spec.js | 8 +- src/mixins/listen-on-root.spec.js | 4 +- src/mixins/listen-on-window.js | 10 +-- src/mixins/listen-on-window.spec.js | 6 +- src/utils/bv-hover-swap.js | 11 +-- src/utils/bv-hover-swap.spec.js | 4 +- src/utils/bv-transition.js | 2 +- src/utils/dom.js | 30 +------- src/utils/dom.spec.js | 77 +++---------------- src/utils/events.js | 42 ++++++++++ src/utils/events.spec.js | 41 ++++++++++ src/utils/observe-dom.js | 10 ++- src/utils/plugins.js | 10 +-- src/utils/target.js | 2 +- src/utils/transporter.spec.js | 2 +- 89 files changed, 389 insertions(+), 422 deletions(-) create mode 100644 src/utils/events.js create mode 100644 src/utils/events.spec.js diff --git a/.eslintrc.js b/.eslintrc.js index 0222428eadc..8116ec010d8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,6 +14,14 @@ module.exports = { Vue: true }, rules: { + 'no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: false + } + ], 'spaced-comment': 'off', // needed to ignore `/*#__PURE__*/` comments 'vue/html-self-closing': [ 'error', diff --git a/docs/common-props.json b/docs/common-props.json index ea442f7f909..4c3bca079c0 100644 --- a/docs/common-props.json +++ b/docs/common-props.json @@ -126,7 +126,7 @@ "description": "Sets the 'placeholder' attribute value on the form control" }, "readonly": { - "description": "Sets the 'readonly' attribute on hte form control" + "description": "Sets the 'readonly' attribute on the form control" }, "plaintext": { "description": "Set the form control as readonly and renders the control to look like plain text (no borders)" diff --git a/docs/markdown/reference/images/README.md b/docs/markdown/reference/images/README.md index 8f6f20f499c..3abf0fd0e0c 100644 --- a/docs/markdown/reference/images/README.md +++ b/docs/markdown/reference/images/README.md @@ -97,7 +97,7 @@ In your `nuxt.config.js` file, add the following to your build section: ```js module.exports = { build: { - extend(config, ctx) { + extend(config) { const vueLoader = config.module.rules.find(rule => rule.loader === 'vue-loader') vueLoader.options.transformAssetUrls = { video: ['src', 'poster'], diff --git a/docs/nuxt.config.js b/docs/nuxt.config.js index 471f99dc4d7..fc961193a4b 100644 --- a/docs/nuxt.config.js +++ b/docs/nuxt.config.js @@ -99,7 +99,7 @@ renderer.blockquote = function(text) { // Bootstrap v4 table support for markdown renderer const originalTable = renderer.table -renderer.table = function(header, body) { +renderer.table = function() { let table = originalTable.apply(this, arguments) table = table .replace('', '
') diff --git a/docs/pages/play.vue b/docs/pages/play.vue index a1fd369b7c5..444d245b59b 100644 --- a/docs/pages/play.vue +++ b/docs/pages/play.vue @@ -597,7 +597,7 @@ export default { // appData watcher this.contentUnWatch = this.$watch( 'appData', - (newVal, oldVal) => { + () => { this.run() }, { deep: true } @@ -605,7 +605,7 @@ export default { // Javascript watcher this.jsUnWatch = this.$watch( () => this.js.trim(), - (newVal, oldVal) => { + () => { this.compileJs() }, { immediate: true } diff --git a/docs/plugins/play.js b/docs/plugins/play.js index 3e4bbf32a95..ced385c6bb0 100644 --- a/docs/plugins/play.js +++ b/docs/plugins/play.js @@ -86,7 +86,7 @@ const destroyVM = (name, vm) => { ;[...document.querySelectorAll(`.vue-example-${name}`)].forEach(removeNode) } -const processExamples = (el, binding, vnode, oldVnode) => { +const processExamples = (el, binding, vnode) => { if (vnode.context.$options.beforeDestroy) { vnode.context.$options.beforeDestroy = [] .concat(vnode.context.$options.beforeDestroy) diff --git a/docs/utils/index.js b/docs/utils/index.js index 5bd63e83a3c..2813b077852 100644 --- a/docs/utils/index.js +++ b/docs/utils/index.js @@ -113,7 +113,7 @@ export const makeTOC = (readme, meta = null) => { // Filter out un-matched values .filter(v => Array.isArray(v)) // Create TOC structure - .forEach(([value, tag, id, content]) => { + .forEach(([, tag, id, content]) => { const href = `#${stripQuotes(id)}` const label = stripHTML(content) if (tag === 'h2') { diff --git a/nuxt/index.js b/nuxt/index.js index 875dd02cf3a..60a99fa4007 100644 --- a/nuxt/index.js +++ b/nuxt/index.js @@ -92,7 +92,7 @@ module.exports = function nuxtBootstrapVue(moduleOptions = {}) { if (!usePretranspiled) { // Use bootstrap-vue source code for smaller prod builds // by aliasing 'bootstrap-vue' to the source files - this.extendBuild((config, { isServer }) => { + this.extendBuild(config => { if (!config.resolve.alias) { config.resolve.alias = {} } diff --git a/scripts/postcss.config.js b/scripts/postcss.config.js index 07054a6ff80..fb139c97fca 100644 --- a/scripts/postcss.config.js +++ b/scripts/postcss.config.js @@ -1,4 +1,4 @@ -module.exports = ctx => ({ +module.exports = () => ({ map: { inline: false, annotation: true, diff --git a/src/components/button-toolbar/button-toolbar.js b/src/components/button-toolbar/button-toolbar.js index a56f591b4dc..77be692fa2d 100644 --- a/src/components/button-toolbar/button-toolbar.js +++ b/src/components/button-toolbar/button-toolbar.js @@ -61,7 +61,7 @@ export const BButtonToolbar = /*#__PURE__*/ Vue.extend({ setItemFocus(item) { item && item.focus && item.focus() }, - focusFirst(evt) { + focusFirst() { const items = this.getItems() this.setItemFocus(items[0]) }, @@ -81,7 +81,7 @@ export const BButtonToolbar = /*#__PURE__*/ Vue.extend({ this.setItemFocus(items[0]) } }, - focusLast(evt) { + focusLast() { const items = this.getItems().reverse() this.setItemFocus(items[0]) }, diff --git a/src/components/button-toolbar/button-toolbar.spec.js b/src/components/button-toolbar/button-toolbar.spec.js index c3806f8da6e..15f0fd0c0df 100644 --- a/src/components/button-toolbar/button-toolbar.spec.js +++ b/src/components/button-toolbar/button-toolbar.spec.js @@ -85,12 +85,9 @@ describe('button-toolbar', () => { const App = Vue.extend({ render(h) { return h(BButtonToolbar, { props: { keyNav: true } }, [ - h(BButtonGroup, {}, [h(BButton, {}, 'a'), h(BButton, {}, 'b')]), - h(BButtonGroup, {}, [ - h(BButton, { props: { disabled: true } }, 'c'), - h(BButton, {}, 'd') - ]), - h(BButtonGroup, {}, [h(BButton, {}, 'e'), h(BButton, {}, 'f')]) + h(BButtonGroup, [h(BButton, 'a'), h(BButton, 'b')]), + h(BButtonGroup, [h(BButton, { props: { disabled: true } }, 'c'), h(BButton, 'd')]), + h(BButtonGroup, [h(BButton, 'e'), h(BButton, 'f')]) ]) } }) diff --git a/src/components/button/button-close.js b/src/components/button/button-close.js index 5b749ef4008..2c16d7300f7 100644 --- a/src/components/button/button-close.js +++ b/src/components/button/button-close.js @@ -30,7 +30,7 @@ export const BButtonClose = /*#__PURE__*/ Vue.extend({ name: NAME, functional: true, props, - render(h, { props, data, listeners, slots, scopedSlots }) { + render(h, { props, data, slots, scopedSlots }) { const $slots = slots() const $scopedSlots = scopedSlots || {} diff --git a/src/components/button/button.spec.js b/src/components/button/button.spec.js index 681bc002519..9fff0139ce9 100644 --- a/src/components/button/button.spec.js +++ b/src/components/button/button.spec.js @@ -208,7 +208,7 @@ describe('button', () => { disabled: true }, listeners: { - click: e => { + click: () => { called++ } } diff --git a/src/components/carousel/carousel.js b/src/components/carousel/carousel.js index 12de4a25ed4..db382fdac7d 100644 --- a/src/components/carousel/carousel.js +++ b/src/components/carousel/carousel.js @@ -3,16 +3,9 @@ import KeyCodes from '../../utils/key-codes' import noop from '../../utils/noop' import observeDom from '../../utils/observe-dom' import { getComponentConfig } from '../../utils/config' -import { - selectAll, - reflow, - addClass, - removeClass, - setAttr, - eventOn, - eventOff -} from '../../utils/dom' +import { selectAll, reflow, addClass, removeClass, setAttr } from '../../utils/dom' import { isBrowser, hasTouchSupport, hasPointerEventSupport } from '../../utils/env' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../../utils/events' import { isUndefined } from '../../utils/inspect' import { toInteger } from '../../utils/number' import idMixin from '../../mixins/id' @@ -55,8 +48,6 @@ const TransitionEndEvents = { transition: 'transitionend' } -const EventOptions = { passive: true, capture: false } - // Return the browser specific transitionEnd event name const getTransitionEndEvent = el => { for (const name in TransitionEndEvents) { @@ -308,7 +299,7 @@ export const BCarousel = /*#__PURE__*/ Vue.extend({ } }, // Restart auto rotate slides when focus/hover leaves the carousel - restart(evt) /* istanbul ignore next: difficult to test */ { + restart() /* istanbul ignore next: difficult to test */ { if (!this.$el.contains(document.activeElement)) { this.start() } @@ -350,7 +341,7 @@ export const BCarousel = /*#__PURE__*/ Vue.extend({ // Transition End handler let called = false /* istanbul ignore next: difficult to test */ - const onceTransEnd = evt => { + const onceTransEnd = () => { if (called) { return } @@ -358,7 +349,9 @@ export const BCarousel = /*#__PURE__*/ Vue.extend({ /* istanbul ignore if: transition events cant be tested in JSDOM */ if (this.transitionEndEvent) { const events = this.transitionEndEvent.split(/\s+/) - events.forEach(evt => eventOff(currentSlide, evt, onceTransEnd, EventOptions)) + events.forEach(evt => + eventOff(currentSlide, evt, onceTransEnd, EVENT_OPTIONS_NO_CAPTURE) + ) } this._animationTimeout = null removeClass(nextSlide, dirClass) @@ -380,7 +373,9 @@ export const BCarousel = /*#__PURE__*/ Vue.extend({ /* istanbul ignore if: transition events cant be tested in JSDOM */ if (this.transitionEndEvent) { const events = this.transitionEndEvent.split(/\s+/) - events.forEach(event => eventOn(currentSlide, event, onceTransEnd, EventOptions)) + events.forEach(event => + eventOn(currentSlide, event, onceTransEnd, EVENT_OPTIONS_NO_CAPTURE) + ) } // Fallback to setTimeout() this._animationTimeout = setTimeout(onceTransEnd, TRANS_DURATION) diff --git a/src/components/carousel/carousel.spec.js b/src/components/carousel/carousel.spec.js index 6fc89d0cea5..9784a01e609 100644 --- a/src/components/carousel/carousel.spec.js +++ b/src/components/carousel/carousel.spec.js @@ -32,10 +32,10 @@ const appDef = { } }, [ - h(BCarouselSlide, {}, 'slide 1'), - h(BCarouselSlide, {}, 'slide 2'), - h(BCarouselSlide, {}, 'slide 3'), - h(BCarouselSlide, {}, 'slide 4') + h(BCarouselSlide, 'slide 1'), + h(BCarouselSlide, 'slide 2'), + h(BCarouselSlide, 'slide 3'), + h(BCarouselSlide, 'slide 4') ] ) } diff --git a/src/components/carousel/package.json b/src/components/carousel/package.json index 1f5a5fc3fc0..61999b20068 100644 --- a/src/components/carousel/package.json +++ b/src/components/carousel/package.json @@ -36,7 +36,7 @@ }, { "prop": "noHoverPause", - "description": "When set, disables the pausing of hte slide show when the current slide is hovered" + "description": "When set, disables the pausing of the slide show when the current slide is hovered" }, { "prop": "interval", diff --git a/src/components/collapse/collapse.js b/src/components/collapse/collapse.js index 50728b886b9..7ad53694992 100644 --- a/src/components/collapse/collapse.js +++ b/src/components/collapse/collapse.js @@ -1,19 +1,11 @@ import Vue from '../../utils/vue' +import { isBrowser } from '../../utils/env' +import { addClass, hasClass, removeClass, closest, matches, getCS } from '../../utils/dom' +import { EVENT_OPTIONS_NO_CAPTURE, eventOnOff } from '../../utils/events' +import { BVCollapse } from '../../utils/bv-collapse' import idMixin from '../../mixins/id' import listenOnRootMixin from '../../mixins/listen-on-root' import normalizeSlotMixin from '../../mixins/normalize-slot' -import { isBrowser } from '../../utils/env' -import { BVCollapse } from '../../utils/bv-collapse' -import { - addClass, - hasClass, - removeClass, - closest, - matches, - getCS, - eventOn, - eventOff -} from '../../utils/dom' // Events we emit on $root const EVENT_STATE = 'bv::collapse::state' @@ -26,9 +18,6 @@ const EVENT_STATE_SYNC = 'bv::collapse::sync::state' const EVENT_TOGGLE = 'bv::toggle::collapse' const EVENT_STATE_REQUEST = 'bv::request::collapse::state' -// Event listener options -const EventOptions = { passive: true, capture: false } - // @vue/component export const BCollapse = /*#__PURE__*/ Vue.extend({ name: 'BCollapse', @@ -137,28 +126,27 @@ export const BCollapse = /*#__PURE__*/ Vue.extend({ }, methods: { setWindowEvents(on) { - const method = on ? eventOn : eventOff - method(window, 'resize', this.handleResize, EventOptions) - method(window, 'orientationchange', this.handleResize, EventOptions) + eventOnOff(on, window, 'resize', this.handleResize, EVENT_OPTIONS_NO_CAPTURE) + eventOnOff(on, window, 'orientationchange', this.handleResize, EVENT_OPTIONS_NO_CAPTURE) }, toggle() { this.show = !this.show }, - onEnter(el) { + onEnter() { this.transitioning = true // This should be moved out so we can add cancellable events this.$emit('show') }, - onAfterEnter(el) { + onAfterEnter() { this.transitioning = false this.$emit('shown') }, - onLeave(el) { + onLeave() { this.transitioning = true // This should be moved out so we can add cancellable events this.$emit('hide') }, - onAfterLeave(el) { + onAfterLeave() { this.transitioning = false this.$emit('hidden') }, diff --git a/src/components/collapse/collapse.spec.js b/src/components/collapse/collapse.spec.js index ce544fa3c2c..a8872a4956f 100644 --- a/src/components/collapse/collapse.spec.js +++ b/src/components/collapse/collapse.spec.js @@ -411,7 +411,7 @@ describe('collapse', () => { const localVue = CreateLocalVue() const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ // JSDOM supports getComputedStyle when using stylesheets (non responsive) // https://github.com/jsdom/jsdom/blob/master/Changelog.md#030 h('style', { attrs: { type: 'text/css' } }, '.collapse:not(.show) { display: none; }'), @@ -471,7 +471,7 @@ describe('collapse', () => { const localVue = CreateLocalVue() const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ // JSDOM supports getComputedStyle when using stylesheets (non responsive) // Although it appears to be picky about CSS definition ordering // https://github.com/jsdom/jsdom/blob/master/Changelog.md#030 diff --git a/src/components/collapse/package.json b/src/components/collapse/package.json index 9880bd0adc0..53bb7aef493 100644 --- a/src/components/collapse/package.json +++ b/src/components/collapse/package.json @@ -24,7 +24,7 @@ "props": [ { "prop": "isNav", - "description": "When set, signifies that hte collapse is bart of a navbar, enabling certain features for navbar support" + "description": "When set, signifies that the collapse is bart of a navbar, enabling certain features for navbar support" }, { "prop": "accordion", diff --git a/src/components/dropdown/dropdown-item.spec.js b/src/components/dropdown/dropdown-item.spec.js index caab23759ff..5a1a3a50149 100644 --- a/src/components/dropdown/dropdown-item.spec.js +++ b/src/components/dropdown/dropdown-item.spec.js @@ -96,7 +96,7 @@ describe('dropdown-item', () => { const App = localVue.extend({ router, render(h) { - return h('ul', {}, [ + return h('ul', [ // router-link h(BDropdownItem, { props: { to: '/a' } }, ['to-a']), // regular link diff --git a/src/components/dropdown/dropdown.spec.js b/src/components/dropdown/dropdown.spec.js index 12ea2d28bb8..1c41b5947ae 100644 --- a/src/components/dropdown/dropdown.spec.js +++ b/src/components/dropdown/dropdown.spec.js @@ -433,7 +433,7 @@ describe('dropdown', () => { const App = localVue.extend({ render(h) { return h('div', { attrs: { id: 'container' } }, [ - h(BDropdown, { props: { id: 'test' } }, [h(BDropdownItem, {}, 'item')]), + h(BDropdown, { props: { id: 'test' } }, [h(BDropdownItem, 'item')]), h('input', { attrs: { id: 'input' } }) ]) } @@ -666,7 +666,7 @@ describe('dropdown', () => { const localVue = new CreateLocalVue() const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h(BDropdown, { props: { id: 'test' } }, [ h(BDropdownItem, { attrs: { id: 'item-1' } }, 'item'), h(BDropdownItem, { attrs: { id: 'item-2' } }, 'item'), diff --git a/src/components/form-checkbox/form-checkbox.js b/src/components/form-checkbox/form-checkbox.js index 31cf9ac7aec..9b66df74f2e 100644 --- a/src/components/form-checkbox/form-checkbox.js +++ b/src/components/form-checkbox/form-checkbox.js @@ -68,13 +68,13 @@ export const BFormCheckbox = /*#__PURE__*/ Vue.extend({ } }, watch: { - computedLocalChecked(newVal, oldVal) { + computedLocalChecked(newVal) { this.$emit('input', newVal) if (this.$refs && this.$refs.input) { this.$emit('update:indeterminate', this.$refs.input.indeterminate) } }, - indeterminate(newVal, oldVal) { + indeterminate(newVal) { this.setIndeterminate(newVal) } }, diff --git a/src/components/form-group/form-group.js b/src/components/form-group/form-group.js index be2b47ee57c..7c7786dad62 100644 --- a/src/components/form-group/form-group.js +++ b/src/components/form-group/form-group.js @@ -447,7 +447,7 @@ export const BFormGroup = { return h( isFieldset ? 'fieldset' : isHorizontal ? BFormRow : 'div', data, - isHorizontal && isFieldset ? [h(BFormRow, {}, [label, content])] : [label, content] + isHorizontal && isFieldset ? [h(BFormRow, [label, content])] : [label, content] ) } } diff --git a/src/components/form-input/form-input.js b/src/components/form-input/form-input.js index 5e33d8ac0ad..47e736975ef 100644 --- a/src/components/form-input/form-input.js +++ b/src/components/form-input/form-input.js @@ -1,4 +1,6 @@ import Vue from '../../utils/vue' +import { arrayIncludes } from '../../utils/array' +import { eventOn, eventOff, eventOnOff } from '../../utils/events' import idMixin from '../../mixins/id' import formMixin from '../../mixins/form' import formSizeMixin from '../../mixins/form-size' @@ -6,8 +8,6 @@ import formStateMixin from '../../mixins/form-state' import formTextMixin from '../../mixins/form-text' import formSelectionMixin from '../../mixins/form-selection' import formValidityMixin from '../../mixins/form-validity' -import { arrayIncludes } from '../../utils/array' -import { eventOn, eventOff } from '../../utils/dom' // Valid supported input types const TYPES = [ @@ -101,20 +101,17 @@ export const BFormInput = /*#__PURE__*/ Vue.extend({ methods: { setWheelStopper(on) { const input = this.$el - // We use native events, so that we don't interfere with propgation - if (on) { - eventOn(input, 'focus', this.onWheelFocus) - eventOn(input, 'blur', this.onWheelBlur) - } else { - eventOff(input, 'focus', this.onWheelFocus) - eventOff(input, 'blur', this.onWheelBlur) + // We use native events, so that we don't interfere with propagation + eventOnOff(on, input, 'focus', this.onWheelFocus) + eventOnOff(on, input, 'blur', this.onWheelBlur) + if (!on) { eventOff(document, 'wheel', this.stopWheel) } }, - onWheelFocus(evt) { + onWheelFocus() { eventOn(document, 'wheel', this.stopWheel) }, - onWheelBlur(evt) { + onWheelBlur() { eventOff(document, 'wheel', this.stopWheel) }, stopWheel(evt) { diff --git a/src/components/form-input/form-input.spec.js b/src/components/form-input/form-input.spec.js index b97bbc13d1e..2297034868c 100644 --- a/src/components/form-input/form-input.spec.js +++ b/src/components/form-input/form-input.spec.js @@ -552,7 +552,7 @@ describe('form-input', () => { const wrapper = mount(BFormInput, { propsData: { value: 'abc', - formatter(value) { + formatter() { return false } }, diff --git a/src/components/form-radio/form-radio.js b/src/components/form-radio/form-radio.js index e48a72d05d8..2b0ccac5f06 100644 --- a/src/components/form-radio/form-radio.js +++ b/src/components/form-radio/form-radio.js @@ -44,7 +44,7 @@ export const BFormRadio = /*#__PURE__*/ Vue.extend({ }, watch: { // Radio Groups can only have a single value, so our watchers are simple - computedLocalChecked(newVal, oldVal) { + computedLocalChecked() { this.$emit('input', this.computedLocalChecked) } }, diff --git a/src/components/form-select/form-select.js b/src/components/form-select/form-select.js index 5ad5de41f98..85289bcb185 100644 --- a/src/components/form-select/form-select.js +++ b/src/components/form-select/form-select.js @@ -74,10 +74,10 @@ export const BFormSelect = /*#__PURE__*/ Vue.extend({ } }, watch: { - value(newVal, oldVal) { + value(newVal) { this.localValue = newVal }, - localValue(newVal, oldVal) { + localValue() { this.$emit('input', this.localValue) } }, diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index cdecbc6e176..cb508f4aac9 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -493,9 +493,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ invalidTagText, duplicateTagText, isInvalid, - invalidTags, isDuplicate, - duplicateTags, disabled, placeholder, addButtonText, @@ -505,7 +503,7 @@ export const BFormTags = /*#__PURE__*/ Vue.extend({ const h = this.$createElement // Make the list of tags - const $tags = tags.map((tag, idx) => { + const $tags = tags.map(tag => { tag = toString(tag) return h( BFormTag, diff --git a/src/components/form-textarea/form-textarea.js b/src/components/form-textarea/form-textarea.js index 1c874354cbf..c78cfe5cd9e 100644 --- a/src/components/form-textarea/form-textarea.js +++ b/src/components/form-textarea/form-textarea.js @@ -90,7 +90,7 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({ } }, watch: { - localValue(newVal, oldVal) { + localValue() { this.setHeight() } }, diff --git a/src/components/jumbotron/package.json b/src/components/jumbotron/package.json index e10238e46fc..b0d2999152b 100644 --- a/src/components/jumbotron/package.json +++ b/src/components/jumbotron/package.json @@ -18,7 +18,7 @@ }, { "prop": "headerLevel", - "description": "Scaling factor of hte header. Values range from 1 to 5" + "description": "Scaling factor of the header. Values range from 1 to 5" }, { "prop": "lead", diff --git a/src/components/link/link.spec.js b/src/components/link/link.spec.js index 2efb1bd5b3a..8dc55e903f1 100644 --- a/src/components/link/link.spec.js +++ b/src/components/link/link.spec.js @@ -282,7 +282,7 @@ describe('b-link', () => { it('should emit "clicked::link" on $root when clicked on', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [h(BLink, { props: { href: '/foo' } }, 'link')]) + return h('div', [h(BLink, { props: { href: '/foo' } }, 'link')]) } }) const spy = jest.fn() @@ -299,7 +299,7 @@ describe('b-link', () => { it('should NOT emit "clicked::link" on $root when clicked on when disabled', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [h(BLink, { props: { href: '/foo', disabled: true } }, 'link')]) + return h('div', [h(BLink, { props: { href: '/foo', disabled: true } }, 'link')]) } }) const spy = jest.fn() @@ -335,7 +335,7 @@ describe('b-link', () => { router, components: { BLink }, render(h) { - return h('main', {}, [ + return h('main', [ // router-link h('b-link', { props: { to: '/a' } }, ['to-a']), // regular link diff --git a/src/components/media/media.js b/src/components/media/media.js index 83123b959fe..b5770a8d106 100644 --- a/src/components/media/media.js +++ b/src/components/media/media.js @@ -47,7 +47,7 @@ export const BMedia = /*#__PURE__*/ Vue.extend({ ) } - childNodes.push(h(BMediaBody, {}, $default)) + childNodes.push(h(BMediaBody, $default)) if ($aside && props.rightAlign) { childNodes.push( diff --git a/src/components/modal/README.md b/src/components/modal/README.md index 93f82cb7eef..2c3d2e5cfd8 100644 --- a/src/components/modal/README.md +++ b/src/components/modal/README.md @@ -972,7 +972,7 @@ method to generate VNodes. const messageVNode = h('div', { class: ['foobar'] }, [ h('p', { class: ['text-center'] }, [ ' Flashy ', - h('strong', {}, 'msgBoxOk'), + h('strong', 'msgBoxOk'), ' message ', ]), h('p', { class: ['text-center'] }, [h('b-spinner')]), @@ -1069,7 +1069,7 @@ already has focus within the ``. ```js export default { methods: { - focusMyElement(e) { + focusMyElement() { this.$refs.focusThis.focus() } } diff --git a/src/components/modal/helpers/bv-modal.js b/src/components/modal/helpers/bv-modal.js index a2ed0cafa0d..27d4875bdf9 100644 --- a/src/components/modal/helpers/bv-modal.js +++ b/src/components/modal/helpers/bv-modal.js @@ -29,7 +29,7 @@ const BASE_PROPS = [ ] // Fallback event resolver (returns undefined) -const defaultResolver = bvModalEvt => {} +const defaultResolver = () => {} // Map prop names to modal slot names const propsToSlots = { @@ -211,7 +211,7 @@ const plugin = Vue => { hideFooter: false, msgBoxContent: message } - return makeMsgBox(this._vm, message, props, bvModalEvt => { + return makeMsgBox(this._vm, message, props, () => { // Always resolve to true for OK return true }) diff --git a/src/components/modal/helpers/bv-modal.spec.js b/src/components/modal/helpers/bv-modal.spec.js index 44079eb931f..e8087d703bd 100644 --- a/src/components/modal/helpers/bv-modal.spec.js +++ b/src/components/modal/helpers/bv-modal.spec.js @@ -60,7 +60,7 @@ describe('$bvModal', () => { it('$bvModal.msgBoxOk() works', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, 'app') + return h('div', 'app') } }) const wrapper = mount(App, { @@ -122,7 +122,7 @@ describe('$bvModal', () => { it('$bvModal.msgBoxConfirm() works', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, 'app') + return h('div', 'app') } }) const wrapper = mount(App, { diff --git a/src/components/modal/helpers/modal-manager.js b/src/components/modal/helpers/modal-manager.js index 2fe251b5555..a6f33148a25 100644 --- a/src/components/modal/helpers/modal-manager.js +++ b/src/components/modal/helpers/modal-manager.js @@ -67,7 +67,7 @@ const ModalManager = /*#__PURE__*/ Vue.extend({ setAttr(document.body, 'data-modal-open-count', String(newCount)) } }, - modals(newVal, oldVal) { + modals(newVal) { this.checkScrollbar() requestAF(() => { this.updateModals(newVal || []) diff --git a/src/components/modal/modal.js b/src/components/modal/modal.js index 80372b80dcf..a82d9f58dba 100644 --- a/src/components/modal/modal.js +++ b/src/components/modal/modal.js @@ -5,17 +5,9 @@ import identity from '../../utils/identity' import observeDom from '../../utils/observe-dom' import { arrayIncludes, concat } from '../../utils/array' import { getComponentConfig } from '../../utils/config' -import { - closest, - contains, - eventOff, - eventOn, - isVisible, - requestAF, - select, - selectAll -} from '../../utils/dom' +import { closest, contains, isVisible, requestAF, select, selectAll } from '../../utils/dom' import { isBrowser } from '../../utils/env' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../../utils/events' import { stripTags } from '../../utils/html' import { isString, isUndefinedOrNull } from '../../utils/inspect' import { HTMLElement } from '../../utils/safe-types' @@ -45,9 +37,6 @@ const OBSERVER_CONFIG = { attributeFilter: ['style', 'class'] } -// Options for DOM event listeners -const EVT_OPTIONS = { passive: true, capture: false } - // Query selector to find all tabbable elements // (includes tabindex="-1", which we filter out after) const TABABLE_SELECTOR = [ @@ -458,7 +447,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({ } }, // Private method to create a BvModalEvent object - buildEvent(type, opts = {}) { + buildEvent(type, options = {}) { return new BvModalEvent(type, { // Default options cancelable: false, @@ -466,7 +455,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({ relatedTarget: null, trigger: null, // Supplied options - ...opts, + ...options, // Options that can't be overridden vueTarget: this, componentId: this.safeId() @@ -671,12 +660,12 @@ export const BModal = /*#__PURE__*/ Vue.extend({ // And if it does, cancel the clickOut handler const modal = this.$refs.modal const onceModalMouseup = evt => { - eventOff(modal, 'mouseup', onceModalMouseup, EVT_OPTIONS) + eventOff(modal, 'mouseup', onceModalMouseup, EVENT_OPTIONS_NO_CAPTURE) if (evt.target === modal) { this.ignoreBackdropClick = true } } - eventOn(modal, 'mouseup', onceModalMouseup, EVT_OPTIONS) + eventOn(modal, 'mouseup', onceModalMouseup, EVENT_OPTIONS_NO_CAPTURE) }, onClickOut(evt) { if (this.ignoreBackdropClick) { @@ -1081,7 +1070,7 @@ export const BModal = /*#__PURE__*/ Vue.extend({ if (this.static) { return this.lazy && this.isHidden ? h() : this.makeModal(h) } else { - return this.isHidden ? h() : h(BTransporterSingle, {}, [this.makeModal(h)]) + return this.isHidden ? h() : h(BTransporterSingle, [this.makeModal(h)]) } } }) diff --git a/src/components/modal/modal.spec.js b/src/components/modal/modal.spec.js index 677abde0e81..167d5e5f119 100644 --- a/src/components/modal/modal.spec.js +++ b/src/components/modal/modal.spec.js @@ -994,7 +994,7 @@ describe('modal', () => { it('returns focus to previous active element when return focus not set and not using v-b-toggle', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h('button', { class: 'trigger', attrs: { id: 'trigger', type: 'button' } }, 'trigger'), h(BModal, { props: { static: true, id: 'test', visible: false } }, 'modal content') ]) @@ -1071,7 +1071,7 @@ describe('modal', () => { it('returns focus to element specified in toggle() method', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h('button', { class: 'trigger', attrs: { id: 'trigger', type: 'button' } }, 'trigger'), h( 'button', @@ -1160,7 +1160,7 @@ describe('modal', () => { it('if focus leaves modal it returns to modal', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h('button', { attrs: { id: 'button', type: 'button' } }, 'Button'), h(BModal, { props: { static: true, id: 'test', visible: true } }, 'Modal content') ]) @@ -1247,7 +1247,7 @@ describe('modal', () => { it('it allows focus for elements when "no-enforce-focus" enabled', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h('button', { attrs: { id: 'button1', type: 'button' } }, 'Button 1'), h('button', { attrs: { id: 'button2', type: 'button' } }, 'Button 2'), h( @@ -1321,7 +1321,7 @@ describe('modal', () => { it('it allows focus for elements in "ignore-enforce-focus-selector" prop', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, [ + return h('div', [ h('button', { attrs: { id: 'button1', type: 'button' } }, 'Button 1'), h('button', { attrs: { id: 'button2', type: 'button' } }, 'Button 2'), h( diff --git a/src/components/nav/nav-text.js b/src/components/nav/nav-text.js index f75aae55aff..696243559f9 100644 --- a/src/components/nav/nav-text.js +++ b/src/components/nav/nav-text.js @@ -8,7 +8,7 @@ export const BNavText = /*#__PURE__*/ Vue.extend({ name: 'BNavText', functional: true, props, - render(h, { props, data, children }) { + render(h, { data, children }) { return h('li', mergeData(data, { staticClass: 'navbar-text' }), children) } }) diff --git a/src/components/pagination-nav/pagination-nav.js b/src/components/pagination-nav/pagination-nav.js index ee4f4570e95..638e4302297 100644 --- a/src/components/pagination-nav/pagination-nav.js +++ b/src/components/pagination-nav/pagination-nav.js @@ -99,12 +99,12 @@ export const BPaginationNav = /*#__PURE__*/ Vue.extend({ } }, watch: { - numberOfPages(newVal, oldVal) { + numberOfPages() { this.$nextTick(() => { this.setNumberOfPages() }) }, - pages(newVal, oldVal) { + pages() { this.$nextTick(() => { this.setNumberOfPages() }) @@ -116,7 +116,7 @@ export const BPaginationNav = /*#__PURE__*/ Vue.extend({ mounted() { if (this.$router) { // We only add the watcher if vue router is detected - this.$watch('$route', (to, from) => { + this.$watch('$route', () => { this.$nextTick(() => { requestAF(() => { this.guessCurrentPage() diff --git a/src/components/pagination/pagination.js b/src/components/pagination/pagination.js index f59acf713c9..94c659b2d8c 100644 --- a/src/components/pagination/pagination.js +++ b/src/components/pagination/pagination.js @@ -131,7 +131,7 @@ export const BPagination = /*#__PURE__*/ Vue.extend({ makePage(pageNum) { return pageNum }, - linkProps(pageNum) { + linkProps() { // Always '#' for pagination component return { href: '#' } } diff --git a/src/components/table/README.md b/src/components/table/README.md index a2e454f50a3..024a325d401 100644 --- a/src/components/table/README.md +++ b/src/components/table/README.md @@ -578,16 +578,16 @@ values: `sm`, `md`, `lg`, or `xl`. - Using props `responsive` and `fixed` together will **not** work as expected. Fixed table layout uses the first row (table header in this case) to compute the width required by each column (and the overall table width) to fit within the width of the parent container — without taking - cells in the `` into consideration — resulting in table that may not be resposive. To - get around this limitation, you would need to specify widths for the columns (or certain columns) - via one of the following methods: + cells in the `` into consideration — resulting in table that may not be responsive. + To get around this limitation, you would need to specify widths for the columns (or certain + columns) via one of the following methods: - Use `` elements within the [`table-colgroup` slot](#table-colgroup) that have widths set (e.g. ``), or - Wrap header cells in `
` elements, via the use of [custom header rendering](#header-and-footer-custom-rendering-via-scoped-slots), which have a minimum width set on them, or - - Use the `thStyle` property of the [field definition object](#field-definition-reference) to - set a width for the column(s), or + - Use the `thStyle` property of the [field definition object](#field-definition-reference) to set + a width for the column(s), or - Use custom CSS to define classes to apply to the columns to set widths, via the `thClass` or `class` properties of the [field definition object](#field-definition-reference). @@ -2221,7 +2221,7 @@ method. ```js -function myProvider(ctx) { +function myProvider() { let items = [] // Perform any items processing needed @@ -2306,7 +2306,7 @@ function should handle errors from data sources and return an empty array to ` + ```js export default { methods: { diff --git a/src/components/table/helpers/mixin-filtering.js b/src/components/table/helpers/mixin-filtering.js index c2684334c49..59824f66bf3 100644 --- a/src/components/table/helpers/mixin-filtering.js +++ b/src/components/table/helpers/mixin-filtering.js @@ -98,7 +98,7 @@ export default { }, watch: { // Watch for debounce being set to 0 - computedFilterDebounce(newVal, oldVal) { + computedFilterDebounce(newVal) { if (!newVal && this.$_filterTimer) { clearTimeout(this.$_filterTimer) this.$_filterTimer = null @@ -110,7 +110,7 @@ export default { // We need a deep watcher in case the user passes // an object when using `filter-function` deep: true, - handler(newCriteria, oldCriteria) { + handler(newCriteria) { const timeout = this.computedFilterDebounce clearTimeout(this.$_filterTimer) this.$_filterTimer = null @@ -127,7 +127,7 @@ export default { }, // Watch for changes to the filter criteria and filtered items vs `localItems` // Set visual state and emit events as required - filteredCheck({ filteredItems, localItems, localFilter }) { + filteredCheck({ filteredItems, localFilter }) { // Determine if the dataset is filtered or not let isFiltered = false if (!localFilter) { diff --git a/src/components/table/helpers/mixin-provider.js b/src/components/table/helpers/mixin-provider.js index b56a762f25e..1dd3111bb62 100644 --- a/src/components/table/helpers/mixin-provider.js +++ b/src/components/table/helpers/mixin-provider.js @@ -68,7 +68,7 @@ export default { }, watch: { // Provider update triggering - items(newVal, oldVal) { + items(newVal) { // If a new provider has been specified, trigger an update if (this.hasProvider || isFunction(newVal)) { this.$nextTick(this._providerUpdate) diff --git a/src/components/table/helpers/mixin-selectable.js b/src/components/table/helpers/mixin-selectable.js index 9051a3386d9..489bcdf5148 100644 --- a/src/components/table/helpers/mixin-selectable.js +++ b/src/components/table/helpers/mixin-selectable.js @@ -91,14 +91,14 @@ export default { this.clearSelected() } }, - selectable(newVal, oldVal) { + selectable(newVal) { this.clearSelected() this.setSelectionHandlers(newVal) }, - selectMode(newVal, oldVal) { + selectMode() { this.clearSelected() }, - hasSelectableRowClick(newVal, oldVal) { + hasSelectableRowClick(newVal) { this.clearSelected() this.setSelectionHandlers(!newVal) }, @@ -151,7 +151,7 @@ export default { const length = this.computedItems.length if (this.isSelectable && length > 0) { this.selectedLastClicked = -1 - this.selectedRows = this.selectableIsMultiSelect ? range(length).map(i => true) : [true] + this.selectedRows = this.selectableIsMultiSelect ? range(length).map(() => true) : [true] } }, isRowSelected(index) { diff --git a/src/components/table/helpers/mixin-sorting.js b/src/components/table/helpers/mixin-sorting.js index 36b0a98255d..385ec9b0f47 100644 --- a/src/components/table/helpers/mixin-sorting.js +++ b/src/components/table/helpers/mixin-sorting.js @@ -141,7 +141,7 @@ export default { } }, watch: { - isSortable(newVal, oldVal) /* istanbul ignore next: pain in the butt to test */ { + isSortable(newVal) /* istanbul ignore next: pain in the butt to test */ { if (newVal) { if (this.isSortable) { this.$on('head-clicked', this.handleSort) @@ -150,14 +150,14 @@ export default { this.$off('head-clicked', this.handleSort) } }, - sortDesc(newVal, oldVal) { + sortDesc(newVal) { if (newVal === this.localSortDesc) { /* istanbul ignore next */ return } this.localSortDesc = newVal || false }, - sortBy(newVal, oldVal) { + sortBy(newVal) { if (newVal === this.localSortBy) { /* istanbul ignore next */ return diff --git a/src/components/table/helpers/mixin-table-renderer.js b/src/components/table/helpers/mixin-table-renderer.js index 0f580a64ae7..451c7fae640 100644 --- a/src/components/table/helpers/mixin-table-renderer.js +++ b/src/components/table/helpers/mixin-table-renderer.js @@ -165,7 +165,7 @@ export default { const $content = [] if (this.isTableSimple) { - $content.push(this.normalizeSlot('default', {})) + $content.push(this.normalizeSlot('default')) } else { // Build the `
` (from caption mixin) $content.push(this.renderCaption ? this.renderCaption() : null) diff --git a/src/components/table/helpers/mixin-tbody-row.js b/src/components/table/helpers/mixin-tbody-row.js index f430ea68f7d..ac417e2b15c 100644 --- a/src/components/table/helpers/mixin-tbody-row.js +++ b/src/components/table/helpers/mixin-tbody-row.js @@ -171,7 +171,7 @@ export default { let $childNodes = slotName ? this.normalizeSlot(slotName, 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])] + $childNodes = [h('div', [$childNodes])] } // Render either a td or th cell return h(cellTag, data, [$childNodes]) diff --git a/src/components/table/table-caption.spec.js b/src/components/table/table-caption.spec.js index 0ca3f9784a0..6fc67573233 100644 --- a/src/components/table/table-caption.spec.js +++ b/src/components/table/table-caption.spec.js @@ -50,7 +50,7 @@ describe('table > caption', () => { scopedSlots: { 'table-caption': function(props) { scope = props - return this.$createElement('b', {}, 'foobar') + return this.$createElement('b', 'foobar') } } }) diff --git a/src/components/table/table-selectable.spec.js b/src/components/table/table-selectable.spec.js index 0f1ce80c44b..4f079244a7f 100644 --- a/src/components/table/table-selectable.spec.js +++ b/src/components/table/table-selectable.spec.js @@ -53,7 +53,7 @@ describe('table > row select', () => { items: testItems }, listeners: { - 'row-clicked': (item, index, evt) => {} + 'row-clicked': () => {} } }) expect(wrapper).toBeDefined() diff --git a/src/components/table/table-tbody-transition.spec.js b/src/components/table/table-tbody-transition.spec.js index 3714f7d363b..091629a0f11 100644 --- a/src/components/table/table-tbody-transition.spec.js +++ b/src/components/table/table-tbody-transition.spec.js @@ -55,10 +55,10 @@ describe('table > tbody transition', () => { fields: testFields, items: testItems, tbodyTransitionHandlers: { - onBeforeEnter: el => {}, - onAfterEnter: el => {}, - onBeforeLeave: el => {}, - onAfterLeave: el => {} + onBeforeEnter: () => {}, + onAfterEnter: () => {}, + onBeforeLeave: () => {}, + onAfterLeave: () => {} } }, stubs: { diff --git a/src/components/table/tbody.js b/src/components/table/tbody.js index 7b1033ead86..5f2374bf8fc 100644 --- a/src/components/table/tbody.js +++ b/src/components/table/tbody.js @@ -90,7 +90,7 @@ export const BTbody = /*#__PURE__*/ Vue.extend({ return h( this.isTransitionGroup ? 'transition-group' : 'tbody', data, - this.normalizeSlot('default', {}) + this.normalizeSlot('default') ) } }) diff --git a/src/components/table/tfoot.js b/src/components/table/tfoot.js index 75f8b936740..c5bcfc09fb8 100644 --- a/src/components/table/tfoot.js +++ b/src/components/table/tfoot.js @@ -75,7 +75,7 @@ export const BTfoot = /*#__PURE__*/ Vue.extend({ // Pass down any native listeners on: this.$listeners }, - this.normalizeSlot('default', {}) + this.normalizeSlot('default') ) } }) diff --git a/src/components/table/thead.js b/src/components/table/thead.js index cf393dd1362..4f89683a003 100644 --- a/src/components/table/thead.js +++ b/src/components/table/thead.js @@ -78,7 +78,7 @@ export const BThead = /*#__PURE__*/ Vue.extend({ // Pass down any native listeners on: this.$listeners }, - this.normalizeSlot('default', {}) + this.normalizeSlot('default') ) } }) diff --git a/src/components/table/tr.js b/src/components/table/tr.js index 2a973deb442..ab58d137bcb 100644 --- a/src/components/table/tr.js +++ b/src/components/table/tr.js @@ -23,7 +23,7 @@ export const BTr = /*#__PURE__*/ Vue.extend({ }, inject: { bvTableRowGroup: { - defaut() /* istanbul ignore next */ { + default() /* istanbul ignore next */ { return {} } } @@ -100,7 +100,7 @@ export const BTr = /*#__PURE__*/ Vue.extend({ // Pass native listeners to child on: this.$listeners }, - this.normalizeSlot('default', {}) + this.normalizeSlot('default') ) } }) diff --git a/src/components/tabs/tab.js b/src/components/tabs/tab.js index 4fd4e5f8010..a8015a97fb1 100644 --- a/src/components/tabs/tab.js +++ b/src/components/tabs/tab.js @@ -85,7 +85,7 @@ export const BTab = /*#__PURE__*/ Vue.extend({ } }, watch: { - localActive(newVal, oldVal) { + localActive(newVal) { // Make 'active' prop work with `.sync` modifier this.$emit('update:active', newVal) }, diff --git a/src/components/tabs/tabs.js b/src/components/tabs/tabs.js index 0e186256b75..7cc0ac960eb 100644 --- a/src/components/tabs/tabs.js +++ b/src/components/tabs/tabs.js @@ -245,11 +245,11 @@ export const BTabs = /*#__PURE__*/ Vue.extend({ } }, watch: { - currentTab(val, old) { + currentTab(newVal) { let index = -1 // Ensure only one tab is active at most this.tabs.forEach((tab, idx) => { - if (val === idx && !tab.disabled) { + if (newVal === idx && !tab.disabled) { tab.localActive = true index = idx } else { @@ -259,17 +259,17 @@ export const BTabs = /*#__PURE__*/ Vue.extend({ // Update the v-model this.$emit('input', index) }, - value(val, old) { - if (val !== old) { - val = parseInt(val, 10) - val = isNaN(val) ? -1 : val - old = parseInt(old, 10) || 0 + value(newVal, oldVal) { + if (newVal !== oldVal) { + newVal = parseInt(newVal, 10) + newVal = isNaN(newVal) ? -1 : newVal + oldVal = parseInt(oldVal, 10) || 0 const tabs = this.tabs - if (tabs[val] && !tabs[val].disabled) { - this.activateTab(tabs[val]) + if (tabs[newVal] && !tabs[newVal].disabled) { + this.activateTab(tabs[newVal]) } else { // Try next or prev tabs - if (val < old) { + if (newVal < oldVal) { this.previousTab() } else { this.nextTab() @@ -277,7 +277,7 @@ export const BTabs = /*#__PURE__*/ Vue.extend({ } } }, - registeredTabs(newVal, oldVal) { + registeredTabs() { // Each b-tab will register/unregister itself. // We use this to detect when tabs are added/removed // to trigger the update of the tabs. @@ -300,7 +300,7 @@ export const BTabs = /*#__PURE__*/ Vue.extend({ }) } }, - isMounted(newVal, oldVal) { + isMounted(newVal) { // Trigger an update after mounted. Needed for tabs inside lazy modals. if (newVal) { requestAF(() => { @@ -453,7 +453,7 @@ export const BTabs = /*#__PURE__*/ Vue.extend({ } // Set the current tab state to active - tabs.forEach((tab, idx) => { + tabs.forEach(tab => { // tab.localActive = idx === tabIndex && !tab.disabled tab.localActive = false }) diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js index dc001b9cd17..ca544ae3554 100644 --- a/src/components/tabs/tabs.spec.js +++ b/src/components/tabs/tabs.spec.js @@ -104,7 +104,7 @@ describe('tabs', () => { it('sets correct tab active when first tab is disabled', async () => { const App = Vue.extend({ render(h) { - return h(BTabs, {}, [ + return h(BTabs, [ h(BTab, { props: { disabled: true } }, 'tab 0'), h(BTab, { props: {} }, 'tab 1'), h(BTab, { props: {} }, 'tab 2') @@ -136,7 +136,7 @@ describe('tabs', () => { it('sets current index based on active prop of b-tab', async () => { const App = Vue.extend({ render(h) { - return h(BTabs, {}, [ + return h(BTabs, [ h(BTab, { props: { active: false } }, 'tab 0'), h(BTab, { props: { active: true } }, 'tab 1'), h(BTab, { props: { active: false } }, 'tab 2') @@ -168,7 +168,7 @@ describe('tabs', () => { it('selects first non-disabled tab when active tab disabled', async () => { const App = Vue.extend({ render(h) { - return h(BTabs, {}, [ + return h(BTabs, [ h(BTab, { props: { active: false, disabled: true } }, 'tab 0'), h(BTab, { props: { active: true } }, 'tab 1'), h(BTab, { props: { active: false } }, 'tab 2') @@ -694,7 +694,7 @@ describe('tabs', () => { expect(tabVm).toBeDefined() // Change title slot content - tabVm.$slots.title = [tabVm.$createElement('span', {}, 'foobar')] + tabVm.$slots.title = [tabVm.$createElement('span', 'foobar')] tabVm.$forceUpdate() await waitNT(wrapper.vm) await waitRAF() diff --git a/src/components/toast/README.md b/src/components/toast/README.md index f7f2b7347f0..1291b9fc5a0 100644 --- a/src/components/toast/README.md +++ b/src/components/toast/README.md @@ -452,7 +452,7 @@ for generating more complex toast content: [ h('b-spinner', { props: { type: 'grow', small: true } }), ' Flashy ', - h('strong', {}, 'toast'), + h('strong', 'toast'), ` message #${this.count} `, h('b-spinner', { props: { type: 'grow', small: true } }) ] diff --git a/src/components/toast/helpers/bv-toast.spec.js b/src/components/toast/helpers/bv-toast.spec.js index f1e721b51a1..f717feb7d53 100644 --- a/src/components/toast/helpers/bv-toast.spec.js +++ b/src/components/toast/helpers/bv-toast.spec.js @@ -74,7 +74,7 @@ describe('$bvToast', () => { it('$bvModal.toast() works', async () => { const App = localVue.extend({ render(h) { - return h('div', {}, 'app') + return h('div', 'app') } }) const wrapper = mount(App, { diff --git a/src/components/toast/package.json b/src/components/toast/package.json index e55f6bebba6..14e7d6c72d9 100644 --- a/src/components/toast/package.json +++ b/src/components/toast/package.json @@ -46,7 +46,7 @@ }, { "prop": "noHoverPause", - "description": "When set, disables the pausing of hte auto hide delay when the mouse hovers the toast" + "description": "When set, disables the pausing of the auto hide delay when the mouse hovers the toast" }, { "prop": "solid", diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js index 4c168e13339..7eb5d6c42d4 100644 --- a/src/components/toast/toast.js +++ b/src/components/toast/toast.js @@ -3,7 +3,8 @@ import { Portal, Wormhole } from 'portal-vue' import BVTransition from '../../utils/bv-transition' import { BvEvent } from '../../utils/bv-event.class' import { getComponentConfig } from '../../utils/config' -import { requestAF, eventOn, eventOff } from '../../utils/dom' +import { requestAF } from '../../utils/dom' +import { EVENT_OPTIONS_NO_CAPTURE, eventOnOff } from '../../utils/events' import { toInteger } from '../../utils/number' import idMixin from '../../mixins/id' import listenOnRootMixin from '../../mixins/listen-on-root' @@ -19,8 +20,6 @@ const NAME = 'BToast' const MIN_DURATION = 1000 -const EVENT_OPTIONS = { passive: true, capture: false } - // --- Props --- export const props = { @@ -168,7 +167,7 @@ export const BToast = /*#__PURE__*/ Vue.extend({ this.$emit('change', newVal) } }, - toaster(newVal) /* istanbul ignore next */ { + toaster() /* istanbul ignore next */ { // If toaster target changed, make sure toaster exists this.$nextTick(this.ensureToaster) }, @@ -246,12 +245,12 @@ export const BToast = /*#__PURE__*/ Vue.extend({ }) } }, - buildEvent(type, opts = {}) { + buildEvent(type, options = {}) { return new BvEvent(type, { cancelable: false, target: this.$el || null, relatedTarget: null, - ...opts, + ...options, vueTarget: this, componentId: this.safeId() }) @@ -290,12 +289,11 @@ export const BToast = /*#__PURE__*/ Vue.extend({ this.timer = null }, setHoverHandler(on) { - const method = on ? eventOn : eventOff const el = this.$refs['b-toast'] - method(el, 'mouseenter', this.onPause, EVENT_OPTIONS) - method(el, 'mouseleave', this.onUnPause, EVENT_OPTIONS) + eventOnOff(on, el, 'mouseenter', this.onPause, EVENT_OPTIONS_NO_CAPTURE) + eventOnOff(on, el, 'mouseleave', this.onUnPause, EVENT_OPTIONS_NO_CAPTURE) }, - onPause(evt) { + onPause() { // Determine time remaining, and then pause timer if (this.noAutoHide || this.noHoverPause || !this.timer || this.resumeDismiss) { return @@ -306,7 +304,7 @@ export const BToast = /*#__PURE__*/ Vue.extend({ this.resumeDismiss = Math.max(this.computedDuration - passed, MIN_DURATION) } }, - onUnPause(evt) { + onUnPause() { // Restart timer with max of time remaining or 1 second if (this.noAutoHide || this.noHoverPause || !this.resumeDismiss) { this.resumeDismiss = this.dismissStarted = 0 @@ -359,7 +357,7 @@ export const BToast = /*#__PURE__*/ Vue.extend({ h(BButtonClose, { staticClass: 'ml-auto mb-1', on: { - click: evt => { + click: () => { this.hide() } } diff --git a/src/components/tooltip/helpers/bv-tooltip.js b/src/components/tooltip/helpers/bv-tooltip.js index 4c422ee00ad..e35f657a8f0 100644 --- a/src/components/tooltip/helpers/bv-tooltip.js +++ b/src/components/tooltip/helpers/bv-tooltip.js @@ -9,21 +9,20 @@ import looseEqual from '../../../utils/loose-equal' import noop from '../../../utils/noop' import { arrayIncludes, concat, from as arrayFrom } from '../../../utils/array' import { - isElement, - isDisabled, - isVisible, closest, contains, - select, - getById, - hasClass, getAttr, + getById, hasAttr, - setAttr, + hasClass, + isDisabled, + isElement, + isVisible, removeAttr, - eventOn, - eventOff + select, + setAttr } from '../../../utils/dom' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff, eventOnOff } from '../../../utils/events' import { isFunction, isNumber, @@ -48,9 +47,6 @@ const MODAL_CLOSE_EVENT = 'bv::modal::hidden' const DROPDOWN_CLASS = 'dropdown' const DROPDOWN_OPEN_SELECTOR = '.dropdown-menu.show' -// Options for Native Event Listeners (since we never call preventDefault) -const EvtOpts = { passive: true, capture: false } - // Data specific to popper and template // We don't use props, as we need reactivity (we can't pass reactive props) const templateData = { @@ -482,7 +478,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ // Destroy the template this.destroyTemplate() // Emit a non-cancelable BvEvent 'shown' - this.emitEvent(this.buildEvent('hidden', {})) + this.emitEvent(this.buildEvent('hidden')) }, // --- Utility methods --- getTarget() { @@ -610,7 +606,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ } }, // --- BvEvent helpers --- - buildEvent(type, opts = {}) { + buildEvent(type, options = {}) { // Defaults to a non-cancellable event return new BvEvent(type, { cancelable: false, @@ -619,7 +615,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ componentId: this.computedId, vueTarget: this, // Add in option overrides - ...opts + ...options }) }, emitEvent(bvEvt) { @@ -645,17 +641,17 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ // Set up our listeners on the target trigger element this.computedTriggers.forEach(trigger => { if (trigger === 'click') { - eventOn(el, 'click', this.handleEvent, EvtOpts) + eventOn(el, 'click', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) } else if (trigger === 'focus') { - eventOn(el, 'focusin', this.handleEvent, EvtOpts) - eventOn(el, 'focusout', this.handleEvent, EvtOpts) + eventOn(el, 'focusin', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) + eventOn(el, 'focusout', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) } else if (trigger === 'blur') { // Used to close $tip when element looses focus /* istanbul ignore next */ - eventOn(el, 'focusout', this.handleEvent, EvtOpts) + eventOn(el, 'focusout', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) } else if (trigger === 'hover') { - eventOn(el, 'mouseenter', this.handleEvent, EvtOpts) - eventOn(el, 'mouseleave', this.handleEvent, EvtOpts) + eventOn(el, 'mouseenter', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) + eventOn(el, 'mouseleave', this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) } }, this) }, @@ -669,7 +665,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ // Clear out any active target listeners events.forEach(evt => { - target && eventOff(target, evt, this.handleEvent, EvtOpts) + target && eventOff(target, evt, this.handleEvent, EVENT_OPTIONS_NO_CAPTURE) }, this) }, setRootListener(on) { @@ -723,9 +719,8 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ // Only needed because of broken event delegation on iOS // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement) { - const method = on ? eventOn : eventOff arrayFrom(document.body.children).forEach(el => { - method(el, 'mouseover', this.$_noop) + eventOnOff(on, el, 'mouseover', this.$_noop) }) } }, @@ -827,7 +822,7 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({ this.enable() } }, - click(evt) { + click() { if (!this.$_enabled || this.dropdownOpen()) { /* istanbul ignore next */ return diff --git a/src/components/tooltip/tooltip.js b/src/components/tooltip/tooltip.js index e17b1229919..1ef3f1895c0 100644 --- a/src/components/tooltip/tooltip.js +++ b/src/components/tooltip/tooltip.js @@ -154,18 +154,18 @@ export const BTooltip = /*#__PURE__*/ Vue.extend({ } } }, - disabled(newVal, oldVal) { + disabled(newVal) { if (newVal) { this.doDisable() } else { this.doEnable() } }, - localShow(show, oldVal) { + localShow(newVal) { // TODO: May need to be done in a `$nextTick()` - this.$emit('update:show', show) + this.$emit('update:show', newVal) }, - templateData(newVal, oldVal) { + templateData() { this.$nextTick(() => { if (this.$_bv_toolpop) { this.$_bv_toolpop.updateData(this.templateData) @@ -173,7 +173,7 @@ export const BTooltip = /*#__PURE__*/ Vue.extend({ }) }, // Watchers for title/content props (prop changes do not trigger the `updated()` hook) - templateTitleContent(newVal, oldVal) { + templateTitleContent() { this.$nextTick(this.updateContent) } }, @@ -313,7 +313,7 @@ export const BTooltip = /*#__PURE__*/ Vue.extend({ doClose() { this.localShow && this.$_bv_toolpop && this.$_bv_toolpop.hide() }, - doDisable(evt) { + doDisable() { this.$_bv_toolpop && this.$_bv_toolpop.disable() }, doEnable() { diff --git a/src/directives/modal/modal.js b/src/directives/modal/modal.js index d112b8fd5c3..21ee73e1c39 100644 --- a/src/directives/modal/modal.js +++ b/src/directives/modal/modal.js @@ -1,14 +1,6 @@ import KeyCodes from '../../utils/key-codes' -import { - eventOn, - eventOff, - getAttr, - hasAttr, - isDisabled, - matches, - select, - setAttr -} from '../../utils/dom' +import { getAttr, hasAttr, isDisabled, matches, select, setAttr } from '../../utils/dom' +import { EVENT_OPTIONS_PASSIVE, eventOn, eventOff } from '../../utils/events' import { isString } from '../../utils/inspect' import { keys } from '../../utils/object' @@ -18,8 +10,6 @@ const EVENT_SHOW = 'bv::show::modal' // Prop name we use to store info on root element const PROPERTY = '__bv_modal_directive__' -const EVENT_OPTS = { passive: true } - const getTarget = ({ modifiers = {}, arg, value }) => { // Try value, then arg, otherwise pick last modifier return isString(value) ? value : isString(arg) ? arg : keys(modifiers).reverse()[0] @@ -68,11 +58,11 @@ const bind = (el, binding, vnode) => { // If element is not a button, we add `role="button"` for accessibility setRole(trigger) // Listen for click events - eventOn(trigger, 'click', handler, EVENT_OPTS) + eventOn(trigger, 'click', handler, EVENT_OPTIONS_PASSIVE) if (trigger.tagName !== 'BUTTON' && getAttr(trigger, 'role') === 'button') { // If trigger isn't a button but has role button, // we also listen for `keydown.space` && `keydown.enter` - eventOn(trigger, 'keydown', handler, EVENT_OPTS) + eventOn(trigger, 'keydown', handler, EVENT_OPTIONS_PASSIVE) } } } @@ -82,10 +72,10 @@ const unbind = el => { const trigger = oldProp.trigger const handler = oldProp.handler if (trigger && handler) { - eventOff(trigger, 'click', handler, EVENT_OPTS) - eventOff(trigger, 'keydown', handler, EVENT_OPTS) - eventOff(el, 'click', handler, EVENT_OPTS) - eventOff(el, 'keydown', handler, EVENT_OPTS) + eventOff(trigger, 'click', handler, EVENT_OPTIONS_PASSIVE) + eventOff(trigger, 'keydown', handler, EVENT_OPTIONS_PASSIVE) + eventOff(el, 'click', handler, EVENT_OPTIONS_PASSIVE) + eventOff(el, 'keydown', handler, EVENT_OPTIONS_PASSIVE) } delete el[PROPERTY] } diff --git a/src/directives/scrollspy/scrollspy.class.js b/src/directives/scrollspy/scrollspy.class.js index dbcd2f31307..de692c0ac8c 100644 --- a/src/directives/scrollspy/scrollspy.class.js +++ b/src/directives/scrollspy/scrollspy.class.js @@ -4,22 +4,21 @@ import observeDom from '../../utils/observe-dom' import { + addClass, + closest, + getAttr, + getBCR, + hasClass, isElement, isVisible, - closest, matches, - getBCR, offset, position, - selectAll, - select, - hasClass, - addClass, removeClass, - getAttr, - eventOn, - eventOff + select, + selectAll } from '../../utils/dom' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../../utils/events' import { isString, isUndefined } from '../../utils/inspect' import { toString as objectToString } from '../../utils/object' import { warn } from '../../utils/warn' @@ -79,9 +78,6 @@ const TransitionEndEvents = [ 'oTransitionEnd' ] -// Options for events -const EventOptions = { passive: true, capture: false } - /* * Utility Methods */ @@ -195,13 +191,13 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ { listen() { const scroller = this.getScroller() if (scroller && scroller.tagName !== 'BODY') { - eventOn(scroller, 'scroll', this, EventOptions) + eventOn(scroller, 'scroll', this, EVENT_OPTIONS_NO_CAPTURE) } - eventOn(window, 'scroll', this, EventOptions) - eventOn(window, 'resize', this, EventOptions) - eventOn(window, 'orientationchange', this, EventOptions) + eventOn(window, 'scroll', this, EVENT_OPTIONS_NO_CAPTURE) + eventOn(window, 'resize', this, EVENT_OPTIONS_NO_CAPTURE) + eventOn(window, 'orientationchange', this, EVENT_OPTIONS_NO_CAPTURE) TransitionEndEvents.forEach(evtName => { - eventOn(window, evtName, this, EventOptions) + eventOn(window, evtName, this, EVENT_OPTIONS_NO_CAPTURE) }) this.setObservers(true) // Schedule a refresh @@ -212,13 +208,13 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ { const scroller = this.getScroller() this.setObservers(false) if (scroller && scroller.tagName !== 'BODY') { - eventOff(scroller, 'scroll', this, EventOptions) + eventOff(scroller, 'scroll', this, EVENT_OPTIONS_NO_CAPTURE) } - eventOff(window, 'scroll', this, EventOptions) - eventOff(window, 'resize', this, EventOptions) - eventOff(window, 'orientationchange', this, EventOptions) + eventOff(window, 'scroll', this, EVENT_OPTIONS_NO_CAPTURE) + eventOff(window, 'resize', this, EVENT_OPTIONS_NO_CAPTURE) + eventOff(window, 'orientationchange', this, EVENT_OPTIONS_NO_CAPTURE) TransitionEndEvents.forEach(evtName => { - eventOff(window, evtName, this, EventOptions) + eventOff(window, evtName, this, EVENT_OPTIONS_NO_CAPTURE) }) } diff --git a/src/icons/iconstack.spec.js b/src/icons/iconstack.spec.js index b3c11f854c9..5e761d29375 100644 --- a/src/icons/iconstack.spec.js +++ b/src/icons/iconstack.spec.js @@ -3,7 +3,7 @@ import { BIconstack } from './iconstack' describe('icons > b-iconstack', () => { it('has expected default structure', async () => { - const wrapper = mount(BIconstack, {}) + const wrapper = mount(BIconstack) expect(wrapper.exists()).toBe(true) expect(wrapper.is('svg')).toBe(true) diff --git a/src/mixins/click-out.js b/src/mixins/click-out.js index 22fe08a559b..e9782ac2c1a 100644 --- a/src/mixins/click-out.js +++ b/src/mixins/click-out.js @@ -1,6 +1,5 @@ -import { contains, eventOff, eventOn } from '../utils/dom' - -const eventOptions = { passive: true, capture: false } +import { contains } from '../utils/dom' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../utils/events' // @vue/component export default { @@ -12,9 +11,19 @@ export default { watch: { listenForClickOut(newValue, oldValue) { if (newValue !== oldValue) { - eventOff(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, eventOptions) + eventOff( + this.clickOutElement, + this.clickOutEventName, + this._clickOutHandler, + EVENT_OPTIONS_NO_CAPTURE + ) if (newValue) { - eventOn(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, eventOptions) + eventOn( + this.clickOutElement, + this.clickOutEventName, + this._clickOutHandler, + EVENT_OPTIONS_NO_CAPTURE + ) } } } @@ -32,11 +41,21 @@ export default { this.clickOutEventName = 'click' } if (this.listenForClickOut) { - eventOn(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, eventOptions) + eventOn( + this.clickOutElement, + this.clickOutEventName, + this._clickOutHandler, + EVENT_OPTIONS_NO_CAPTURE + ) } }, beforeDestroy() /* istanbul ignore next */ { - eventOff(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, eventOptions) + eventOff( + this.clickOutElement, + this.clickOutEventName, + this._clickOutHandler, + EVENT_OPTIONS_NO_CAPTURE + ) }, methods: { isClickOut(evt) { diff --git a/src/mixins/click-out.spec.js b/src/mixins/click-out.spec.js index 2600db16550..2945cf5b983 100644 --- a/src/mixins/click-out.spec.js +++ b/src/mixins/click-out.spec.js @@ -13,12 +13,12 @@ describe('utils/click-out', () => { this.listenForClickOut = true }, methods: { - clickOutHandler(evt) { + clickOutHandler() { count++ } }, render(h) { - return h('div', {}, [h('button', {}, 'button')]) + return h('div', [h('button', 'button')]) } }) diff --git a/src/mixins/focus-in.js b/src/mixins/focus-in.js index 94b84b75c31..f8b9a41d6c0 100644 --- a/src/mixins/focus-in.js +++ b/src/mixins/focus-in.js @@ -1,6 +1,4 @@ -import { eventOff, eventOn } from '../utils/dom' - -const eventOptions = { passive: true, capture: false } +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../utils/events' // @vue/component export default { @@ -12,9 +10,9 @@ export default { watch: { listenForFocusIn(newValue, oldValue) { if (newValue !== oldValue) { - eventOff(this.focusInElement, 'focusin', this._focusInHandler, eventOptions) + eventOff(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) if (newValue) { - eventOn(this.focusInElement, 'focusin', this._focusInHandler, eventOptions) + eventOn(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) } } } @@ -28,11 +26,11 @@ export default { this.focusInElement = document } if (this.listenForFocusIn) { - eventOn(this.focusInElement, 'focusin', this._focusInHandler, eventOptions) + eventOn(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) } }, beforeDestroy() /* istanbul ignore next */ { - eventOff(this.focusInElement, 'focusin', this._focusInHandler, eventOptions) + eventOff(this.focusInElement, 'focusin', this._focusInHandler, EVENT_OPTIONS_NO_CAPTURE) }, methods: { _focusInHandler(evt) { diff --git a/src/mixins/focus-in.spec.js b/src/mixins/focus-in.spec.js index 63975c08193..d2bf5bcf7cf 100644 --- a/src/mixins/focus-in.spec.js +++ b/src/mixins/focus-in.spec.js @@ -13,12 +13,12 @@ describe('utils/focus-in', () => { this.listenForFocusIn = true }, methods: { - focusInHandler(evt) { + focusInHandler() { count++ } }, render(h) { - return h('div', {}, [h('button', {}, 'button')]) + return h('div', [h('button', 'button')]) } }) diff --git a/src/mixins/form-radio-check-group.js b/src/mixins/form-radio-check-group.js index c1e41ee460e..4d79d68e171 100644 --- a/src/mixins/form-radio-check-group.js +++ b/src/mixins/form-radio-check-group.js @@ -67,10 +67,10 @@ export default { } }, watch: { - checked(newVal, oldVal) { + checked(newVal) { this.localChecked = newVal }, - localChecked(newVal, oldVal) { + localChecked(newVal) { this.$emit('input', newVal) } }, diff --git a/src/mixins/form-radio-check.js b/src/mixins/form-radio-check.js index 914a83d6ceb..1704f00f332 100644 --- a/src/mixins/form-radio-check.js +++ b/src/mixins/form-radio-check.js @@ -143,7 +143,7 @@ export default { } }, watch: { - checked(newVal, oldVal) { + checked(newVal) { this.computedLocalChecked = newVal } }, diff --git a/src/mixins/listen-on-document.js b/src/mixins/listen-on-document.js index b3d930e0b24..264ea300d79 100644 --- a/src/mixins/listen-on-document.js +++ b/src/mixins/listen-on-document.js @@ -1,11 +1,9 @@ import { arrayIncludes } from '../utils/array' -import { eventOff, eventOn } from '../utils/dom' import { isBrowser } from '../utils/env' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../utils/events' import { isString, isFunction } from '../utils/inspect' import { keys } from '../utils/object' -const eventOptions = { passive: true, capture: false } - const PROP = '$_bv_documentHandlers_' // @vue/component @@ -30,7 +28,7 @@ export default { // Remove all registered event handlers keys(items).forEach(evtName => { const handlers = items[evtName] || [] - handlers.forEach(handler => eventOff(document, evtName, handler, eventOptions)) + handlers.forEach(handler => eventOff(document, evtName, handler, EVENT_OPTIONS_NO_CAPTURE)) }) }) }, @@ -43,13 +41,13 @@ export default { this[PROP][evtName] = this[PROP][evtName] || [] if (!arrayIncludes(this[PROP][evtName], handler)) { this[PROP][evtName].push(handler) - eventOn(document, evtName, handler, eventOptions) + eventOn(document, evtName, handler, EVENT_OPTIONS_NO_CAPTURE) } } }, listenOffDocument(evtName, handler) { if (this[PROP] && isString(evtName) && isFunction(handler)) { - eventOff(document, evtName, handler, eventOptions) + eventOff(document, evtName, handler, EVENT_OPTIONS_NO_CAPTURE) this[PROP][evtName] = (this[PROP][evtName] || []).filter(h => h !== handler) } } diff --git a/src/mixins/listen-on-document.spec.js b/src/mixins/listen-on-document.spec.js index c5400f7dd14..8a9be83b9c8 100644 --- a/src/mixins/listen-on-document.spec.js +++ b/src/mixins/listen-on-document.spec.js @@ -23,14 +23,14 @@ describe('mixins/listen-on-document', () => { this.listenOnDocument('click', spyClick2) }, watch: { - offClickOne(newVal, oldVal) { + offClickOne(newVal) { if (newVal) { this.listenOffDocument('click', spyClick1) } } }, render(h) { - return h('div', {}, this.$slots.default) + return h('div', this.$slots.default) } }) @@ -50,8 +50,8 @@ describe('mixins/listen-on-document', () => { const props = { offClickOne: this.offClickOne } - return h('div', {}, [ - h('span', {}, ''), + return h('div', [ + h('span', ''), h('input', { type: 'text' }), this.destroy ? h() : h(TestComponent, { props }, 'test-component') ]) diff --git a/src/mixins/listen-on-root.spec.js b/src/mixins/listen-on-root.spec.js index acd4ce60197..f21236d5f08 100644 --- a/src/mixins/listen-on-root.spec.js +++ b/src/mixins/listen-on-root.spec.js @@ -14,7 +14,7 @@ describe('mixins/listen-on-root', () => { this.listenOnRootOnce('root-once', spyOnce) }, render(h) { - return h('div', {}, this.$slots.default) + return h('div', this.$slots.default) } }) @@ -27,7 +27,7 @@ describe('mixins/listen-on-root', () => { } }, render(h) { - return h('div', {}, [this.destroy ? h() : h(TestComponent, {}, 'test-component')]) + return h('div', [this.destroy ? h() : h(TestComponent, 'test-component')]) } }) diff --git a/src/mixins/listen-on-window.js b/src/mixins/listen-on-window.js index 10ea6876b30..030ea58ca3e 100644 --- a/src/mixins/listen-on-window.js +++ b/src/mixins/listen-on-window.js @@ -1,11 +1,9 @@ import { arrayIncludes } from '../utils/array' -import { eventOff, eventOn } from '../utils/dom' import { isBrowser } from '../utils/env' +import { EVENT_OPTIONS_NO_CAPTURE, eventOn, eventOff } from '../utils/events' import { isString, isFunction } from '../utils/inspect' import { keys } from '../utils/object' -const eventOptions = { passive: true, capture: false } - const PROP = '$_bv_windowHandlers_' // @vue/component @@ -26,7 +24,7 @@ export default { // Remove all registered event handlers keys(items).forEach(evtName => { const handlers = items[evtName] || [] - handlers.forEach(handler => eventOff(window, evtName, handler, eventOptions)) + handlers.forEach(handler => eventOff(window, evtName, handler, EVENT_OPTIONS_NO_CAPTURE)) }) } }, @@ -39,13 +37,13 @@ export default { this[PROP][evtName] = this[PROP][evtName] || [] if (!arrayIncludes(this[PROP][evtName], handler)) { this[PROP][evtName].push(handler) - eventOn(window, evtName, handler, eventOptions) + eventOn(window, evtName, handler, EVENT_OPTIONS_NO_CAPTURE) } } }, listenOffWindow(evtName, handler) { if (isBrowser && this[PROP] && isString(evtName) && isFunction(handler)) { - eventOff(window, evtName, handler, eventOptions) + eventOff(window, evtName, handler, EVENT_OPTIONS_NO_CAPTURE) this[PROP][evtName] = (this[PROP][evtName] || []).filter(h => h !== handler) } } diff --git a/src/mixins/listen-on-window.spec.js b/src/mixins/listen-on-window.spec.js index d4f10b36dfc..13656e6fcd8 100644 --- a/src/mixins/listen-on-window.spec.js +++ b/src/mixins/listen-on-window.spec.js @@ -23,14 +23,14 @@ describe('mixins/listen-on-window', () => { this.listenOnWindow('resize', spyResize2) }, watch: { - offResizeOne(newVal, oldVal) { + offResizeOne(newVal) { if (newVal) { this.listenOffWindow('resize', spyResize1) } } }, render(h) { - return h('div', {}, this.$slots.default) + return h('div', this.$slots.default) } }) @@ -50,7 +50,7 @@ describe('mixins/listen-on-window', () => { const props = { offResizeOne: this.offResizeOne } - return h('div', {}, [this.destroy ? h() : h(TestComponent, { props }, 'test-component')]) + return h('div', [this.destroy ? h() : h(TestComponent, { props }, 'test-component')]) } }) diff --git a/src/utils/bv-hover-swap.js b/src/utils/bv-hover-swap.js index b605f17d541..27308f99f69 100644 --- a/src/utils/bv-hover-swap.js +++ b/src/utils/bv-hover-swap.js @@ -1,9 +1,5 @@ import Vue from './vue' -import { eventOn, eventOff } from './dom' - -// --- Constants --- - -const EVENT_OPTIONS = { passive: true } +import { EVENT_OPTIONS_PASSIVE, eventOnOff } from './events' // @vue/component export const BVHoverSwap = /*#__PURE__*/ Vue.extend({ @@ -49,9 +45,8 @@ export const BVHoverSwap = /*#__PURE__*/ Vue.extend({ this.listen(false) this.$_hoverEl = el } - const method = on ? eventOn : eventOff - method(this.$_hoverEl, 'mouseenter', this.handleHover, EVENT_OPTIONS) - method(this.$_hoverEl, 'mouseleave', this.handleHover, EVENT_OPTIONS) + eventOnOff(on, this.$_hoverEl, 'mouseenter', this.handleHover, EVENT_OPTIONS_PASSIVE) + eventOnOff(on, this.$_hoverEl, 'mouseleave', this.handleHover, EVENT_OPTIONS_PASSIVE) }, handleHover(evt) { this.isHovered = evt.type === 'mouseenter' diff --git a/src/utils/bv-hover-swap.spec.js b/src/utils/bv-hover-swap.spec.js index 484c2554637..253821460b5 100644 --- a/src/utils/bv-hover-swap.spec.js +++ b/src/utils/bv-hover-swap.spec.js @@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils' import { waitNT } from '../../tests/utils' import { BVHoverSwap } from './bv-hover-swap' -describe('utils/bv-hoverswap', () => { +describe('utils/bv-hover-swap', () => { it('works', async () => { const wrapper = mount(BVHoverSwap, { slots: { @@ -36,7 +36,7 @@ describe('utils/bv-hoverswap', () => { props: { parent: { type: Boolean, - defaut: false + default: false } }, methods: { diff --git a/src/utils/bv-transition.js b/src/utils/bv-transition.js index b74e2d74f2a..e748e0c9ab9 100644 --- a/src/utils/bv-transition.js +++ b/src/utils/bv-transition.js @@ -51,7 +51,7 @@ export const BVTransition = /*#__PURE__*/ Vue.extend({ default: null } }, - render(h, { children, data, listeners, props }) { + render(h, { children, data, props }) { let transProps = props.transProps if (!isPlainObject(transProps)) { transProps = props.noFade ? NO_FADE_PROPS : FADE_PROPS diff --git a/src/utils/dom.js b/src/utils/dom.js index 3145ec61503..4e7f72362f0 100644 --- a/src/utils/dom.js +++ b/src/utils/dom.js @@ -1,6 +1,6 @@ import { from as arrayFrom } from './array' -import { hasWindowSupport, hasDocumentSupport, hasPassiveEventSupport } from './env' -import { isFunction, isNull, isObject } from '../utils/inspect' +import { hasWindowSupport, hasDocumentSupport } from './env' +import { isFunction, isNull } from '../utils/inspect' // --- Constants --- @@ -48,32 +48,6 @@ export const MutationObs = // --- Utils --- -// Normalize event options based on support of passive option -// Exported only for testing purposes -export const parseEventOptions = options => { - /* istanbul ignore else: can't test in JSDOM, as it supports passive */ - if (hasPassiveEventSupport) { - return isObject(options) ? options : { useCapture: !!options || false } - } else { - // Need to translate to actual Boolean value - return !!(isObject(options) ? options.useCapture : options) - } -} - -// Attach an event listener to an element -export const eventOn = (el, evtName, handler, options) => { - if (el && el.addEventListener) { - el.addEventListener(evtName, handler, parseEventOptions(options)) - } -} - -// Remove an event listener from an element -export const eventOff = (el, evtName, handler, options) => { - if (el && el.removeEventListener) { - el.removeEventListener(evtName, handler, parseEventOptions(options)) - } -} - // Remove a node from DOM export const removeNode = el => el && el.parentNode && el.parentNode.removeChild(el) diff --git a/src/utils/dom.spec.js b/src/utils/dom.spec.js index c697a527215..1dfdda22fc6 100644 --- a/src/utils/dom.spec.js +++ b/src/utils/dom.spec.js @@ -10,12 +10,10 @@ import { selectAll, hasAttr, getAttr, - hasClass, - parseEventOptions + hasClass } from './dom' -import { hasPassiveEventSupport } from './env' -const template1 = ` +const template = `
@@ -28,9 +26,7 @@ const template1 = `
` -const App = Vue.extend({ - template: template1 -}) +const App = Vue.extend({ template }) describe('utils/dom', () => { it('isElement works', async () => { @@ -73,7 +69,7 @@ describe('utils/dom', () => { expect($span.exists()).toBe(true) expect(hasClass($span.element, 'barspan')).toBe(true) expect(hasClass($span.element, 'foobar')).toBe(true) - expect(hasClass($span.element, 'fizzlerocks')).toBe(false) + expect(hasClass($span.element, 'fizzle-rocks')).toBe(false) expect(hasClass(null, 'foobar')).toBe(false) wrapper.destroy() @@ -119,7 +115,7 @@ describe('utils/dom', () => { expect($baz.exists()).toBe(true) expect(closest('div.baz', $btns.at(0).element)).toBeDefined() expect(closest('div.baz', $btns.at(0).element)).toBe($baz.element) - expect(closest('div.nothere', $btns.at(0).element)).toBe(null) + expect(closest('div.not-here', $btns.at(0).element)).toBe(null) expect(closest('div.baz', $baz.element)).toBe(null) expect(closest('div.baz', $baz.element, true)).toBe($baz.element) @@ -207,15 +203,15 @@ describe('utils/dom', () => { // With root element specified expect(select('button', wrapper.element)).toBe($btns.at(0).element) expect(select('button#button3', wrapper.element)).toBe($btns.at(2).element) - expect(select('span.nothere', wrapper.element)).toBe(null) + expect(select('span.not-here', wrapper.element)).toBe(null) - // Note: It appears that vue-test-utils is not detaching previous app instances - // and elements once the test is complete! + // Note: It appears that `vue-test-utils` is not detaching previous + // app instances and elements once the test is complete! expect(select('button')).not.toBe(null) expect(select('button')).toBe($btns.at(0).element) expect(select('button#button3')).not.toBe(null) expect(select('button#button3')).toBe($btns.at(2).element) - expect(select('span.nothere')).toBe(null) + expect(select('span.not-here')).toBe(null) wrapper.destroy() }) @@ -246,8 +242,8 @@ describe('utils/dom', () => { expect(selectAll('div.baz button', wrapper.element)[2]).toBe($btns.at(2).element) // Without root element specified (assumes document as root) - // Note: It appears that vue-test-utils is not detaching previous app instances - // and elements once the test is complete! + // Note: It appears that `vue-test-utils` is not detaching previous + // app instances and elements once the test is complete! expect(Array.isArray(selectAll('button'))).toBe(true) expect(selectAll('button')).not.toEqual([]) expect(selectAll('button').length).toBe(3) @@ -266,55 +262,4 @@ describe('utils/dom', () => { wrapper.destroy() }) - - it('event options parsing works', async () => { - // JSDOM probably does not support passive mode - if (hasPassiveEventSupport) { - // Converts boolean to object - expect(parseEventOptions(true)).toEqual({ useCapture: true }) - expect(parseEventOptions(false)).toEqual({ useCapture: false }) - expect(parseEventOptions()).toEqual({ useCapture: false }) - - // Parses object correctly (returns as-is) - expect(parseEventOptions({ useCapture: false })).toEqual({ useCapture: false }) - expect(parseEventOptions({ useCapture: true })).toEqual({ useCapture: true }) - expect(parseEventOptions({})).toEqual({}) - expect( - parseEventOptions({ - useCapture: false, - foobar: true - }) - ).toEqual({ useCapture: false, foobar: true }) - expect( - parseEventOptions({ - useCapture: true, - foobar: false - }) - ).toEqual({ useCapture: true, foobar: false }) - } else { - // Converts non object to boolean - expect(parseEventOptions(true)).toEqual(true) - expect(parseEventOptions(false)).toEqual(false) - expect(parseEventOptions()).toEqual(false) - expect(parseEventOptions(null)).toEqual(false) - // Converts object to boolean - expect(parseEventOptions({ useCapture: false })).toEqual(false) - expect(parseEventOptions({ useCapture: true })).toEqual(true) - expect(parseEventOptions({})).toEqual(false) - expect( - parseEventOptions({ - useCapture: false, - foobar: true - }) - ).toEqual(false) - expect( - parseEventOptions({ - useCapture: true, - foobar: true - }) - ).toEqual(true) - expect(parseEventOptions({ foobar: true })).toEqual(false) - expect(parseEventOptions({ foobar: false })).toEqual(false) - } - }) }) diff --git a/src/utils/events.js b/src/utils/events.js new file mode 100644 index 00000000000..2b489cc10f1 --- /dev/null +++ b/src/utils/events.js @@ -0,0 +1,42 @@ +import { hasPassiveEventSupport } from './env' +import { isObject } from './inspect' + +// --- Constants --- + +export const EVENT_OPTIONS_PASSIVE = { passive: true } +export const EVENT_OPTIONS_NO_CAPTURE = { passive: true, capture: true } + +// --- Utils --- + +// Normalize event options based on support of passive option +// Exported only for testing purposes +export const parseEventOptions = options => { + /* istanbul ignore else: can't test in JSDOM, as it supports passive */ + if (hasPassiveEventSupport) { + return isObject(options) ? options : { capture: !!options || false } + } else { + // Need to translate to actual Boolean value + return !!(isObject(options) ? options.capture : options) + } +} + +// Attach an event listener to an element +export const eventOn = (el, evtName, handler, options) => { + if (el && el.addEventListener) { + el.addEventListener(evtName, handler, parseEventOptions(options)) + } +} + +// Remove an event listener from an element +export const eventOff = (el, evtName, handler, options) => { + if (el && el.removeEventListener) { + el.removeEventListener(evtName, handler, parseEventOptions(options)) + } +} + +// Utility method to add/remove a event listener based on first argument (boolean) +// It passes all other arguments to the `eventOn()` or `eventOff` method +export const eventOnOff = (on, ...args) => { + const method = on ? eventOn : eventOff + method(...args) +} diff --git a/src/utils/events.spec.js b/src/utils/events.spec.js new file mode 100644 index 00000000000..2c94b4c7c85 --- /dev/null +++ b/src/utils/events.spec.js @@ -0,0 +1,41 @@ +import { parseEventOptions } from './events' +import { hasPassiveEventSupport } from './env' + +describe('utils/events', () => { + it('event options parsing works', async () => { + // JSDOM probably does not support passive mode + if (hasPassiveEventSupport) { + // Converts boolean to object + expect(parseEventOptions(true)).toEqual({ capture: true }) + expect(parseEventOptions(false)).toEqual({ capture: false }) + expect(parseEventOptions()).toEqual({ capture: false }) + + // Parses object correctly (returns as-is) + expect(parseEventOptions({ capture: false })).toEqual({ capture: false }) + expect(parseEventOptions({ capture: true })).toEqual({ capture: true }) + expect(parseEventOptions({})).toEqual({}) + expect(parseEventOptions({ capture: false, foobar: true })).toEqual({ + capture: false, + foobar: true + }) + expect(parseEventOptions({ capture: true, foobar: false })).toEqual({ + capture: true, + foobar: false + }) + } else { + // Converts non object to boolean + expect(parseEventOptions(true)).toEqual(true) + expect(parseEventOptions(false)).toEqual(false) + expect(parseEventOptions()).toEqual(false) + expect(parseEventOptions(null)).toEqual(false) + // Converts object to boolean + expect(parseEventOptions({ capture: false })).toEqual(false) + expect(parseEventOptions({ capture: true })).toEqual(true) + expect(parseEventOptions({})).toEqual(false) + expect(parseEventOptions({ capture: false, foobar: true })).toEqual(false) + expect(parseEventOptions({ capture: true, foobar: true })).toEqual(true) + expect(parseEventOptions({ foobar: true })).toEqual(false) + expect(parseEventOptions({ foobar: false })).toEqual(false) + } + }) +}) diff --git a/src/utils/observe-dom.js b/src/utils/observe-dom.js index 57a7b6ca1eb..9824b274362 100644 --- a/src/utils/observe-dom.js +++ b/src/utils/observe-dom.js @@ -5,10 +5,14 @@ import { warnNoMutationObserverSupport } from './warn' * Observe a DOM element changes, falls back to eventListener mode * @param {Element} el The DOM element to observe * @param {Function} callback callback to be called on change - * @param {object} [opts={childList: true, subtree: true}] observe options + * @param {object} [options={childList: true, subtree: true}] observe options * @see http://stackoverflow.com/questions/3219758 */ -const observeDom = (el, callback, opts) => /* istanbul ignore next: difficult to test in JSDOM */ { +const observeDom = ( + el, + callback, + options +) => /* istanbul ignore next: difficult to test in JSDOM */ { // Handle cases where we might be passed a Vue instance el = el ? el.$el || el : null @@ -64,7 +68,7 @@ const observeDom = (el, callback, opts) => /* istanbul ignore next: difficult to }) // Have the observer observe foo for changes in children, etc - obs.observe(el, { childList: true, subtree: true, ...opts }) + obs.observe(el, { childList: true, subtree: true, ...options }) // We return a reference to the observer so that `obs.disconnect()` // can be called if necessary diff --git a/src/utils/plugins.js b/src/utils/plugins.js index 71678a4867e..cabb101a504 100644 --- a/src/utils/plugins.js +++ b/src/utils/plugins.js @@ -55,7 +55,7 @@ export const installFactory = ({ components, directives, plugins } = {}) => { * @returns {function} plugin install function */ export const installFactoryNoConfig = ({ components, directives, plugins } = {}) => { - const install = (Vue, config = {}) => { + const install = Vue => { if (install.installed) { /* istanbul ignore next */ return @@ -77,9 +77,9 @@ export const installFactoryNoConfig = ({ components, directives, plugins } = {}) * @param {object} { components, directives, plugins } * @returns {object} plugin install object */ -export const pluginFactory = (opts = {}, extend = {}) => ({ +export const pluginFactory = (options = {}, extend = {}) => ({ ...extend, - install: installFactory(opts) + install: installFactory(options) }) /** @@ -87,9 +87,9 @@ export const pluginFactory = (opts = {}, extend = {}) => ({ * @param {object} { components, directives, plugins } * @returns {object} plugin install object */ -export const pluginFactoryNoConfig = (opts = {}, extend = {}) => ({ +export const pluginFactoryNoConfig = (options = {}, extend = {}) => ({ ...extend, - install: installFactoryNoConfig(opts) + install: installFactoryNoConfig(options) }) /** diff --git a/src/utils/target.js b/src/utils/target.js index 88ab909869a..a25bff09fe8 100644 --- a/src/utils/target.js +++ b/src/utils/target.js @@ -1,5 +1,5 @@ import { keys } from './object' -import { eventOn, eventOff } from './dom' +import { eventOn, eventOff } from './events' const allListenTypes = { hover: true, click: true, focus: true } diff --git a/src/utils/transporter.spec.js b/src/utils/transporter.spec.js index ebf68c5efa3..5008dd2fbad 100644 --- a/src/utils/transporter.spec.js +++ b/src/utils/transporter.spec.js @@ -8,7 +8,7 @@ describe('utils/transporter component', () => { it('renders in-pace when disabled=true', async () => { const App = localVue.extend({ render(h) { - return h(BTransporterSingle, { props: { disabled: true } }, [h('div', {}, 'content')]) + return h(BTransporterSingle, { props: { disabled: true } }, [h('div', 'content')]) } }) From e9a99e6e2cf080ae5d9783229b93fd2eaeedf436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacob=20M=C3=BCller?= Date: Thu, 13 Feb 2020 21:34:58 +0100 Subject: [PATCH 03/15] fix(events): correct `capture` value of `EVENT_OPTIONS_NO_CAPTURE` (#4763) --- src/utils/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/events.js b/src/utils/events.js index 2b489cc10f1..aba2207ad8d 100644 --- a/src/utils/events.js +++ b/src/utils/events.js @@ -4,7 +4,7 @@ import { isObject } from './inspect' // --- Constants --- export const EVENT_OPTIONS_PASSIVE = { passive: true } -export const EVENT_OPTIONS_NO_CAPTURE = { passive: true, capture: true } +export const EVENT_OPTIONS_NO_CAPTURE = { passive: true, capture: false } // --- Utils --- From 214168b73db1227c3dcc525c75f39dbef4b9127f Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Thu, 13 Feb 2020 18:39:41 -0400 Subject: [PATCH 04/15] chore: ignore `browser.js` and browser-icons.js` during esm modular build (#4764) --- scripts/build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index c4757f0a27c..319e39f6370 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -29,7 +29,8 @@ echo 'Compiling ESM modular build...' NODE_ENV=esm babel src \ --out-dir esm \ --ignore 'src/**/*.spec.js' \ - --ignore 'src/browser*.js' \ + --ignore 'src/browser.js' \ + --ignore 'src/browser-icons.js' \ --ignore 'src/icons-only.js' echo "${BV_BANNER}" | cat - esm/index.js > esm/tmp.js && mv -f esm/tmp.js esm/index.js echo 'Done.' From 6adcd0f86a43ad01fe67284d2505b9ef9257ab00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2020 06:54:16 +0100 Subject: [PATCH 05/15] chore(deps): update devdependency rollup to ^1.31.1 (#4765) Co-authored-by: WhiteSource Renovate --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d8717a531db..4931e632593 100644 --- a/package.json +++ b/package.json @@ -145,7 +145,7 @@ "postcss-cli": "^7.1.0", "prettier": "1.14.3", "require-context": "^1.1.0", - "rollup": "^1.31.0", + "rollup": "^1.31.1", "rollup-plugin-babel": "^4.3.3", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", diff --git a/yarn.lock b/yarn.lock index b84d7f14515..3dfa95a597e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11749,10 +11749,10 @@ rollup-pluginutils@^2.8.1: dependencies: estree-walker "^0.6.1" -rollup@^1.31.0: - version "1.31.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.31.0.tgz#e2a87212e96aa7850f3eb53fdd02cf89f2d2fe9a" - integrity sha512-9C6ovSyNeEwvuRuUUmsTpJcXac1AwSL1a3x+O5lpmQKZqi5mmrjauLeqIjvREC+yNRR8fPdzByojDng+af3nVw== +rollup@^1.31.1: + version "1.31.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.31.1.tgz#4170d6f87148d46e5fbe29b493f8f3ea3453c96f" + integrity sha512-2JREN1YdrS/kpPzEd33ZjtuNbOuBC3ePfuZBdKEybvqcEcszW1ckyVqzcEiEe0nE8sqHK+pbJg+PsAgRJ8+1dg== dependencies: "@types/estree" "*" "@types/node" "*" From 968c95758e45610a8c002507790c79d87d8fe956 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Fri, 14 Feb 2020 12:43:44 -0400 Subject: [PATCH 06/15] fix(b-modal): transition timing (#4766) --- src/components/modal/modal.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/modal/modal.js b/src/components/modal/modal.js index a82d9f58dba..b70cd1a7eda 100644 --- a/src/components/modal/modal.js +++ b/src/components/modal/modal.js @@ -604,10 +604,13 @@ export const BModal = /*#__PURE__*/ Vue.extend({ }, onEnter() { this.isBlock = true + // We add show class 1 frame after + requestAF(() => { + this.isShow = true + }) }, onAfterEnter() { this.checkModalOverflow() - this.isShow = true this.isTransitioning = false // We use `requestAF()` to allow transition hooks to complete // before passing control over to the other handlers From 97a65c2150c212767cdd650cb8aa390e45e19d38 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Fri, 14 Feb 2020 14:12:17 -0400 Subject: [PATCH 07/15] docs(popovers, tooltips): add info about focus only trigger and cross platform behaviour (#4767) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(popovers, tooltips): add info about focus only trigger and cross platform behaviour * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md Co-authored-by: Jacob Müller --- src/components/popover/README.md | 30 ++++++++++++++++++++++++++++-- src/components/tooltip/README.md | 26 ++++++++++++++++++++++++++ src/directives/popover/README.md | 19 +++++++++++++++++++ src/directives/tooltip/README.md | 19 +++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/components/popover/README.md b/src/components/popover/README.md index 3bccd6f360b..96bebbbb432 100644 --- a/src/components/popover/README.md +++ b/src/components/popover/README.md @@ -160,11 +160,37 @@ If a popover has more than one trigger, then all triggers must be cleared before close. I.e. if a popover has the trigger `focus click`, and it was opened by `focus`, and the user then clicks the trigger element, they must click it again **and** move focus to close the popover. +### Caveats with `focus` trigger on `