Skip to content

Commit 8a1f348

Browse files
authored
Merge pull request #240 from vuejs/class-component
Basic support for class component
2 parents ed07b50 + 1380114 commit 8a1f348

File tree

6 files changed

+66
-3
lines changed

6 files changed

+66
-3
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@
3333
"jsdom-global": "^3.0.2",
3434
"lint-staged": "^10.0.9",
3535
"prettier": "^2.0.2",
36+
"reflect-metadata": "^0.1.13",
3637
"rollup": "^1.31.1",
3738
"rollup-plugin-typescript2": "^0.26.0",
3839
"ts-jest": "25.2.1",
3940
"tsd": "0.11.0",
4041
"typescript": "^3.7.5",
4142
"vue": "^3.0.2",
43+
"vue-class-component": "^8.0.0-beta.4",
4244
"vue-jest": "^5.0.0-alpha.5",
4345
"vue-router": "^4.0.0-rc.1",
4446
"vuex": "^4.0.0-beta.4"

src/mount.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,19 @@ import {
2626

2727
import { config } from './config'
2828
import { GlobalMountOptions } from './types'
29-
import { mergeGlobalProperties } from './utils'
29+
import {
30+
isClassComponent,
31+
isFunctionalComponent,
32+
isObjectComponent,
33+
mergeGlobalProperties
34+
} from './utils'
3035
import { processSlot } from './utils/compileSlots'
3136
import { createWrapper, VueWrapper } from './vueWrapper'
3237
import { attachEmitListener } from './emitMixin'
3338
import { createDataMixin } from './dataMixin'
3439
import { MOUNT_COMPONENT_REF, MOUNT_PARENT_NAME } from './constants'
3540
import { stubComponents } from './stubs'
41+
import { VueConstructor } from 'vue-class-component'
3642

3743
// NOTE this should come from `vue`
3844
type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
@@ -67,6 +73,12 @@ export type ObjectEmitsOptions = Record<
6773
>
6874
export type EmitsOptions = ObjectEmitsOptions | string[]
6975

76+
// Class component
77+
export function mount(
78+
originalComponent: VueConstructor,
79+
options?: MountingOptions<any>
80+
): VueWrapper<ComponentPublicInstance<any>>
81+
7082
// Functional component with emits
7183
export function mount<Props, E extends EmitsOptions = {}>(
7284
originalComponent: FunctionalComponent<Props, E>,
@@ -228,7 +240,7 @@ export function mount(
228240

229241
const functionalComponentEmits: Record<string, unknown[]> = {}
230242

231-
if (typeof originalComponent === 'function') {
243+
if (isFunctionalComponent(originalComponent)) {
232244
// we need to wrap it like this so we can capture emitted events.
233245
// we capture events using a mixin that mutates `emit` in `beforeCreate`,
234246
// but functional components do not support mixins, so we need to wrap it
@@ -240,8 +252,10 @@ export function mount(
240252
)
241253
}
242254
})
243-
} else {
255+
} else if (isObjectComponent(originalComponent)) {
244256
component = { ...originalComponent }
257+
} else {
258+
component = originalComponent
245259
}
246260

247261
const el = document.createElement('div')

src/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,15 @@ export const mergeDeep = (
6464

6565
return target
6666
}
67+
68+
export function isClassComponent(component: any) {
69+
return '__vccBase' in component
70+
}
71+
72+
export function isFunctionalComponent(component: any) {
73+
return typeof component === 'function' && !isClassComponent(component)
74+
}
75+
76+
export function isObjectComponent(component: any) {
77+
return typeof component !== 'function' && !isClassComponent(component)
78+
}

tests/features/classComponent.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import 'reflect-metadata'
2+
import { h } from 'vue'
3+
import { Options, Vue } from 'vue-class-component'
4+
import { mount } from '../../src'
5+
6+
test('data: $props should be available', () => {
7+
@Options({
8+
props: ['foo']
9+
})
10+
class MyComp extends Vue {
11+
message = 'answer is ' + (this.$props as any).foo
12+
render() {
13+
return h('div', this.message)
14+
}
15+
}
16+
17+
const wrapper = mount(MyComp, {
18+
props: {
19+
foo: 42
20+
}
21+
})
22+
23+
expect(wrapper.html()).toBe('<div>answer is 42</div>')
24+
})

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"lib": ["DOM", "ES2015", "ES2017"],
77
"moduleResolution": "node",
88
"esModuleInterop": true,
9+
"experimentalDecorators": true,
910
"allowSyntheticDefaultImports": true,
1011
"baseUrl": ".",
1112
"paths": {

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5083,6 +5083,11 @@ redent@^2.0.0:
50835083
indent-string "^3.0.0"
50845084
strip-indent "^2.0.0"
50855085

5086+
reflect-metadata@^0.1.13:
5087+
version "0.1.13"
5088+
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
5089+
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
5090+
50865091
regenerate-unicode-properties@^8.2.0:
50875092
version "8.2.0"
50885093
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
@@ -6154,6 +6159,11 @@ verror@1.10.0:
61546159
core-util-is "1.0.2"
61556160
extsprintf "^1.2.0"
61566161

6162+
vue-class-component@^8.0.0-beta.4:
6163+
version "8.0.0-beta.4"
6164+
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-8.0.0-beta.4.tgz#bff95cdd44eb450a4a4e54b69da22099613d8071"
6165+
integrity sha512-+QXBhVH/Mz8dEC+IU7e8XXM54Tn0Aj9/saybeuK8XmhQiJlcijCB8kB7CYpBEMpHWaA+DoLr6LvHMbclYRCwZQ==
6166+
61576167
vue-jest@^5.0.0-alpha.5:
61586168
version "5.0.0-alpha.5"
61596169
resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-5.0.0-alpha.5.tgz#a24b7569ba2078836a316033f283e151afc4906b"

0 commit comments

Comments
 (0)