Skip to content

Commit

Permalink
fix: allow access to nested computed values (fix #2196) (#2356)
Browse files Browse the repository at this point in the history
* fix: allow access to nested computed values

* fix: remove useless access to vm
  • Loading branch information
Evobaso-J authored Mar 13, 2024
1 parent d767850 commit 6fbdfa1
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/createInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
defineComponent,
reactive,
shallowReactive,
isRef,
ref,
AppConfig,
ComponentOptions,
Expand All @@ -17,6 +16,7 @@ import { MountingOptions, Slot } from './types'
import {
getComponentsFromStubs,
getDirectivesFromStubs,
isDeepRef,
isFunctionalComponent,
isObject,
isObjectComponent,
Expand Down Expand Up @@ -174,7 +174,7 @@ export function createInstance(
...options?.props,
ref: MOUNT_COMPONENT_REF
}).forEach(([k, v]) => {
if (isRef(v)) {
if (isDeepRef(v)) {
refs[k] = v
} else {
props[k] = v
Expand Down
12 changes: 11 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
VNode,
VNodeProps,
FunctionalComponent,
ComponentInternalInstance
ComponentInternalInstance,
Ref
} from 'vue'

export interface RefSelector {
Expand Down Expand Up @@ -167,3 +168,12 @@ export type VueNode<T extends Node = Node> = T & {
export type VueElement = VueNode<Element>

export type DefinedComponent = new (...args: any[]) => any

/**
* T is a DeepRef if:
* - It's a Ref itself
* - It's an array containing a ref at any level
* - It's an object containing a ref at any level
*/
export type DeepRef<T> =
T extends Ref<T> ? T : T extends object ? DeepRef<T> : Ref<T>
17 changes: 15 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { GlobalMountOptions, RefSelector, Stub, Stubs } from './types'
import { DeepRef, GlobalMountOptions, RefSelector, Stub, Stubs } from './types'
import {
Component,
ComponentOptions,
ComponentPublicInstance,
ConcreteComponent,
Directive,
FunctionalComponent
FunctionalComponent,
isRef
} from 'vue'
import { config } from './config'

Expand Down Expand Up @@ -252,3 +253,15 @@ export const getGlobalThis = (): any => {
: {})
)
}

/**
* Checks if the given value is a DeepRef.
*
* For both arrays and objects, it will recursively check
* if any of their values is a Ref.
*
* @param {DeepRef<T> | unknown} r - The value to check.
* @returns {boolean} Returns true if the value is a DeepRef, false otherwise.
*/
export const isDeepRef = <T>(r: DeepRef<T> | unknown): r is DeepRef<T> =>
isRef(r) || (isObject(r) && Object.values(r).some(isDeepRef))
55 changes: 55 additions & 0 deletions tests/components/WithDeepRef.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div id="countValue">{{ countValue }}</div>
<div id="oneLayerCountObjectValue">{{ oneLayerCountObjectValue }}</div>
<div id="twoLayersCountObjectValue">{{ twoLayersCountObjectValue }}</div>
<div id="countArrayValue">{{ countArrayValue }}</div>
<div id="countMatrixValue">{{ countMatrixValue }}</div>
<div id="oneLayerCountObjectArrayValue">
{{ oneLayerCountObjectArrayValue }}
</div>
<div id="oneLayerCountArrayObjectValue">
{{ oneLayerCountArrayObjectValue }}
</div>
<div id="oneLayerCountObjectMatrixValue">
{{ oneLayerCountObjectMatrixValue }}
</div>
</template>

<script setup lang="ts">
import { computed, Ref } from 'vue'
type Props = {
count: Ref<number>
oneLayerCountObject: { count: Ref<number> }
twoLayersCountObject: { oneLayerCountObject: { count: Ref<number> } }
countArray: Ref<number>[]
countMatrix: Ref<number>[][]
oneLayerCountObjectArray: { count: Ref<number> }[]
oneLayerCountArrayObject: { count: Ref<number>[] }
oneLayerCountObjectMatrix: { count: Ref<number> }[][]
}
const props = defineProps<Props>()
const countValue = computed(() => props.count.value)
const oneLayerCountObjectValue = computed(
() => props.oneLayerCountObject.count.value
)
const twoLayersCountObjectValue = computed(
() => props.twoLayersCountObject.oneLayerCountObject.count.value
)
const countArrayValue = computed(() => props.countArray[0].value)
const countMatrixValue = computed(() => props.countMatrix[0][0].value)
const oneLayerCountObjectArrayValue = computed(
() => props.oneLayerCountObjectArray[0].count.value
)
const oneLayerCountArrayObjectValue = computed(
() => props.oneLayerCountArrayObject.count[0].value
)
const oneLayerCountObjectMatrixValue = computed(
() => props.oneLayerCountObjectMatrix[0][0].count.value
)
</script>
28 changes: 27 additions & 1 deletion tests/mount.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { describe, expect, it, vi } from 'vitest'
import { defineComponent } from 'vue'
import { defineComponent, ref } from 'vue'
import { mount } from '../src'
import DefinePropsAndDefineEmits from './components/DefinePropsAndDefineEmits.vue'
import WithDeepRef from './components/WithDeepRef.vue'
import HelloFromVitestPlayground from './components/HelloFromVitestPlayground.vue'

describe('mount: general tests', () => {
Expand Down Expand Up @@ -44,4 +45,29 @@ describe('mount: general tests', () => {
expect(wrapper.get('div').text()).toContain('Hello')
expect(wrapper.get('div').classes()).toContain('end')
})
it('allows access to nested computed values', async () => {
const wrapper = mount(WithDeepRef, {
props: {
count: ref(1),
oneLayerCountObject: { count: ref(2) },
twoLayersCountObject: { oneLayerCountObject: { count: ref(3) } },

countArray: [ref(4)],
countMatrix: [[ref(5)]],

oneLayerCountObjectArray: [{ count: ref(6) }],
oneLayerCountArrayObject: { count: [ref(7)] },
oneLayerCountObjectMatrix: [[{ count: ref(8) }]]
}
})

expect(wrapper.get('#countValue').text()).toBe('1')
expect(wrapper.get('#oneLayerCountObjectValue').text()).toBe('2')
expect(wrapper.get('#twoLayersCountObjectValue').text()).toBe('3')
expect(wrapper.get('#countArrayValue').text()).toBe('4')
expect(wrapper.get('#countMatrixValue').text()).toBe('5')
expect(wrapper.get('#oneLayerCountObjectArrayValue').text()).toBe('6')
expect(wrapper.get('#oneLayerCountArrayObjectValue').text()).toBe('7')
expect(wrapper.get('#oneLayerCountObjectMatrixValue').text()).toBe('8')
})
})

0 comments on commit 6fbdfa1

Please sign in to comment.