Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(compat): add Vue 3 support via @vue/compat (fixes #5196) #6845

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e993c71
chore(compat): introduce Vue3 testing infrastructure
xanf Nov 17, 2021
eff66bf
chore(compat): replace providing components with getter functions
xanf Nov 6, 2021
60f3850
chore(compat): replace parent/root access with wrappers
xanf Nov 6, 2021
96e01ae
chore(compat): introduce vue3 compatibility wrapper
xanf Nov 17, 2021
d4eadd8
chore(compat): update attrs mixin for vue3
xanf Nov 17, 2021
ff6c300
chore(compat): implement component access from vnode for vue3
xanf Nov 9, 2021
6bdd078
chore(compat): update listeners mixin for vue3
xanf Nov 17, 2021
666308c
chore(compat): delay first attempt to show image for nextTick
xanf Nov 17, 2021
cdb7c9f
feature(vue3): replace transporter implementation with teleport
xanf Nov 17, 2021
7501419
chore(compat): disable tests related to has-listener in Vue 3
xanf Nov 17, 2021
1230750
chore(compat): skip tbody-transition tests in Vue 3
xanf Nov 17, 2021
0cd0532
chore(compat): fetch fresh template element when checking visibility
xanf Nov 17, 2021
3f90d99
chore(compat): disable subset of config specs due to localVue
xanf Nov 17, 2021
37ecf19
chore(compat): unify access to component instance from directive
xanf Nov 17, 2021
3372217
chore(compat): make tabs properly filter in Vue 3
xanf Nov 17, 2021
71b584e
chore(compat): silence most warning from Vue 3 compat build
xanf Nov 17, 2021
6e61190
chore: fix warnings about accessing undefined fields in render
xanf Nov 17, 2021
e3074e1
chore(compat): move tooltip init from created nextTick to mounted
xanf Nov 18, 2021
09fb229
chore(compat): add required compatConfig for tests
xanf Nov 20, 2021
5b246ce
chore(compat): add required compatConfig for components and mixins
xanf Nov 20, 2021
961951b
chore(compat): drop $children usage in tabs component
xanf Nov 20, 2021
9225db4
chore(compat): do not pass extra props in link if there is no nuxt
xanf Nov 20, 2021
92afaad
chore(compat): do not pass false value in BVTransition
xanf Nov 20, 2021
2d4bfe4
chore(compat): update hook event names for vue3 (now vnode events)
xanf Nov 20, 2021
f499476
chore(compat): remove this.$set usage in vue3 version of cache
xanf Nov 20, 2021
e4dd825
chore(compat): remove scopedSlots usage in test helper
xanf Nov 20, 2021
b583ed5
chore(compat): upgrade vue3 compatibility layer
xanf Nov 20, 2021
1e13325
chore(compat): improve tests setup compat for vue3
xanf Nov 20, 2021
24f4da1
chore(compat): do not pollute Vue.extend
xanf Nov 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions jest.config.js
@@ -1,10 +1,20 @@
const useVue2 = 'USE_VUE2' in process.env

const moduleNameMapper = useVue2
? {}
: {
'^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'
},
transformIgnorePatterns: ['/node_modules(?![\\\\/]vue-test-utils-compat[\\\\/])'],
coverageDirectory: './coverage/',
testEnvironmentOptions: {
pretendToBeVisual: true
Expand Down
7 changes: 5 additions & 2 deletions package.json
Expand Up @@ -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.24",
"@vue/compiler-dom": "^3.2.24",
"@vue/test-utils": "^1.3.0",
"@vue/test-utils-vue3": "npm:@vue/test-utils@^2.0.0",
"autoprefixer": "^10.2.5",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
Expand Down Expand Up @@ -148,10 +151,10 @@
"standard-version": "^9.3.0",
"terser": "^5.7.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.3"
},
"keywords": [
"Bootstrap",
Expand Down
8 changes: 6 additions & 2 deletions src/components/alert/alert.js
@@ -1,4 +1,4 @@
import { COMPONENT_UID_KEY, Vue } from '../../vue'
import { COMPONENT_UID_KEY, defineComponent } from '../../vue'
import { NAME_ALERT } from '../../constants/components'
import { EVENT_NAME_DISMISSED, EVENT_NAME_DISMISS_COUNT_DOWN } from '../../constants/events'
import {
Expand Down Expand Up @@ -68,8 +68,12 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BAlert = /*#__PURE__*/ Vue.extend({
export const BAlert = /*#__PURE__*/ defineComponent({
name: NAME_ALERT,
compatConfig: {
MODE: 3,
OPTIONS_BEFORE_DESTROY: 'suppress-warning'
},
mixins: [modelMixin, normalizeSlotMixin],
props,
data() {
Expand Down
4 changes: 2 additions & 2 deletions src/components/aspect/aspect.js
@@ -1,4 +1,4 @@
import { Vue } from '../../vue'
import { defineComponent } 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'
Expand Down Expand Up @@ -26,7 +26,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BAspect = /*#__PURE__*/ Vue.extend({
export const BAspect = /*#__PURE__*/ defineComponent({
name: NAME_ASPECT,
mixins: [normalizeSlotMixin],
props,
Expand Down
6 changes: 3 additions & 3 deletions src/components/avatar/avatar-group.js
@@ -1,4 +1,4 @@
import { Vue } from '../../vue'
import { defineComponent } from '../../vue'
import { NAME_AVATAR_GROUP } from '../../constants/components'
import {
PROP_TYPE_BOOLEAN,
Expand Down Expand Up @@ -33,11 +33,11 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BAvatarGroup = /*#__PURE__*/ Vue.extend({
export const BAvatarGroup = /*#__PURE__*/ defineComponent({
name: NAME_AVATAR_GROUP,
mixins: [normalizeSlotMixin],
provide() {
return { bvAvatarGroup: this }
return { getBvAvatarGroup: () => this }
},
props,
computed: {
Expand Down
9 changes: 6 additions & 3 deletions src/components/avatar/avatar.js
@@ -1,4 +1,4 @@
import { Vue } from '../../vue'
import { defineComponent } from '../../vue'
import { NAME_AVATAR } from '../../constants/components'
import { EVENT_NAME_CLICK, EVENT_NAME_IMG_ERROR } from '../../constants/events'
import {
Expand Down Expand Up @@ -67,11 +67,11 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BAvatar = /*#__PURE__*/ Vue.extend({
export const BAvatar = /*#__PURE__*/ defineComponent({
name: NAME_AVATAR,
mixins: [normalizeSlotMixin],
inject: {
bvAvatarGroup: { default: null }
getBvAvatarGroup: { default: () => () => null }
},
props,
data() {
Expand All @@ -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
Expand Down
12 changes: 6 additions & 6 deletions src/components/avatar/avatar.spec.js
Expand Up @@ -250,7 +250,7 @@ describe('avatar', () => {
const wrapper1 = mount(BAvatar, {
provide: {
// Emulate `undefined`/`null` props
bvAvatarGroup: {}
getBvAvatarGroup: () => ({})
}
})

Expand All @@ -265,9 +265,9 @@ describe('avatar', () => {

const wrapper2 = mount(BAvatar, {
provide: {
bvAvatarGroup: {
getBvAvatarGroup: () => ({
variant: 'danger'
}
})
}
})

Expand All @@ -289,7 +289,7 @@ describe('avatar', () => {
},
provide: {
// Emulate `undefined`/`null` props
bvAvatarGroup: {}
getBvAvatarGroup: () => ({})
}
})

Expand All @@ -307,9 +307,9 @@ describe('avatar', () => {
size: '2em'
},
provide: {
bvAvatarGroup: {
getBvAvatarGroup: () => ({
size: '5em'
}
})
}
})

Expand Down
4 changes: 2 additions & 2 deletions src/components/badge/badge.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -25,7 +25,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BBadge = /*#__PURE__*/ Vue.extend({
export const BBadge = /*#__PURE__*/ defineComponent({
name: NAME_BADGE,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/breadcrumb/breadcrumb-item.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, mergeData } from '../../vue'
import { NAME_BREADCRUMB_ITEM } from '../../constants/components'
import { makePropsConfigurable } from '../../utils/props'
import { BBreadcrumbLink, props as BBreadcrumbLinkProps } from './breadcrumb-link'
Expand All @@ -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__*/ defineComponent({
name: NAME_BREADCRUMB_ITEM,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/breadcrumb/breadcrumb-link.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, mergeData } from '../../vue'
import { NAME_BREADCRUMB_LINK } from '../../constants/components'
import { PROP_TYPE_STRING } from '../../constants/props'
import { htmlOrText } from '../../utils/html'
Expand All @@ -21,7 +21,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BBreadcrumbLink = /*#__PURE__*/ Vue.extend({
export const BBreadcrumbLink = /*#__PURE__*/ defineComponent({
name: NAME_BREADCRUMB_LINK,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/breadcrumb/breadcrumb.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, mergeData } from '../../vue'
import { NAME_BREADCRUMB } from '../../constants/components'
import { PROP_TYPE_ARRAY } from '../../constants/props'
import { isArray, isObject } from '../../utils/inspect'
Expand All @@ -18,7 +18,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BBreadcrumb = /*#__PURE__*/ Vue.extend({
export const BBreadcrumb = /*#__PURE__*/ defineComponent({
name: NAME_BREADCRUMB,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/button-group/button-group.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -21,7 +21,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BButtonGroup = /*#__PURE__*/ Vue.extend({
export const BButtonGroup = /*#__PURE__*/ defineComponent({
name: NAME_BUTTON_GROUP,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/button-toolbar/button-toolbar.js
@@ -1,4 +1,4 @@
import { Vue } from '../../vue'
import { defineComponent } 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'
Expand Down Expand Up @@ -30,7 +30,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BButtonToolbar = /*#__PURE__*/ Vue.extend({
export const BButtonToolbar = /*#__PURE__*/ defineComponent({
name: NAME_BUTTON_TOOLBAR,
mixins: [normalizeSlotMixin],
props,
Expand Down
5 changes: 5 additions & 0 deletions src/components/button-toolbar/button-toolbar.spec.js
Expand Up @@ -82,6 +82,11 @@ describe('button-toolbar', () => {

// Test App for keynav
const App = {
compatConfig: {
MODE: 3,
RENDER_FUNCTION: 'suppress-warning',
COMPONENT_FUNCTIONAL: 'suppress-warning'
},
render(h) {
return h(BButtonToolbar, { props: { keyNav: true } }, [
h(BButtonGroup, [h(BButton, 'a'), h(BButton, 'b')]),
Expand Down
8 changes: 6 additions & 2 deletions src/components/button/button-close.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -22,8 +22,12 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BButtonClose = /*#__PURE__*/ Vue.extend({
export const BButtonClose = /*#__PURE__*/ defineComponent({
name: NAME_BUTTON_CLOSE,
compatConfig: {
MODE: 3,
INSTANCE_SCOPED_SLOTS: 'suppress-warning'
},
functional: true,
props,
render(h, { props, data, slots, scopedSlots }) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/button/button.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand Down Expand Up @@ -115,7 +115,7 @@ const computeAttrs = (props, data) => {
// --- Main component ---

// @vue/component
export const BButton = /*#__PURE__*/ Vue.extend({
export const BButton = /*#__PURE__*/ defineComponent({
name: NAME_BUTTON,
functional: true,
props,
Expand Down
10 changes: 7 additions & 3 deletions src/components/calendar/calendar.js
@@ -1,4 +1,4 @@
import { Vue } from '../../vue'
import { defineComponent } from '../../vue'
import { NAME_CALENDAR } from '../../constants/components'
import {
CALENDAR_GREGORY,
Expand Down Expand Up @@ -180,8 +180,12 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BCalendar = Vue.extend({
export const BCalendar = defineComponent({
name: NAME_CALENDAR,
compatConfig: {
MODE: 3,
OPTIONS_BEFORE_DESTROY: 'suppress-warning'
},
// Mixin order is important!
mixins: [attrsMixin, idMixin, modelMixin, normalizeSlotMixin],
props,
Expand Down Expand Up @@ -811,7 +815,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]
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/card/card-body.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand Down Expand Up @@ -29,7 +29,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BCardBody = /*#__PURE__*/ Vue.extend({
export const BCardBody = /*#__PURE__*/ defineComponent({
name: NAME_CARD_BODY,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/card/card-footer.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -21,7 +21,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BCardFooter = /*#__PURE__*/ Vue.extend({
export const BCardFooter = /*#__PURE__*/ defineComponent({
name: NAME_CARD_FOOTER,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/card/card-group.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -17,7 +17,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BCardGroup = /*#__PURE__*/ Vue.extend({
export const BCardGroup = /*#__PURE__*/ defineComponent({
name: NAME_CARD_GROUP,
functional: true,
props,
Expand Down
4 changes: 2 additions & 2 deletions src/components/card/card-header.js
@@ -1,4 +1,4 @@
import { Vue, mergeData } from '../../vue'
import { defineComponent, 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'
Expand All @@ -21,7 +21,7 @@ export const props = makePropsConfigurable(
// --- Main component ---

// @vue/component
export const BCardHeader = /*#__PURE__*/ Vue.extend({
export const BCardHeader = /*#__PURE__*/ defineComponent({
name: NAME_CARD_HEADER,
functional: true,
props,
Expand Down