diff --git a/.eslintrc.js b/.eslintrc.js index 0ba3d0ff0bd..1a064631871 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,9 +15,6 @@ module.exports = { es6: true, 'jest/globals': true }, - globals: { - Vue: true - }, rules: { 'no-unused-vars': [ 'error', diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e84c1129df0..4185f1c5f1e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -125,8 +125,19 @@ jobs: - name: Test unit run: yarn run test:unit --coverage --maxWorkers=2 + - name: Test unit (Vue 3) + run: yarn run test:unit --coverage --maxWorkers=2 + env: + USE_VUE3: '1' + + - name: Merge coverage + run: + npx istanbul-merge --out ./coverage-final.json coverage/coverage-final.json + coverage-vue3/coverage-final.json + - name: CodeCov uses: codecov/codecov-action@v3.1.1 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests + files: ./coverage-final.json diff --git a/.gitignore b/.gitignore index 91a43b70be3..38d10d68e50 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .vercel/ .vscode/ coverage/ +coverage-vue3/ dist/ docs-dist/ esm/ diff --git a/jest.config.js b/jest.config.js index 044993b8baf..56b7921e99d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,11 +1,21 @@ +const useVue3 = 'USE_VUE3' in process.env + +const moduleNameMapper = useVue3 + ? { + '^vue$': '@vue/compat', + '^@vue/test-utils$': '@vue/test-utils-vue3' + } + : {} + module.exports = { testRegex: 'spec.js$', moduleFileExtensions: ['js', 'vue'], + moduleNameMapper, transform: { - '^.+\\.js$': 'babel-jest', - '.*\\.(vue)$': 'vue-jest' + '^.+\\.js$': 'babel-jest' }, - coverageDirectory: './coverage/', + transformIgnorePatterns: ['/node_modules(?![\\\\/]vue-test-utils-compat[\\\\/])'], + coverageDirectory: useVue3 ? './coverage-vue3' : './coverage/', testEnvironmentOptions: { pretendToBeVisual: true }, diff --git a/package.json b/package.json index b566ad1cc71..a1fcd547bec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue", - "version": "2.22.0", + "version": "2.23.0", "description": "With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extensive and automated WAI-ARIA accessibility markup.", "main": "./dist/bootstrap-vue.common.js", "web": "./dist/bootstrap-vue.js", @@ -66,7 +66,7 @@ "docs-gen": "cross-env NODE_ENV=docs nuxt generate -c docs/nuxt.config.js", "lint": "eslint --ext .js,.md,.vue ./", "postinstall": "opencollective || exit 0", - "prepare": "husky install", + "prepare": "husky install && yarn run build", "prettify": "prettier --write '**/*.{js,json,md,scss,ts,vue}'", "release": "yarn run prettify && yarn run test && yarn run build && yarn run release-notes && standard-version", "release-notes": "jiti ./scripts/release-notes", @@ -99,7 +99,10 @@ "@nuxtjs/robots": "^2.5.0", "@nuxtjs/sitemap": "^2.4.0", "@testing-library/jest-dom": "^5.12.0", + "@vue/compat": "^3.2.40", + "@vue/compiler-dom": "^3.2.40", "@vue/test-utils": "^1.3.0", + "@vue/test-utils-vue3": "npm:@vue/test-utils@2.2.0", "autoprefixer": "^10.4.0", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.1.0", @@ -148,10 +151,10 @@ "standard-version": "^9.3.0", "terser": "^5.15.0", "vue": "^2.6.12", - "vue-jest": "^3.0.7", "vue-router": "^3.5.1", "vue-server-renderer": "^2.6.12", - "vue-template-compiler": "^2.6.12" + "vue-template-compiler": "^2.6.12", + "vue-test-utils-compat": "0.0.6" }, "keywords": [ "Bootstrap", diff --git a/src/components/alert/alert.js b/src/components/alert/alert.js index 89875b81b2f..e618006c35d 100644 --- a/src/components/alert/alert.js +++ b/src/components/alert/alert.js @@ -1,4 +1,3 @@ -import { COMPONENT_UID_KEY, Vue } from '../../vue' import { NAME_ALERT } from '../../constants/components' import { EVENT_NAME_DISMISSED, EVENT_NAME_DISMISS_COUNT_DOWN } from '../../constants/events' import { @@ -7,13 +6,14 @@ import { PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_DISMISS } from '../../constants/slots' +import { normalizeSlotMixin } from '../../mixins/normalize-slot' import { requestAF } from '../../utils/dom' import { isBoolean, isNumeric } from '../../utils/inspect' import { makeModelMixin } from '../../utils/model' import { toInteger } from '../../utils/number' import { sortKeys } from '../../utils/object' import { makeProp, makePropsConfigurable } from '../../utils/props' -import { normalizeSlotMixin } from '../../mixins/normalize-slot' +import { COMPONENT_UID_KEY, extend } from '../../vue' import { BButtonClose } from '../button/button-close' import { BVTransition } from '../transition/bv-transition' @@ -68,7 +68,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BAlert = /*#__PURE__*/ Vue.extend({ +export const BAlert = /*#__PURE__*/ extend({ name: NAME_ALERT, mixins: [modelMixin, normalizeSlotMixin], props, diff --git a/src/components/aspect/aspect.js b/src/components/aspect/aspect.js index d9fce4bb124..772cf59087f 100644 --- a/src/components/aspect/aspect.js +++ b/src/components/aspect/aspect.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_ASPECT } from '../../constants/components' import { PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props' import { RX_ASPECT, RX_ASPECT_SEPARATOR } from '../../constants/regex' @@ -26,7 +26,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BAspect = /*#__PURE__*/ Vue.extend({ +export const BAspect = /*#__PURE__*/ extend({ name: NAME_ASPECT, mixins: [normalizeSlotMixin], props, diff --git a/src/components/avatar/avatar-group.js b/src/components/avatar/avatar-group.js index e80bc3b2190..2109bd46f1d 100644 --- a/src/components/avatar/avatar-group.js +++ b/src/components/avatar/avatar-group.js @@ -1,4 +1,3 @@ -import { Vue } from '../../vue' import { NAME_AVATAR_GROUP } from '../../constants/components' import { PROP_TYPE_BOOLEAN, @@ -6,10 +5,11 @@ import { PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props' +import { normalizeSlotMixin } from '../../mixins/normalize-slot' import { mathMax, mathMin } from '../../utils/math' import { toFloat } from '../../utils/number' import { makeProp, makePropsConfigurable } from '../../utils/props' -import { normalizeSlotMixin } from '../../mixins/normalize-slot' +import { extend } from '../../vue' import { computeSize } from './avatar' // --- Props --- @@ -33,11 +33,11 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BAvatarGroup = /*#__PURE__*/ Vue.extend({ +export const BAvatarGroup = /*#__PURE__*/ extend({ name: NAME_AVATAR_GROUP, mixins: [normalizeSlotMixin], provide() { - return { bvAvatarGroup: this } + return { getBvAvatarGroup: () => this } }, props, computed: { diff --git a/src/components/avatar/avatar.js b/src/components/avatar/avatar.js index c510f198c1d..cea0423f1ab 100644 --- a/src/components/avatar/avatar.js +++ b/src/components/avatar/avatar.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_AVATAR } from '../../constants/components' import { EVENT_NAME_CLICK, EVENT_NAME_IMG_ERROR } from '../../constants/events' import { @@ -67,11 +67,11 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BAvatar = /*#__PURE__*/ Vue.extend({ +export const BAvatar = /*#__PURE__*/ extend({ name: NAME_AVATAR, mixins: [normalizeSlotMixin], inject: { - bvAvatarGroup: { default: null } + getBvAvatarGroup: { default: () => () => null } }, props, data() { @@ -80,6 +80,9 @@ export const BAvatar = /*#__PURE__*/ Vue.extend({ } }, computed: { + bvAvatarGroup() { + return this.getBvAvatarGroup() + }, computedSize() { // Always use the avatar group size const { bvAvatarGroup } = this diff --git a/src/components/avatar/avatar.spec.js b/src/components/avatar/avatar.spec.js index 304766a4d84..013698ff412 100644 --- a/src/components/avatar/avatar.spec.js +++ b/src/components/avatar/avatar.spec.js @@ -250,7 +250,7 @@ describe('avatar', () => { const wrapper1 = mount(BAvatar, { provide: { // Emulate `undefined`/`null` props - bvAvatarGroup: {} + getBvAvatarGroup: () => ({}) } }) @@ -265,9 +265,9 @@ describe('avatar', () => { const wrapper2 = mount(BAvatar, { provide: { - bvAvatarGroup: { + getBvAvatarGroup: () => ({ variant: 'danger' - } + }) } }) @@ -289,7 +289,7 @@ describe('avatar', () => { }, provide: { // Emulate `undefined`/`null` props - bvAvatarGroup: {} + getBvAvatarGroup: () => ({}) } }) @@ -307,9 +307,9 @@ describe('avatar', () => { size: '2em' }, provide: { - bvAvatarGroup: { + getBvAvatarGroup: () => ({ size: '5em' - } + }) } }) diff --git a/src/components/badge/badge.js b/src/components/badge/badge.js index bff564a3fa4..37c6ffc12e8 100644 --- a/src/components/badge/badge.js +++ b/src/components/badge/badge.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BADGE } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { omit, sortKeys } from '../../utils/object' @@ -25,7 +25,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BBadge = /*#__PURE__*/ Vue.extend({ +export const BBadge = /*#__PURE__*/ extend({ name: NAME_BADGE, functional: true, props, diff --git a/src/components/breadcrumb/breadcrumb-item.js b/src/components/breadcrumb/breadcrumb-item.js index 02cd6e13156..073fa4f577f 100644 --- a/src/components/breadcrumb/breadcrumb-item.js +++ b/src/components/breadcrumb/breadcrumb-item.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BREADCRUMB_ITEM } from '../../constants/components' import { makePropsConfigurable } from '../../utils/props' import { BBreadcrumbLink, props as BBreadcrumbLinkProps } from './breadcrumb-link' @@ -10,7 +10,7 @@ export const props = makePropsConfigurable(BBreadcrumbLinkProps, NAME_BREADCRUMB // --- Main component --- // @vue/component -export const BBreadcrumbItem = /*#__PURE__*/ Vue.extend({ +export const BBreadcrumbItem = /*#__PURE__*/ extend({ name: NAME_BREADCRUMB_ITEM, functional: true, props, diff --git a/src/components/breadcrumb/breadcrumb-link.js b/src/components/breadcrumb/breadcrumb-link.js index 3fdd65652b0..da7338c7e1f 100644 --- a/src/components/breadcrumb/breadcrumb-link.js +++ b/src/components/breadcrumb/breadcrumb-link.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BREADCRUMB_LINK } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { htmlOrText } from '../../utils/html' @@ -21,7 +21,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BBreadcrumbLink = /*#__PURE__*/ Vue.extend({ +export const BBreadcrumbLink = /*#__PURE__*/ extend({ name: NAME_BREADCRUMB_LINK, functional: true, props, diff --git a/src/components/breadcrumb/breadcrumb.js b/src/components/breadcrumb/breadcrumb.js index 0bb16805185..90c3235ec04 100644 --- a/src/components/breadcrumb/breadcrumb.js +++ b/src/components/breadcrumb/breadcrumb.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BREADCRUMB } from '../../constants/components' import { PROP_TYPE_ARRAY } from '../../constants/props' import { isArray, isObject } from '../../utils/inspect' @@ -18,7 +18,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BBreadcrumb = /*#__PURE__*/ Vue.extend({ +export const BBreadcrumb = /*#__PURE__*/ extend({ name: NAME_BREADCRUMB, functional: true, props, diff --git a/src/components/button-group/button-group.js b/src/components/button-group/button-group.js index 16850a8e305..ea4e2d453da 100644 --- a/src/components/button-group/button-group.js +++ b/src/components/button-group/button-group.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BUTTON_GROUP } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { pick, sortKeys } from '../../utils/object' @@ -21,7 +21,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BButtonGroup = /*#__PURE__*/ Vue.extend({ +export const BButtonGroup = /*#__PURE__*/ extend({ name: NAME_BUTTON_GROUP, functional: true, props, diff --git a/src/components/button-toolbar/button-toolbar.js b/src/components/button-toolbar/button-toolbar.js index af0d6eda7ea..092ed02ad33 100644 --- a/src/components/button-toolbar/button-toolbar.js +++ b/src/components/button-toolbar/button-toolbar.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_BUTTON_TOOLBAR } from '../../constants/components' import { PROP_TYPE_BOOLEAN } from '../../constants/props' import { CODE_DOWN, CODE_LEFT, CODE_RIGHT, CODE_UP } from '../../constants/key-codes' @@ -30,7 +30,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BButtonToolbar = /*#__PURE__*/ Vue.extend({ +export const BButtonToolbar = /*#__PURE__*/ extend({ name: NAME_BUTTON_TOOLBAR, mixins: [normalizeSlotMixin], props, diff --git a/src/components/button-toolbar/button-toolbar.spec.js b/src/components/button-toolbar/button-toolbar.spec.js index 0167a87d85d..dcb0ec5003c 100644 --- a/src/components/button-toolbar/button-toolbar.spec.js +++ b/src/components/button-toolbar/button-toolbar.spec.js @@ -108,42 +108,12 @@ describe('button-toolbar', () => { const $btns = wrapper.findAll('button') expect($btns).toBeDefined() expect($btns.length).toBe(6) - expect( - $btns - .at(0) - .find('button[tabindex="-1"') - .exists() - ).toBe(true) - expect( - $btns - .at(1) - .find('button[tabindex="-1"') - .exists() - ).toBe(true) - expect( - $btns - .at(2) - .find('button[tabindex="-1"') - .exists() - ).toBe(false) // Disabled button - expect( - $btns - .at(3) - .find('button[tabindex="-1"') - .exists() - ).toBe(true) - expect( - $btns - .at(4) - .find('button[tabindex="-1"') - .exists() - ).toBe(true) - expect( - $btns - .at(5) - .find('button[tabindex="-1"') - .exists() - ).toBe(true) + expect($btns.at(0).element.matches('button[tabindex="-1"')).toBe(true) + expect($btns.at(1).element.matches('button[tabindex="-1"')).toBe(true) + expect($btns.at(2).element.matches('button[tabindex="-1"')).toBe(false) // Disabled button + expect($btns.at(3).element.matches('button[tabindex="-1"')).toBe(true) + expect($btns.at(4).element.matches('button[tabindex="-1"')).toBe(true) + expect($btns.at(5).element.matches('button[tabindex="-1"')).toBe(true) wrapper.destroy() }) diff --git a/src/components/button/button-close.js b/src/components/button/button-close.js index aa3f62ca717..667747cc8eb 100644 --- a/src/components/button/button-close.js +++ b/src/components/button/button-close.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BUTTON_CLOSE } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_DEFAULT } from '../../constants/slots' @@ -22,7 +22,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BButtonClose = /*#__PURE__*/ Vue.extend({ +export const BButtonClose = /*#__PURE__*/ extend({ name: NAME_BUTTON_CLOSE, functional: true, props, diff --git a/src/components/button/button.js b/src/components/button/button.js index 97b49883cef..b9723636e55 100644 --- a/src/components/button/button.js +++ b/src/components/button/button.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_BUTTON } from '../../constants/components' import { CODE_ENTER, CODE_SPACE } from '../../constants/key-codes' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' @@ -115,7 +115,7 @@ const computeAttrs = (props, data) => { // --- Main component --- // @vue/component -export const BButton = /*#__PURE__*/ Vue.extend({ +export const BButton = /*#__PURE__*/ extend({ name: NAME_BUTTON, functional: true, props, @@ -170,6 +170,10 @@ export const BButton = /*#__PURE__*/ Vue.extend({ on } - return h(link ? BLink : props.tag, mergeData(data, componentData), children) + return h( + link ? BLink : props.tag, + mergeData({ ...data, props: undefined }, componentData), + children + ) } }) diff --git a/src/components/calendar/calendar.js b/src/components/calendar/calendar.js index b68013a1910..880316c9b91 100644 --- a/src/components/calendar/calendar.js +++ b/src/components/calendar/calendar.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_CALENDAR } from '../../constants/components' import { CALENDAR_GREGORY, @@ -180,7 +180,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCalendar = Vue.extend({ +export const BCalendar = extend({ name: NAME_CALENDAR, // Mixin order is important! mixins: [attrsMixin, idMixin, modelMixin, normalizeSlotMixin], @@ -811,7 +811,7 @@ export const BCalendar = Vue.extend({ { staticClass: 'b-calendar-header', class: { 'sr-only': this.hideHeader }, - attrs: { title: this.selectedDate ? this.labelSelectedDate || null : null } + attrs: { title: this.selectedDate ? this.labelSelected || null : null } }, [$header] ) diff --git a/src/components/card/card-body.js b/src/components/card/card-body.js index ee6e99eb04e..4d181360961 100644 --- a/src/components/card/card-body.js +++ b/src/components/card/card-body.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_BODY } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_BOOLEAN } from '../../constants/props' import { sortKeys } from '../../utils/object' @@ -29,7 +29,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardBody = /*#__PURE__*/ Vue.extend({ +export const BCardBody = /*#__PURE__*/ extend({ name: NAME_CARD_BODY, functional: true, props, diff --git a/src/components/card/card-footer.js b/src/components/card/card-footer.js index 93be2d16227..4540162b3c8 100644 --- a/src/components/card/card-footer.js +++ b/src/components/card/card-footer.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_FOOTER } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' import { htmlOrText } from '../../utils/html' @@ -21,7 +21,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardFooter = /*#__PURE__*/ Vue.extend({ +export const BCardFooter = /*#__PURE__*/ extend({ name: NAME_CARD_FOOTER, functional: true, props, diff --git a/src/components/card/card-group.js b/src/components/card/card-group.js index 9cc0eeb61f7..bbf6e869935 100644 --- a/src/components/card/card-group.js +++ b/src/components/card/card-group.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_GROUP } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -17,7 +17,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardGroup = /*#__PURE__*/ Vue.extend({ +export const BCardGroup = /*#__PURE__*/ extend({ name: NAME_CARD_GROUP, functional: true, props, diff --git a/src/components/card/card-header.js b/src/components/card/card-header.js index 945dd346b37..82d39d5da0c 100644 --- a/src/components/card/card-header.js +++ b/src/components/card/card-header.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_HEADER } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' import { htmlOrText } from '../../utils/html' @@ -21,7 +21,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardHeader = /*#__PURE__*/ Vue.extend({ +export const BCardHeader = /*#__PURE__*/ extend({ name: NAME_CARD_HEADER, functional: true, props, diff --git a/src/components/card/card-img-lazy.js b/src/components/card/card-img-lazy.js index 178f3c8d21d..208fb412182 100644 --- a/src/components/card/card-img-lazy.js +++ b/src/components/card/card-img-lazy.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_IMG_LAZY } from '../../constants/components' import { keys, omit, sortKeys } from '../../utils/object' import { makePropsConfigurable } from '../../utils/props' @@ -19,7 +19,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardImgLazy = /*#__PURE__*/ Vue.extend({ +export const BCardImgLazy = /*#__PURE__*/ extend({ name: NAME_CARD_IMG_LAZY, functional: true, props, diff --git a/src/components/card/card-img.js b/src/components/card/card-img.js index 900a6b4e4b1..ed2a7ef03de 100644 --- a/src/components/card/card-img.js +++ b/src/components/card/card-img.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_IMG } from '../../constants/components' import { PROP_TYPE_BOOLEAN } from '../../constants/props' import { pick, sortKeys } from '../../utils/object' @@ -21,7 +21,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardImg = /*#__PURE__*/ Vue.extend({ +export const BCardImg = /*#__PURE__*/ extend({ name: NAME_CARD_IMG, functional: true, props, diff --git a/src/components/card/card-sub-title.js b/src/components/card/card-sub-title.js index 14e79e40ed9..1b133c153b5 100644 --- a/src/components/card/card-sub-title.js +++ b/src/components/card/card-sub-title.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_SUB_TITLE } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -18,7 +18,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardSubTitle = /*#__PURE__*/ Vue.extend({ +export const BCardSubTitle = /*#__PURE__*/ extend({ name: NAME_CARD_SUB_TITLE, functional: true, props, diff --git a/src/components/card/card-text.js b/src/components/card/card-text.js index a9ebdee33e6..c82f8df39e7 100644 --- a/src/components/card/card-text.js +++ b/src/components/card/card-text.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_TEXT } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -15,7 +15,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardText = /*#__PURE__*/ Vue.extend({ +export const BCardText = /*#__PURE__*/ extend({ name: NAME_CARD_TEXT, functional: true, props, diff --git a/src/components/card/card-title.js b/src/components/card/card-title.js index 768efb10657..e2d46d79ad1 100644 --- a/src/components/card/card-title.js +++ b/src/components/card/card-title.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD_TITLE } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -17,7 +17,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCardTitle = /*#__PURE__*/ Vue.extend({ +export const BCardTitle = /*#__PURE__*/ extend({ name: NAME_CARD_TITLE, functional: true, props, diff --git a/src/components/card/card.js b/src/components/card/card.js index b2f7caef3b6..4837af66c61 100644 --- a/src/components/card/card.js +++ b/src/components/card/card.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CARD } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_DEFAULT, SLOT_NAME_FOOTER, SLOT_NAME_HEADER } from '../../constants/slots' @@ -40,7 +40,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCard = /*#__PURE__*/ Vue.extend({ +export const BCard = /*#__PURE__*/ extend({ name: NAME_CARD, functional: true, props, diff --git a/src/components/carousel/carousel-slide.js b/src/components/carousel/carousel-slide.js index 95a0396185a..6a7e094f554 100644 --- a/src/components/carousel/carousel-slide.js +++ b/src/components/carousel/carousel-slide.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_CAROUSEL_SLIDE } from '../../constants/components' import { HAS_TOUCH_SUPPORT } from '../../constants/env' import { PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props' @@ -43,17 +43,20 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCarouselSlide = /*#__PURE__*/ Vue.extend({ +export const BCarouselSlide = /*#__PURE__*/ extend({ name: NAME_CAROUSEL_SLIDE, mixins: [idMixin, normalizeSlotMixin], inject: { - bvCarousel: { + getBvCarousel: { // Explicitly disable touch if not a child of carousel - default: () => ({ noTouch: true }) + default: () => () => ({ noTouch: true }) } }, props, computed: { + bvCarousel() { + return this.getBvCarousel() + }, contentClasses() { return [ this.contentVisibleUp ? 'd-none' : '', diff --git a/src/components/carousel/carousel-slide.spec.js b/src/components/carousel/carousel-slide.spec.js index 5e26b93084b..832f8966740 100644 --- a/src/components/carousel/carousel-slide.spec.js +++ b/src/components/carousel/carousel-slide.spec.js @@ -144,9 +144,9 @@ describe('carousel-slide', () => { it('has style background inherited from carousel parent', async () => { const wrapper = mount(BCarouselSlide, { provide: { - bvCarousel: { + getBvCarousel: () => ({ background: 'rgb(1, 2, 3)' - } + }) } }) @@ -254,10 +254,10 @@ describe('carousel-slide', () => { const wrapper = mount(BCarouselSlide, { provide: { // Mock carousel injection - bvCarousel: { + getBvCarousel: () => ({ imgWidth: '1024', imgHeight: '480' - } + }) }, propsData: { imgSrc: 'https://picsum.photos/1024/480/?image=52' diff --git a/src/components/carousel/carousel.js b/src/components/carousel/carousel.js index 287b311e716..0340734868d 100644 --- a/src/components/carousel/carousel.js +++ b/src/components/carousel/carousel.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_CAROUSEL } from '../../constants/components' import { IS_BROWSER, HAS_POINTER_EVENT_SUPPORT, HAS_TOUCH_SUPPORT } from '../../constants/env' import { @@ -132,11 +132,11 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCarousel = /*#__PURE__*/ Vue.extend({ +export const BCarousel = /*#__PURE__*/ extend({ name: NAME_CAROUSEL, mixins: [idMixin, modelMixin, normalizeSlotMixin], provide() { - return { bvCarousel: this } + return { getBvCarousel: () => this } }, props, data() { diff --git a/src/components/collapse/collapse.js b/src/components/collapse/collapse.js index 07e55ce891a..ca8bdba7228 100644 --- a/src/components/collapse/collapse.js +++ b/src/components/collapse/collapse.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_COLLAPSE } from '../../constants/components' import { CLASS_NAME_SHOW } from '../../constants/classes' import { IS_BROWSER } from '../../constants/env' @@ -55,7 +55,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BCollapse = /*#__PURE__*/ Vue.extend({ +export const BCollapse = /*#__PURE__*/ extend({ name: NAME_COLLAPSE, mixins: [idMixin, modelMixin, normalizeSlotMixin, listenOnRootMixin], props, diff --git a/src/components/collapse/helpers/bv-collapse.js b/src/components/collapse/helpers/bv-collapse.js index 5b4b6806379..ea96340db66 100644 --- a/src/components/collapse/helpers/bv-collapse.js +++ b/src/components/collapse/helpers/bv-collapse.js @@ -5,7 +5,7 @@ // during the enter/leave transition phases only // Although it appears that Vue may be leaving the classes // in-place after the transition completes -import { Vue, mergeData } from '../../../vue' +import { extend, mergeData } from '../../../vue' import { NAME_COLLAPSE_HELPER } from '../../../constants/components' import { PROP_TYPE_BOOLEAN } from '../../../constants/props' import { getBCR, reflow, removeStyle, requestAF, setStyle } from '../../../utils/dom' @@ -72,7 +72,7 @@ export const props = { // --- Main component --- // @vue/component -export const BVCollapse = /*#__PURE__*/ Vue.extend({ +export const BVCollapse = /*#__PURE__*/ extend({ name: NAME_COLLAPSE_HELPER, functional: true, props, diff --git a/src/components/dropdown/dropdown-divider.js b/src/components/dropdown/dropdown-divider.js index a5531bd8212..e1f4d27f6fa 100644 --- a/src/components/dropdown/dropdown-divider.js +++ b/src/components/dropdown/dropdown-divider.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_DROPDOWN_DIVIDER } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -16,7 +16,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownDivider = /*#__PURE__*/ Vue.extend({ +export const BDropdownDivider = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_DIVIDER, functional: true, props, diff --git a/src/components/dropdown/dropdown-form.js b/src/components/dropdown/dropdown-form.js index 4c9109f458a..faf55523d40 100644 --- a/src/components/dropdown/dropdown-form.js +++ b/src/components/dropdown/dropdown-form.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_DROPDOWN_FORM } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_BOOLEAN } from '../../constants/props' import { omit, sortKeys } from '../../utils/object' @@ -19,7 +19,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownForm = /*#__PURE__*/ Vue.extend({ +export const BDropdownForm = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_FORM, functional: true, props, diff --git a/src/components/dropdown/dropdown-group.js b/src/components/dropdown/dropdown-group.js index a30c93c6dba..1c84e6167c0 100644 --- a/src/components/dropdown/dropdown-group.js +++ b/src/components/dropdown/dropdown-group.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_DROPDOWN_GROUP } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_DEFAULT, SLOT_NAME_HEADER } from '../../constants/slots' @@ -25,7 +25,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownGroup = /*#__PURE__*/ Vue.extend({ +export const BDropdownGroup = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_GROUP, functional: true, props, diff --git a/src/components/dropdown/dropdown-header.js b/src/components/dropdown/dropdown-header.js index f1721d937ea..600ad6ae4e9 100644 --- a/src/components/dropdown/dropdown-header.js +++ b/src/components/dropdown/dropdown-header.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_DROPDOWN_HEADER } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { isTag } from '../../utils/dom' @@ -19,7 +19,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownHeader = /*#__PURE__*/ Vue.extend({ +export const BDropdownHeader = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_HEADER, functional: true, props, diff --git a/src/components/dropdown/dropdown-item-button.js b/src/components/dropdown/dropdown-item-button.js index 6e3b1057a85..8bb700ad9e4 100644 --- a/src/components/dropdown/dropdown-item-button.js +++ b/src/components/dropdown/dropdown-item-button.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_DROPDOWN_ITEM_BUTTON } from '../../constants/components' import { EVENT_NAME_CLICK } from '../../constants/events' import { @@ -26,15 +26,19 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownItemButton = /*#__PURE__*/ Vue.extend({ +export const BDropdownItemButton = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_ITEM_BUTTON, mixins: [attrsMixin, normalizeSlotMixin], inject: { - bvDropdown: { default: null } + getBvDropdown: { default: () => () => null } }, inheritAttrs: false, props, computed: { + bvDropdown() { + return this.getBvDropdown() + }, + computedAttrs() { return { ...this.bvAttrs, diff --git a/src/components/dropdown/dropdown-item-button.spec.js b/src/components/dropdown/dropdown-item-button.spec.js index 0c01b8c1301..66e5183537d 100644 --- a/src/components/dropdown/dropdown-item-button.spec.js +++ b/src/components/dropdown/dropdown-item-button.spec.js @@ -54,12 +54,12 @@ describe('dropdown-item-button', () => { let refocus = null const wrapper = mount(BDropdownItemButton, { provide: { - bvDropdown: { + getBvDropdown: () => ({ hide(arg) { called = true refocus = arg } - } + }) } }) expect(wrapper.element.tagName).toBe('LI') @@ -81,12 +81,12 @@ describe('dropdown-item-button', () => { disabled: true }, provide: { - bvDropdown: { + getBvDropdown: () => ({ hide(arg) { called = true refocus = arg } - } + }) } }) expect(wrapper.element.tagName).toBe('LI') diff --git a/src/components/dropdown/dropdown-item.js b/src/components/dropdown/dropdown-item.js index d2bb1da1d58..79cb76132d5 100644 --- a/src/components/dropdown/dropdown-item.js +++ b/src/components/dropdown/dropdown-item.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_DROPDOWN_ITEM } from '../../constants/components' import { EVENT_NAME_CLICK } from '../../constants/events' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' @@ -25,15 +25,18 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownItem = /*#__PURE__*/ Vue.extend({ +export const BDropdownItem = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_ITEM, mixins: [attrsMixin, normalizeSlotMixin], inject: { - bvDropdown: { default: null } + getBvDropdown: { default: () => () => null } }, inheritAttrs: false, props, computed: { + bvDropdown() { + return this.getBvDropdown() + }, computedAttrs() { return { ...this.bvAttrs, diff --git a/src/components/dropdown/dropdown-item.spec.js b/src/components/dropdown/dropdown-item.spec.js index 5533e58df67..f5b6dcd8af3 100644 --- a/src/components/dropdown/dropdown-item.spec.js +++ b/src/components/dropdown/dropdown-item.spec.js @@ -34,12 +34,12 @@ describe('dropdown-item', () => { let refocus = null const wrapper = mount(BDropdownItem, { provide: { - bvDropdown: { + getBvDropdown: () => ({ hide(arg) { called = true refocus = arg } - } + }) } }) expect(wrapper.element.tagName).toBe('LI') @@ -60,12 +60,12 @@ describe('dropdown-item', () => { const wrapper = mount(BDropdownItem, { propsData: { disabled: true }, provide: { - bvDropdown: { + getBvDropdown: () => ({ hide(arg) { called = true refocus = arg } - } + }) } }) expect(wrapper.element.tagName).toBe('LI') diff --git a/src/components/dropdown/dropdown-text.js b/src/components/dropdown/dropdown-text.js index d269ac4586f..e8426d19bb4 100644 --- a/src/components/dropdown/dropdown-text.js +++ b/src/components/dropdown/dropdown-text.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_DROPDOWN_TEXT } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props' import { omit } from '../../utils/object' @@ -18,7 +18,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdownText = /*#__PURE__*/ Vue.extend({ +export const BDropdownText = /*#__PURE__*/ extend({ name: NAME_DROPDOWN_TEXT, functional: true, props, diff --git a/src/components/dropdown/dropdown.js b/src/components/dropdown/dropdown.js index bc9ce938e9c..0e7a9e0e6d0 100644 --- a/src/components/dropdown/dropdown.js +++ b/src/components/dropdown/dropdown.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_DROPDOWN } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, @@ -54,7 +54,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BDropdown = /*#__PURE__*/ Vue.extend({ +export const BDropdown = /*#__PURE__*/ extend({ name: NAME_DROPDOWN, mixins: [idMixin, dropdownMixin, normalizeSlotMixin], props, diff --git a/src/components/embed/embed.js b/src/components/embed/embed.js index fdb54894301..fb1d0b0e50a 100644 --- a/src/components/embed/embed.js +++ b/src/components/embed/embed.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_EMBED } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { arrayIncludes } from '../../utils/array' @@ -25,7 +25,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BEmbed = /*#__PURE__*/ Vue.extend({ +export const BEmbed = /*#__PURE__*/ extend({ name: NAME_EMBED, functional: true, props, diff --git a/src/components/form-btn-label-control/bv-form-btn-label-control.js b/src/components/form-btn-label-control/bv-form-btn-label-control.js index e830ec9035b..10645ae5ece 100644 --- a/src/components/form-btn-label-control/bv-form-btn-label-control.js +++ b/src/components/form-btn-label-control/bv-form-btn-label-control.js @@ -1,7 +1,7 @@ // // Private component used by `b-form-datepicker` and `b-form-timepicker` // -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_BUTTON_LABEL_CONTROL } from '../../constants/components' import { PROP_TYPE_ARRAY_OBJECT_STRING, @@ -54,7 +54,7 @@ export const props = sortKeys({ // --- Main component --- // @vue/component -export const BVFormBtnLabelControl = /*#__PURE__*/ Vue.extend({ +export const BVFormBtnLabelControl = /*#__PURE__*/ extend({ name: NAME_FORM_BUTTON_LABEL_CONTROL, directives: { 'b-hover': VBHover diff --git a/src/components/form-checkbox/form-checkbox-group.js b/src/components/form-checkbox/form-checkbox-group.js index 10b6dc24a32..50e996ba8f3 100644 --- a/src/components/form-checkbox/form-checkbox-group.js +++ b/src/components/form-checkbox/form-checkbox-group.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_CHECKBOX_GROUP } from '../../constants/components' import { PROP_TYPE_ARRAY, PROP_TYPE_BOOLEAN } from '../../constants/props' import { sortKeys } from '../../utils/object' @@ -24,13 +24,13 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormCheckboxGroup = /*#__PURE__*/ Vue.extend({ +export const BFormCheckboxGroup = /*#__PURE__*/ extend({ name: NAME_FORM_CHECKBOX_GROUP, // Includes render function mixins: [formRadioCheckGroupMixin], provide() { return { - bvCheckGroup: this + getBvCheckGroup: () => this } }, props, diff --git a/src/components/form-checkbox/form-checkbox-group.spec.js b/src/components/form-checkbox/form-checkbox-group.spec.js index 4ac74dd27d7..4c3f3c23bbb 100644 --- a/src/components/form-checkbox/form-checkbox-group.spec.js +++ b/src/components/form-checkbox/form-checkbox-group.spec.js @@ -370,7 +370,7 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) wrapper.destroy() }) @@ -389,7 +389,7 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual([]) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).attributes('disabled')).toBeUndefined() expect($inputs.at(1).attributes('disabled')).toBeUndefined() expect($inputs.at(2).attributes('disabled')).toBeDefined() @@ -460,7 +460,7 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual(value) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).element.checked).toBe(true) expect($inputs.at(1).element.checked).toBe(true) expect($inputs.at(2).element.checked).toBe(true) @@ -472,7 +472,7 @@ describe('form-checkbox-group', () => { await waitNT(wrapper.vm) expect(wrapper.vm.localChecked).toEqual(value) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).element.checked).toBe(true) expect($inputs.at(1).element.checked).toBe(true) expect($inputs.at(2).element.checked).toBe(true) @@ -484,7 +484,7 @@ describe('form-checkbox-group', () => { await waitNT(wrapper.vm) expect(wrapper.vm.localChecked).toEqual(value.slice().reverse()) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).element.checked).toBe(true) expect($inputs.at(1).element.checked).toBe(true) expect($inputs.at(2).element.checked).toBe(true) @@ -509,14 +509,14 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual(['two']) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).element.checked).toBe(false) expect($inputs.at(1).element.checked).toBe(true) expect($inputs.at(2).element.checked).toBe(false) await wrapper.setProps({ checked: ['three', 'one'] }) expect(wrapper.vm.localChecked).toEqual(['three', 'one']) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) expect($inputs.at(0).element.checked).toBe(true) expect($inputs.at(1).element.checked).toBe(false) expect($inputs.at(2).element.checked).toBe(true) @@ -539,8 +539,8 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual([]) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) - expect($inputs.wrappers.every(c => c.find('input.is-valid').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input.is-valid'))).toBe(true) wrapper.destroy() }) @@ -558,8 +558,8 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual([]) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) - expect($inputs.wrappers.every(c => c.find('input.is-invalid').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input.is-invalid'))).toBe(true) wrapper.destroy() }) @@ -577,8 +577,8 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual([]) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) - expect($inputs.wrappers.every(c => c.find('input[disabled]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[disabled]'))).toBe(true) wrapper.destroy() }) @@ -597,9 +597,9 @@ describe('form-checkbox-group', () => { const $inputs = wrapper.findAll('input') expect($inputs.length).toBe(3) expect(wrapper.vm.localChecked).toEqual([]) - expect($inputs.wrappers.every(c => c.find('input[type=checkbox]').exists())).toBe(true) - expect($inputs.wrappers.every(c => c.find('input[required]').exists())).toBe(true) - expect($inputs.wrappers.every(c => c.find('input[aria-required="true"]').exists())).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[type=checkbox]'))).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[required]'))).toBe(true) + expect($inputs.wrappers.every(c => c.element.matches('input[aria-required="true"]'))).toBe(true) wrapper.destroy() }) diff --git a/src/components/form-checkbox/form-checkbox.js b/src/components/form-checkbox/form-checkbox.js index a9dc68f7ef4..b2bfba2660d 100644 --- a/src/components/form-checkbox/form-checkbox.js +++ b/src/components/form-checkbox/form-checkbox.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_CHECKBOX } from '../../constants/components' import { EVENT_NAME_CHANGE, MODEL_EVENT_NAME_PREFIX } from '../../constants/events' import { PROP_TYPE_ANY, PROP_TYPE_BOOLEAN } from '../../constants/props' @@ -37,17 +37,20 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormCheckbox = /*#__PURE__*/ Vue.extend({ +export const BFormCheckbox = /*#__PURE__*/ extend({ name: NAME_FORM_CHECKBOX, mixins: [formRadioCheckMixin], inject: { - bvGroup: { - from: 'bvCheckGroup', - default: null + getBvGroup: { + from: 'getBvCheckGroup', + default: () => () => null } }, props, computed: { + bvGroup() { + return this.getBvGroup() + }, isChecked() { const { value, computedLocalChecked: checked } = this return isArray(checked) ? looseIndexOf(checked, value) > -1 : looseEqual(checked, value) diff --git a/src/components/form-datepicker/form-datepicker.js b/src/components/form-datepicker/form-datepicker.js index 942bd87bf82..197fb14c5d0 100644 --- a/src/components/form-datepicker/form-datepicker.js +++ b/src/components/form-datepicker/form-datepicker.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_DATEPICKER } from '../../constants/components' import { EVENT_NAME_CONTEXT, EVENT_NAME_HIDDEN, EVENT_NAME_SHOWN } from '../../constants/events' import { PROP_TYPE_BOOLEAN, PROP_TYPE_DATE_STRING, PROP_TYPE_STRING } from '../../constants/props' @@ -75,7 +75,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormDatepicker = /*#__PURE__*/ Vue.extend({ +export const BFormDatepicker = /*#__PURE__*/ extend({ name: NAME_FORM_DATEPICKER, mixins: [idMixin, modelMixin], props, diff --git a/src/components/form-file/form-file.js b/src/components/form-file/form-file.js index 82ed44cdb9b..0820e7f2a1c 100644 --- a/src/components/form-file/form-file.js +++ b/src/components/form-file/form-file.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_FILE } from '../../constants/components' import { HAS_PROMISE_SUPPORT } from '../../constants/env' import { EVENT_NAME_CHANGE, EVENT_OPTIONS_PASSIVE } from '../../constants/events' @@ -176,7 +176,7 @@ const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormFile = /*#__PURE__*/ Vue.extend({ +export const BFormFile = /*#__PURE__*/ extend({ name: NAME_FORM_FILE, mixins: [ attrsMixin, diff --git a/src/components/form-group/form-group.js b/src/components/form-group/form-group.js index ed16deccf76..e00f92da44b 100644 --- a/src/components/form-group/form-group.js +++ b/src/components/form-group/form-group.js @@ -85,7 +85,7 @@ export const generateProps = () => // --- Main component --- -// We do not use `Vue.extend()` here as that would evaluate the props +// We do not use `extend()` here as that would evaluate the props // immediately, which we do not want to happen // @vue/component export const BFormGroup = { diff --git a/src/components/form-input/form-input.js b/src/components/form-input/form-input.js index 853e1ae758d..10243ac83a0 100644 --- a/src/components/form-input/form-input.js +++ b/src/components/form-input/form-input.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_INPUT } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props' import { arrayIncludes } from '../../utils/array' @@ -61,7 +61,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormInput = /*#__PURE__*/ Vue.extend({ +export const BFormInput = /*#__PURE__*/ extend({ name: NAME_FORM_INPUT, // Mixin order is important! mixins: [ diff --git a/src/components/form-radio/form-radio-group.js b/src/components/form-radio/form-radio-group.js index 2c8d434c0cc..d7745bbc256 100644 --- a/src/components/form-radio/form-radio-group.js +++ b/src/components/form-radio/form-radio-group.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_RADIO_GROUP } from '../../constants/components' import { makePropsConfigurable } from '../../utils/props' import { @@ -13,12 +13,12 @@ export const props = makePropsConfigurable(formRadioCheckGroupProps, NAME_FORM_R // --- Main component --- // @vue/component -export const BFormRadioGroup = /*#__PURE__*/ Vue.extend({ +export const BFormRadioGroup = /*#__PURE__*/ extend({ name: NAME_FORM_RADIO_GROUP, mixins: [formRadioCheckGroupMixin], provide() { return { - bvRadioGroup: this + getBvRadioGroup: () => this } }, props, diff --git a/src/components/form-radio/form-radio-group.spec.js b/src/components/form-radio/form-radio-group.spec.js index 6e5f3f070ba..74421829129 100644 --- a/src/components/form-radio/form-radio-group.spec.js +++ b/src/components/form-radio/form-radio-group.spec.js @@ -348,7 +348,7 @@ describe('form-radio-group', () => { const radios = wrapper.findAll('input') expect(radios.length).toBe(3) - expect(radios.wrappers.every(c => c.find('input[type=radio]').exists())).toBe(true) + expect(radios.wrappers.every(c => c.element.matches('input[type=radio]'))).toBe(true) wrapper.destroy() }) @@ -365,7 +365,7 @@ describe('form-radio-group', () => { const radios = wrapper.findAll('input') expect(radios.length).toBe(3) expect(wrapper.vm.localChecked).toEqual('') - expect(radios.wrappers.every(c => c.find('input[type=radio]').exists())).toBe(true) + expect(radios.wrappers.every(c => c.element.matches('input[type=radio]'))).toBe(true) expect(radios.at(0).attributes('disabled')).toBeUndefined() expect(radios.at(1).attributes('disabled')).toBeUndefined() expect(radios.at(2).attributes('disabled')).toBeDefined() diff --git a/src/components/form-radio/form-radio.js b/src/components/form-radio/form-radio.js index 27577d12b09..02b75dd9d10 100644 --- a/src/components/form-radio/form-radio.js +++ b/src/components/form-radio/form-radio.js @@ -1,12 +1,7 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_RADIO } from '../../constants/components' -import { looseEqual } from '../../utils/loose-equal' import { makePropsConfigurable } from '../../utils/props' -import { - MODEL_EVENT_NAME, - formRadioCheckMixin, - props as formRadioCheckProps -} from '../../mixins/form-radio-check' +import { formRadioCheckMixin, props as formRadioCheckProps } from '../../mixins/form-radio-check' // --- Props --- @@ -15,21 +10,19 @@ export const props = makePropsConfigurable(formRadioCheckProps, NAME_FORM_RADIO) // --- Main component --- // @vue/component -export const BFormRadio = /*#__PURE__*/ Vue.extend({ +export const BFormRadio = /*#__PURE__*/ extend({ name: NAME_FORM_RADIO, mixins: [formRadioCheckMixin], inject: { - bvGroup: { - from: 'bvRadioGroup', - default: false + getBvGroup: { + from: 'getBvRadioGroup', + default: () => () => null } }, props, - watch: { - computedLocalChecked(newValue, oldValue) { - if (!looseEqual(newValue, oldValue)) { - this.$emit(MODEL_EVENT_NAME, newValue) - } + computed: { + bvGroup() { + return this.getBvGroup() } } }) diff --git a/src/components/form-rating/form-rating.js b/src/components/form-rating/form-rating.js index 97953c45455..964ca44ed97 100644 --- a/src/components/form-rating/form-rating.js +++ b/src/components/form-rating/form-rating.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_RATING, NAME_FORM_RATING_STAR } from '../../constants/components' import { EVENT_NAME_CHANGE, EVENT_NAME_SELECTED } from '../../constants/events' import { @@ -58,7 +58,7 @@ const clampValue = (value, min, max) => mathMax(mathMin(value, max), min) // --- Helper components --- // @vue/component -const BVFormRatingStar = Vue.extend({ +const BVFormRatingStar = extend({ name: NAME_FORM_RATING_STAR, mixins: [normalizeSlotMixin], props: { @@ -140,7 +140,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormRating = /*#__PURE__*/ Vue.extend({ +export const BFormRating = /*#__PURE__*/ extend({ name: NAME_FORM_RATING, components: { BIconStar, BIconStarHalf, BIconStarFill, BIconX }, mixins: [idMixin, modelMixin, formSizeMixin], diff --git a/src/components/form-rating/form-rating.spec.js b/src/components/form-rating/form-rating.spec.js index 197a38e1ada..6a30c3e55ef 100644 --- a/src/components/form-rating/form-rating.spec.js +++ b/src/components/form-rating/form-rating.spec.js @@ -66,8 +66,8 @@ describe('form-rating', () => { const $icons = wrapper.findAll('.b-icon') expect($icons.length).toBe(5) - expect($icons.wrappers.every(i => i.find('.bi-star').exists())).toBe(true) - expect($icons.wrappers.every(i => i.find('.text-primary').exists())).toBe(true) + expect($icons.wrappers.every(i => i.element.matches('.bi-star'))).toBe(true) + expect($icons.wrappers.every(i => i.element.matches('.text-primary'))).toBe(true) expect($icons.wrappers.every(i => i.find('.text-warning').exists())).toBe(false) wrapper.destroy() @@ -322,18 +322,8 @@ describe('form-rating', () => { const $stars = wrapper.findAll('.b-rating-star') // The clear button is a "star" expect($stars.length).toBe(6) - expect( - $stars - .at(0) - .find('.b-rating-star-clear') - .exists() - ).toBe(true) - expect( - $stars - .at(1) - .find('.b-rating-star-clear') - .exists() - ).toBe(false) + expect($stars.at(0).element.matches('.b-rating-star-clear')).toBe(true) + expect($stars.at(1).element.matches('.b-rating-star-clear')).toBe(false) const $clear = wrapper.find('.b-rating-star-clear') expect($clear.exists()).toBe(true) diff --git a/src/components/form-select/form-select-option-group.js b/src/components/form-select/form-select-option-group.js index 546de2749ed..eb18bc7c60e 100644 --- a/src/components/form-select/form-select-option-group.js +++ b/src/components/form-select/form-select-option-group.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_SELECT_OPTION_GROUP } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_FIRST } from '../../constants/slots' @@ -22,7 +22,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormSelectOptionGroup = /*#__PURE__*/ Vue.extend({ +export const BFormSelectOptionGroup = /*#__PURE__*/ extend({ name: NAME_FORM_SELECT_OPTION_GROUP, mixins: [normalizeSlotMixin, formOptionsMixin], props, diff --git a/src/components/form-select/form-select-option-group.spec.js b/src/components/form-select/form-select-option-group.spec.js index f4368efaeba..0212f845c32 100644 --- a/src/components/form-select/form-select-option-group.spec.js +++ b/src/components/form-select/form-select-option-group.spec.js @@ -70,24 +70,9 @@ describe('form-select-option-group', () => { expect($options.at(0).attributes('value')).toBe('1') expect($options.at(1).attributes('value')).toBe('2') expect($options.at(2).attributes('value')).toBe('3') - expect( - $options - .at(0) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(1) - .find('[disabled]') - .exists() - ).toBe(true) - expect( - $options - .at(2) - .find('[disabled]') - .exists() - ).toBe(false) + expect($options.at(0).element.matches('[disabled]')).toBe(false) + expect($options.at(1).element.matches('[disabled]')).toBe(true) + expect($options.at(2).element.matches('[disabled]')).toBe(false) wrapper.destroy() }) diff --git a/src/components/form-select/form-select-option.js b/src/components/form-select/form-select-option.js index eece9980add..9a012b65bd3 100644 --- a/src/components/form-select/form-select-option.js +++ b/src/components/form-select/form-select-option.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM_SELECT_OPTION } from '../../constants/components' import { PROP_TYPE_ANY, PROP_TYPE_BOOLEAN } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -16,7 +16,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormSelectOption = /*#__PURE__*/ Vue.extend({ +export const BFormSelectOption = /*#__PURE__*/ extend({ name: NAME_FORM_SELECT_OPTION, functional: true, props, diff --git a/src/components/form-select/form-select.js b/src/components/form-select/form-select.js index 63b4cde5665..00dbb071adf 100644 --- a/src/components/form-select/form-select.js +++ b/src/components/form-select/form-select.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_SELECT } from '../../constants/components' import { EVENT_NAME_CHANGE } from '../../constants/events' import { @@ -51,7 +51,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormSelect = /*#__PURE__*/ Vue.extend({ +export const BFormSelect = /*#__PURE__*/ extend({ name: NAME_FORM_SELECT, mixins: [ idMixin, diff --git a/src/components/form-select/form-select.spec.js b/src/components/form-select/form-select.spec.js index cb730eb98bc..0fa4d711e6a 100644 --- a/src/components/form-select/form-select.spec.js +++ b/src/components/form-select/form-select.spec.js @@ -336,24 +336,9 @@ describe('form-select', () => { expect($options.at(0).attributes('value')).toBe('1') expect($options.at(1).attributes('value')).toBe('2') expect($options.at(2).attributes('value')).toBe('3') - expect( - $options - .at(0) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(1) - .find('[disabled]') - .exists() - ).toBe(true) - expect( - $options - .at(2) - .find('[disabled]') - .exists() - ).toBe(false) + expect($options.at(0).element.matches('[disabled]')).toBe(false) + expect($options.at(1).element.matches('[disabled]')).toBe(true) + expect($options.at(2).element.matches('[disabled]')).toBe(false) wrapper.destroy() }) @@ -402,24 +387,9 @@ describe('form-select', () => { expect($options.at(0).attributes('value')).toBe('1.5') expect($options.at(1).attributes('value')).toBe('5') expect($options.at(2).attributes('value')).toBe('50.75') - expect( - $options - .at(0) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(1) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(2) - .find('[disabled]') - .exists() - ).toBe(true) + expect($options.at(0).element.matches('[disabled]')).toBe(false) + expect($options.at(1).element.matches('[disabled]')).toBe(false) + expect($options.at(2).element.matches('[disabled]')).toBe(true) wrapper.destroy() }) @@ -457,30 +427,10 @@ describe('form-select', () => { expect($options.at(1).attributes('value')).toBe('2') expect($options.at(2).attributes('value')).toBe('3') expect($options.at(3).attributes('value')).toBe('4') - expect( - $options - .at(0) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(1) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(2) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(3) - .find('[disabled]') - .exists() - ).toBe(true) + expect($options.at(0).element.matches('[disabled]')).toBe(false) + expect($options.at(1).element.matches('[disabled]')).toBe(false) + expect($options.at(2).element.matches('[disabled]')).toBe(false) + expect($options.at(3).element.matches('[disabled]')).toBe(true) wrapper.destroy() }) @@ -514,30 +464,10 @@ describe('form-select', () => { expect($options.at(1).attributes('value')).toBe('2') expect($options.at(2).attributes('value')).toBe('3') expect($options.at(3).attributes('value')).toBe('4') - expect( - $options - .at(0) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(1) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(2) - .find('[disabled]') - .exists() - ).toBe(false) - expect( - $options - .at(3) - .find('[disabled]') - .exists() - ).toBe(true) + expect($options.at(0).element.matches('[disabled]')).toBe(false) + expect($options.at(1).element.matches('[disabled]')).toBe(false) + expect($options.at(2).element.matches('[disabled]')).toBe(false) + expect($options.at(3).element.matches('[disabled]')).toBe(true) wrapper.destroy() }) diff --git a/src/components/form-select/helpers/mixin-options.js b/src/components/form-select/helpers/mixin-options.js index b623c161dee..4d5a7d26fcc 100644 --- a/src/components/form-select/helpers/mixin-options.js +++ b/src/components/form-select/helpers/mixin-options.js @@ -1,4 +1,4 @@ -import { Vue } from '../../../vue' +import { extend } from '../../../vue' import { PROP_TYPE_STRING } from '../../../constants/props' import { get } from '../../../utils/get' import { isNull, isPlainObject, isUndefined } from '../../../utils/inspect' @@ -20,7 +20,7 @@ export const props = makePropsConfigurable( // --- Mixin --- // @vue/component -export const optionsMixin = Vue.extend({ +export const optionsMixin = extend({ mixins: [formOptionsMixin], props, methods: { diff --git a/src/components/form-spinbutton/form-spinbutton.js b/src/components/form-spinbutton/form-spinbutton.js index 50318e36b8b..25c148b01f9 100644 --- a/src/components/form-spinbutton/form-spinbutton.js +++ b/src/components/form-spinbutton/form-spinbutton.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_SPINBUTTON } from '../../constants/components' import { EVENT_NAME_CHANGE } from '../../constants/events' import { @@ -100,7 +100,7 @@ export const props = makePropsConfigurable( // --- Main Component --- // @vue/component -export const BFormSpinbutton = /*#__PURE__*/ Vue.extend({ +export const BFormSpinbutton = /*#__PURE__*/ extend({ name: NAME_FORM_SPINBUTTON, // Mixin order is important! mixins: [attrsMixin, idMixin, modelMixin, formSizeMixin, formStateMixin, normalizeSlotMixin], @@ -113,6 +113,9 @@ export const BFormSpinbutton = /*#__PURE__*/ Vue.extend({ } }, computed: { + required() { + return false + }, spinId() { return this.safeId() }, diff --git a/src/components/form-tags/form-tag.js b/src/components/form-tags/form-tag.js index f1a8d69d437..37f1c8686a7 100644 --- a/src/components/form-tags/form-tag.js +++ b/src/components/form-tags/form-tag.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_TAG } from '../../constants/components' import { EVENT_NAME_REMOVE } from '../../constants/events' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' @@ -29,7 +29,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormTag = /*#__PURE__*/ Vue.extend({ +export const BFormTag = /*#__PURE__*/ extend({ name: NAME_FORM_TAG, mixins: [idMixin, normalizeSlotMixin], props, diff --git a/src/components/form-tags/form-tags.js b/src/components/form-tags/form-tags.js index ad15e7067d7..d1028122b48 100644 --- a/src/components/form-tags/form-tags.js +++ b/src/components/form-tags/form-tags.js @@ -1,6 +1,6 @@ // Tagged input form control // Based loosely on https://adamwathan.me/renderless-components-in-vuejs/ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_TAGS } from '../../constants/components' import { EVENT_NAME_BLUR, @@ -141,7 +141,7 @@ const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormTags = /*#__PURE__*/ Vue.extend({ +export const BFormTags = /*#__PURE__*/ extend({ name: NAME_FORM_TAGS, mixins: [ listenersMixin, diff --git a/src/components/form-textarea/form-textarea.js b/src/components/form-textarea/form-textarea.js index 7117ec273a9..fa64a4ef69a 100644 --- a/src/components/form-textarea/form-textarea.js +++ b/src/components/form-textarea/form-textarea.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_TEXTAREA } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props' import { getCS, getStyle, isVisible, requestAF, setStyle } from '../../utils/dom' @@ -43,7 +43,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormTextarea = /*#__PURE__*/ Vue.extend({ +export const BFormTextarea = /*#__PURE__*/ extend({ name: NAME_FORM_TEXTAREA, directives: { 'b-visible': VBVisible @@ -67,6 +67,9 @@ export const BFormTextarea = /*#__PURE__*/ Vue.extend({ } }, computed: { + type() { + return null + }, computedStyle() { const styles = { // Setting `noResize` to true will disable the ability for the user to diff --git a/src/components/form-timepicker/form-timepicker.js b/src/components/form-timepicker/form-timepicker.js index d8f6ed6963e..830110fa067 100644 --- a/src/components/form-timepicker/form-timepicker.js +++ b/src/components/form-timepicker/form-timepicker.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_TIMEPICKER } from '../../constants/components' import { EVENT_NAME_CONTEXT, EVENT_NAME_SHOWN, EVENT_NAME_HIDDEN } from '../../constants/events' import { PROP_TYPE_BOOLEAN, PROP_TYPE_DATE_STRING, PROP_TYPE_STRING } from '../../constants/props' @@ -64,7 +64,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormTimepicker = /*#__PURE__*/ Vue.extend({ +export const BFormTimepicker = /*#__PURE__*/ extend({ name: NAME_FORM_TIMEPICKER, mixins: [idMixin, modelMixin], props, diff --git a/src/components/form/form-datalist.js b/src/components/form/form-datalist.js index d371c825d39..7793b82b120 100644 --- a/src/components/form/form-datalist.js +++ b/src/components/form/form-datalist.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_FORM_DATALIST } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { htmlOrText } from '../../utils/html' @@ -20,7 +20,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormDatalist = /*#__PURE__*/ Vue.extend({ +export const BFormDatalist = /*#__PURE__*/ extend({ name: NAME_FORM_DATALIST, mixins: [formOptionsMixin, normalizeSlotMixin], props, diff --git a/src/components/form/form-invalid-feedback.js b/src/components/form/form-invalid-feedback.js index 8292d9154db..0cbefb74330 100644 --- a/src/components/form/form-invalid-feedback.js +++ b/src/components/form/form-invalid-feedback.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM_INVALID_FEEDBACK } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -22,7 +22,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormInvalidFeedback = /*#__PURE__*/ Vue.extend({ +export const BFormInvalidFeedback = /*#__PURE__*/ extend({ name: NAME_FORM_INVALID_FEEDBACK, functional: true, props, diff --git a/src/components/form/form-text.js b/src/components/form/form-text.js index 0d92d8e58e6..82505040330 100644 --- a/src/components/form/form-text.js +++ b/src/components/form/form-text.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM_TEXT } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -18,7 +18,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormText = /*#__PURE__*/ Vue.extend({ +export const BFormText = /*#__PURE__*/ extend({ name: NAME_FORM_TEXT, functional: true, props, diff --git a/src/components/form/form-valid-feedback.js b/src/components/form/form-valid-feedback.js index 40453eee9f1..c20316ebc6a 100644 --- a/src/components/form/form-valid-feedback.js +++ b/src/components/form/form-valid-feedback.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM_VALID_FEEDBACK } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -22,7 +22,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormValidFeedback = /*#__PURE__*/ Vue.extend({ +export const BFormValidFeedback = /*#__PURE__*/ extend({ name: NAME_FORM_VALID_FEEDBACK, functional: true, props, diff --git a/src/components/form/form.js b/src/components/form/form.js index bc8daa42fdd..4c80769ac8d 100644 --- a/src/components/form/form.js +++ b/src/components/form/form.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -18,7 +18,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BForm = /*#__PURE__*/ Vue.extend({ +export const BForm = /*#__PURE__*/ extend({ name: NAME_FORM, functional: true, props, diff --git a/src/components/image/img-lazy.js b/src/components/image/img-lazy.js index 6aace5dd50d..e705906ae27 100644 --- a/src/components/image/img-lazy.js +++ b/src/components/image/img-lazy.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_IMG_LAZY } from '../../constants/components' import { HAS_INTERACTION_OBSERVER_SUPPORT } from '../../constants/env' import { MODEL_EVENT_NAME_PREFIX } from '../../constants/events' @@ -39,7 +39,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BImgLazy = /*#__PURE__*/ Vue.extend({ +export const BImgLazy = /*#__PURE__*/ extend({ name: NAME_IMG_LAZY, directives: { 'b-visible': VBVisible @@ -104,7 +104,9 @@ export const BImgLazy = /*#__PURE__*/ Vue.extend({ }, mounted() { // If `IntersectionObserver` is not available, image is always shown - this.isShown = HAS_INTERACTION_OBSERVER_SUPPORT ? this[MODEL_PROP_NAME_SHOW] : true + this.$nextTick(() => { + this.isShown = HAS_INTERACTION_OBSERVER_SUPPORT ? this[MODEL_PROP_NAME_SHOW] : true + }) }, methods: { updateShowProp() { diff --git a/src/components/image/img.js b/src/components/image/img.js index a55fea50bfc..6c585b02ea2 100644 --- a/src/components/image/img.js +++ b/src/components/image/img.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_IMG } from '../../constants/components' import { PROP_TYPE_ARRAY_STRING, @@ -72,7 +72,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BImg = /*#__PURE__*/ Vue.extend({ +export const BImg = /*#__PURE__*/ extend({ name: NAME_IMG, functional: true, props, diff --git a/src/components/input-group/input-group-addon.js b/src/components/input-group/input-group-addon.js index 4b5a8ca8af9..092e3348b80 100644 --- a/src/components/input-group/input-group-addon.js +++ b/src/components/input-group/input-group-addon.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_INPUT_GROUP_ADDON } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -19,7 +19,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BInputGroupAddon = /*#__PURE__*/ Vue.extend({ +export const BInputGroupAddon = /*#__PURE__*/ extend({ name: NAME_INPUT_GROUP_ADDON, functional: true, props, diff --git a/src/components/input-group/input-group-append.js b/src/components/input-group/input-group-append.js index fa5a9b22eb7..cb02f970925 100644 --- a/src/components/input-group/input-group-append.js +++ b/src/components/input-group/input-group-append.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_INPUT_GROUP_APPEND } from '../../constants/components' import { omit } from '../../utils/object' import { makePropsConfigurable } from '../../utils/props' @@ -14,7 +14,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BInputGroupAppend = /*#__PURE__*/ Vue.extend({ +export const BInputGroupAppend = /*#__PURE__*/ extend({ name: NAME_INPUT_GROUP_APPEND, functional: true, props, diff --git a/src/components/input-group/input-group-prepend.js b/src/components/input-group/input-group-prepend.js index 25f3f8294f3..ce63d01c7d4 100644 --- a/src/components/input-group/input-group-prepend.js +++ b/src/components/input-group/input-group-prepend.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_INPUT_GROUP_PREPEND } from '../../constants/components' import { omit } from '../../utils/object' import { makePropsConfigurable } from '../../utils/props' @@ -14,7 +14,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BInputGroupPrepend = /*#__PURE__*/ Vue.extend({ +export const BInputGroupPrepend = /*#__PURE__*/ extend({ name: NAME_INPUT_GROUP_PREPEND, functional: true, props, diff --git a/src/components/input-group/input-group-text.js b/src/components/input-group/input-group-text.js index cfe4ac4d3fa..69f35271cb8 100644 --- a/src/components/input-group/input-group-text.js +++ b/src/components/input-group/input-group-text.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_INPUT_GROUP_TEXT } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -15,7 +15,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BInputGroupText = /*#__PURE__*/ Vue.extend({ +export const BInputGroupText = /*#__PURE__*/ extend({ name: NAME_INPUT_GROUP_TEXT, functional: true, props, diff --git a/src/components/input-group/input-group.js b/src/components/input-group/input-group.js index ce2e8041ad6..c51855ff433 100644 --- a/src/components/input-group/input-group.js +++ b/src/components/input-group/input-group.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_INPUT_GROUP } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { SLOT_NAME_APPEND, SLOT_NAME_DEFAULT, SLOT_NAME_PREPEND } from '../../constants/slots' @@ -27,7 +27,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BInputGroup = /*#__PURE__*/ Vue.extend({ +export const BInputGroup = /*#__PURE__*/ extend({ name: NAME_INPUT_GROUP, functional: true, props, diff --git a/src/components/jumbotron/jumbotron.js b/src/components/jumbotron/jumbotron.js index cc5966ce371..d7715ecae15 100644 --- a/src/components/jumbotron/jumbotron.js +++ b/src/components/jumbotron/jumbotron.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_JUMBOTRON } from '../../constants/components' import { PROP_TYPE_BOOLEAN, @@ -36,7 +36,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BJumbotron = /*#__PURE__*/ Vue.extend({ +export const BJumbotron = /*#__PURE__*/ extend({ name: NAME_JUMBOTRON, functional: true, props, diff --git a/src/components/layout/col.js b/src/components/layout/col.js index df773d92a5a..2c48e655a65 100644 --- a/src/components/layout/col.js +++ b/src/components/layout/col.js @@ -106,7 +106,7 @@ export const generateProps = () => { // --- Main component --- -// We do not use Vue.extend here as that would evaluate the props +// We do not use extend here as that would evaluate the props // immediately, which we do not want to happen // @vue/component export const BCol = { diff --git a/src/components/layout/container.js b/src/components/layout/container.js index ec2c48781de..a9c78046abc 100644 --- a/src/components/layout/container.js +++ b/src/components/layout/container.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_CONTAINER } from '../../constants/components' import { PROP_TYPE_BOOLEAN_STRING, PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -17,7 +17,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BContainer = /*#__PURE__*/ Vue.extend({ +export const BContainer = /*#__PURE__*/ extend({ name: NAME_CONTAINER, functional: true, props, diff --git a/src/components/layout/form-row.js b/src/components/layout/form-row.js index 70d9af5cfc4..89a4a3533b1 100644 --- a/src/components/layout/form-row.js +++ b/src/components/layout/form-row.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_FORM_ROW } from '../../constants/components' import { PROP_TYPE_STRING } from '../../constants/props' import { makeProp, makePropsConfigurable } from '../../utils/props' @@ -15,7 +15,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BFormRow = /*#__PURE__*/ Vue.extend({ +export const BFormRow = /*#__PURE__*/ extend({ name: NAME_FORM_ROW, functional: true, props, diff --git a/src/components/layout/row.js b/src/components/layout/row.js index 4a1d6eb969f..8e9743c2668 100644 --- a/src/components/layout/row.js +++ b/src/components/layout/row.js @@ -65,7 +65,7 @@ export const generateProps = () => { // --- Main component --- -// We do not use `Vue.extend()` here as that would evaluate the props +// We do not use `extend()` here as that would evaluate the props // immediately, which we do not want to happen // @vue/component export const BRow = { diff --git a/src/components/link/link.js b/src/components/link/link.js index 4c026f5fed4..b0a7d38c737 100644 --- a/src/components/link/link.js +++ b/src/components/link/link.js @@ -1,4 +1,4 @@ -import { Vue } from '../../vue' +import { extend } from '../../vue' import { NAME_LINK } from '../../constants/components' import { EVENT_NAME_CLICK } from '../../constants/events' import { @@ -74,7 +74,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BLink = /*#__PURE__*/ Vue.extend({ +export const BLink = /*#__PURE__*/ extend({ name: NAME_LINK, // Mixin order is important! mixins: [attrsMixin, listenersMixin, listenOnRootMixin, normalizeSlotMixin], @@ -104,7 +104,10 @@ export const BLink = /*#__PURE__*/ Vue.extend({ return this.isRouterLink ? { ...pluckProps( - omit({ ...routerLinkProps, ...nuxtLinkProps }, ['event', 'prefetch', 'routerTag']), + omit( + { ...routerLinkProps, ...(this.computedTag === 'nuxt-link' ? nuxtLinkProps : {}) }, + ['event', 'prefetch', 'routerTag'] + ), this ), // Only add these props, when actually defined @@ -161,9 +164,11 @@ export const BLink = /*#__PURE__*/ Vue.extend({ } else { // Router links do not emit instance `click` events, so we // add in an `$emit('click', event)` on its Vue instance + // + // seems not to be required for Vue3 compat build /* istanbul ignore next: difficult to test, but we know it works */ - if (isRouterLink && event.currentTarget.__vue__) { - event.currentTarget.__vue__.$emit(EVENT_NAME_CLICK, event) + if (isRouterLink) { + event.currentTarget.__vue__?.$emit(EVENT_NAME_CLICK, event) } // Call the suppliedHandler(s), if any provided concat(suppliedHandler) diff --git a/src/components/list-group/list-group-item.js b/src/components/list-group/list-group-item.js index bf591af27b7..62fe63758c8 100644 --- a/src/components/list-group/list-group-item.js +++ b/src/components/list-group/list-group-item.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_LIST_GROUP_ITEM } from '../../constants/components' import { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../constants/props' import { arrayIncludes } from '../../utils/array' @@ -32,7 +32,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BListGroupItem = /*#__PURE__*/ Vue.extend({ +export const BListGroupItem = /*#__PURE__*/ extend({ name: NAME_LIST_GROUP_ITEM, functional: true, props, diff --git a/src/components/list-group/list-group.js b/src/components/list-group/list-group.js index 93ceda15779..986332a288d 100644 --- a/src/components/list-group/list-group.js +++ b/src/components/list-group/list-group.js @@ -1,4 +1,4 @@ -import { Vue, mergeData } from '../../vue' +import { extend, mergeData } from '../../vue' import { NAME_LIST_GROUP } from '../../constants/components' import { PROP_TYPE_BOOLEAN, @@ -22,7 +22,7 @@ export const props = makePropsConfigurable( // --- Main component --- // @vue/component -export const BListGroup = /*#__PURE__*/ Vue.extend({ +export const BListGroup = /*#__PURE__*/ extend({ name: NAME_LIST_GROUP, functional: true, props, diff --git a/src/components/media/README.md b/src/components/media/README.md index 5cacc78ff3b..0b32c4135c9 100644 --- a/src/components/media/README.md +++ b/src/components/media/README.md @@ -124,15 +124,15 @@ You can easily nest media objects by including another `` inside parent ## Vertical align -Aside can be vertically aligned using `vertical-align` prop, set to `top`, `center` or `end`. -The default alignment is `top`. +Aside can be vertically aligned using `vertical-align` prop, set to `top`, `center` or `end`. The +default alignment is `top`. ## Media list -Because the media object has few structural requirements, you can use this component as -a list item in HTML lists. On your `