Skip to content

Commit

Permalink
Merge branch 'main' into fix/reactive-teleport
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/vnodeTransformers/util.ts
  • Loading branch information
freakzlike committed May 21, 2023
2 parents cc9639d + b94a0ae commit 4eb66e0
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Expand Up @@ -15,7 +15,7 @@ assignees: ''

**To Reproduce**
<!-- A link to a minimal reproduction (with the minimum code to reproduce the issue).
It takes just a few minutes to build a repro online with https://stackblitz.com/fork/github/vitest-dev/vitest/tree/main/examples/vue?initialPath=__vitest__
It takes just a few minutes to build a repro online with https://stackblitz.com/github/vuejs/create-vue-templates/tree/main/typescript-vitest?file=src%2Fcomponents%2F__tests__%2FHelloWorld.spec.ts
-->

**Expected behavior**
Expand Down
8 changes: 8 additions & 0 deletions docs/.vitepress/config.ts
Expand Up @@ -106,6 +106,10 @@ export default defineConfig({
}
]
},
{
text: 'FAQ',
link: '/guide/faq/'
},
{
text: 'Migrating from Vue 2',
link: '/migration/'
Expand Down Expand Up @@ -214,6 +218,10 @@ export default defineConfig({
}
]
},
{
text: 'FAQ',
link: '/guide/faq/'
},
{
text: 'Migrating from Vue 2',
link: '/migration/'
Expand Down
8 changes: 8 additions & 0 deletions docs/fr/.vitepress/locale-config.ts
Expand Up @@ -6,6 +6,10 @@ const frLocaleConfig: DefaultTheme.LocaleConfig & Omit<DefaultTheme.Config, "loc
nav: [
{ text: 'Guide', link: '/fr/guide/' },
{ text: 'API', link: '/fr/api/' },
{
text: 'FAQ',
link: '/fr/guide/faq/'
},
{ text: 'Migrer depuis Vue 2', link: '/fr/migration/' },
{
text: 'Journal de modifications',
Expand Down Expand Up @@ -86,6 +90,10 @@ const frLocaleConfig: DefaultTheme.LocaleConfig & Omit<DefaultTheme.Config, "loc
}
]
},
{
text: 'FAQ',
link: '/fr/guide/faq/'
},
{
text: 'Migrer depuis Vue 2',
link: '/fr/migration/'
Expand Down
25 changes: 25 additions & 0 deletions docs/fr/guide/faq/index.md
@@ -0,0 +1,25 @@
# FAQ

[[toc]]

## Vue warn: Failed setting prop

```
[Vue warn]: Failed setting prop "prefix" on <component-stub>: value foo is invalid.
TypeError: Cannot set property prefix of #<Element> which has only a getter
```

Cet avertissement est affiché si vous utilisez `shallowMount` ou `stubs` avec une propriété dont le nom est celui de l'une des propriétés de [`Element`](https://developer.mozilla.org/fr-FR/docs/Web/API/Element).

Parmi les noms de propriétés courants partagés avec `Element` figurent&nbsp;:
* `attributes`
* `children`
* `prefix`

Voir https://developer.mozilla.org/en-US/docs/Web/API/Element

**Solutions possibles**

1. Utilisez `mount` au lieu de `shallowMount` pour rendre le composant sans utiliser de `stubs`
2. Ignorez l'avertissement en utilisant un mock pour `console.warn`
3. Renommez la propriété du composant pour éviter les conflits avec les propriétés de `Element`
25 changes: 25 additions & 0 deletions docs/guide/faq/index.md
@@ -0,0 +1,25 @@
# FAQ

[[toc]]

## Vue warn: Failed setting prop

```
[Vue warn]: Failed setting prop "prefix" on <component-stub>: value foo is invalid.
TypeError: Cannot set property prefix of #<Element> which has only a getter
```

This warning is shown in case you are using `shallowMount` or `stubs` with a property name that is shared with [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element).

Common property names that are shared with `Element`:
* `attributes`
* `children`
* `prefix`

See: https://developer.mozilla.org/en-US/docs/Web/API/Element

**Possible solutions**

1. Use `mount` instead of `shallowMount` to render without stubs
2. Ignore the warning by mocking `console.warn`
3. Rename the prop to not clash with `Element` properties
1 change: 1 addition & 0 deletions src/createInstance.ts
Expand Up @@ -307,6 +307,7 @@ export function createInstance(
// stub out Transition and Transition Group by default.
transformVNodeArgs(
createVNodeTransformer({
rootComponents,
transformers: [
createStubComponentsTransformer({
rootComponents,
Expand Down
17 changes: 8 additions & 9 deletions src/vnodeTransformers/stubComponentsTransformer.ts
@@ -1,4 +1,9 @@
import { isKeepAlive, isTeleport, VTUVNodeTypeTransformer } from './util'
import {
isKeepAlive,
isRootComponent,
isTeleport,
VTUVNodeTypeTransformer
} from './util'
import {
Transition,
TransitionGroup,
Expand Down Expand Up @@ -177,14 +182,8 @@ export function createStubComponentsTransformer({
})
}

if (
// Don't stub VTU_ROOT component
!instance ||
// Don't stub mounted component on root level
(rootComponents.component === type && !instance?.parent) ||
// Don't stub component with compat wrapper
(rootComponents.functional && rootComponents.functional === type)
) {
// Don't stub root components
if (isRootComponent(rootComponents, type, instance)) {
return type
}

Expand Down
29 changes: 26 additions & 3 deletions src/vnodeTransformers/util.ts
@@ -1,6 +1,6 @@
import { isComponent } from '../utils'
import { registerStub } from '../stubs'
import { ConcreteComponent, transformVNodeArgs } from 'vue'
import { Component, ConcreteComponent, transformVNodeArgs } from 'vue'

type VNodeArgsTransformerFn = NonNullable<
Parameters<typeof transformVNodeArgs>[0]
Expand All @@ -23,9 +23,30 @@ export type VTUVNodeTypeTransformer = (
export const isTeleport = (type: any): boolean => type.__isTeleport
export const isKeepAlive = (type: any): boolean => type.__isKeepAlive

export interface RootComponents {
// Component which has been passed to mount. For functional components it contains a wrapper
component?: Component
// If component is functional then contains the original component otherwise empty
functional?: Component
}
export const isRootComponent = (
rootComponents: RootComponents,
type: VNodeTransformerInputComponentType,
instance: InstanceArgsType
): boolean =>
!!(
!instance ||
// Don't stub mounted component on root level
(rootComponents.component === type && !instance?.parent) ||
// Don't stub component with compat wrapper
(rootComponents.functional && rootComponents.functional === type)
)

export const createVNodeTransformer = ({
rootComponents,
transformers
}: {
rootComponents: RootComponents
transformers: VTUVNodeTypeTransformer[]
}): VNodeArgsTransformerFn => {
const transformationCache: WeakMap<
Expand All @@ -40,17 +61,19 @@ export const createVNodeTransformer = ({
return [originalType, props, children, ...restVNodeArgs]
}

const componentType: VNodeTransformerInputComponentType = originalType

const cachedTransformation = transformationCache.get(originalType)
if (
cachedTransformation &&
// Don't use cache for root component, as it could use stubbed recursive component
!isRootComponent(rootComponents, componentType, instance) &&
!isTeleport(originalType) &&
!isKeepAlive(originalType)
) {
return [cachedTransformation, props, children, ...restVNodeArgs]
}

const componentType: VNodeTransformerInputComponentType = originalType

const transformedType = transformers.reduce(
(type, transformer) => transformer(type, instance),
componentType
Expand Down
13 changes: 11 additions & 2 deletions tests/components/RecursiveComponent.vue
@@ -1,14 +1,23 @@
<template>
<div>
<h2>{{ name }}</h2>
<Hello />
<RecursiveComponent v-if="first" />
<template
v-for="item in items"
:key="item"
>
<RecursiveComponent
:name="item"
/>
</template>
</div>
</template>

<script setup lang="ts">
import Hello from './Hello.vue'
defineProps<{
first?: boolean
name: string
items?: string[]
}>()
</script>
16 changes: 13 additions & 3 deletions tests/shallowMount.spec.ts
Expand Up @@ -74,19 +74,29 @@ describe('shallowMount', () => {
)
})

it('stub instance of same component', () => {
it('stub instance of same component', async () => {
const wrapper = mount(RecursiveComponent, {
shallow: true,
props: {
first: true
name: '1',
items: ['2']
}
})
expect(wrapper.html()).toEqual(
'<div>\n' +
' <h2>1</h2>\n' +
' <hello-stub></hello-stub>\n' +
' <recursive-component-stub first="false"></recursive-component-stub>\n' +
' <recursive-component-stub name="2"></recursive-component-stub>\n' +
'</div>'
)

expect(wrapper.find('h2').text()).toBe('1')

await wrapper.setProps({
name: '3'
})

expect(wrapper.find('h2').text()).toBe('3')
})

it('correctly renders slot content', () => {
Expand Down

0 comments on commit 4eb66e0

Please sign in to comment.