Browse Source

fix(compat): maintain compatConfig option in legacy functional comp (#4974)

Illya Klymov 4 năm trước cách đây
mục cha
commit
ee97cf5a4d

+ 1 - 0
packages/runtime-core/src/compat/componentFunctional.ts

@@ -55,6 +55,7 @@ export function convertLegacyFunctionalComponent(comp: ComponentOptions) {
   }
   Func.props = comp.props
   Func.displayName = comp.name
+  Func.compatConfig = comp.compatConfig
   // v2 functional components do not inherit attrs
   Func.inheritAttrs = false
 

+ 6 - 1
packages/runtime-core/src/component.ts

@@ -60,7 +60,11 @@ import { markAttrsAccessed } from './componentRenderUtils'
 import { currentRenderingInstance } from './componentRenderContext'
 import { startMeasure, endMeasure } from './profiling'
 import { convertLegacyRenderFn } from './compat/renderFn'
-import { globalCompatConfig, validateCompatConfig } from './compat/compatConfig'
+import {
+  CompatConfig,
+  globalCompatConfig,
+  validateCompatConfig
+} from './compat/compatConfig'
 import { SchedulerJob } from './scheduler'
 
 export type Data = Record<string, unknown>
@@ -111,6 +115,7 @@ export interface FunctionalComponent<P = {}, E extends EmitsOptions = {}>
   emits?: E | (keyof E)[]
   inheritAttrs?: boolean
   displayName?: string
+  compatConfig?: CompatConfig
 }
 
 export interface ClassComponent {

+ 73 - 38
packages/vue-compat/__tests__/componentFunctional.spec.ts

@@ -18,45 +18,80 @@ afterEach(() => {
   Vue.configureCompat({ MODE: 3 })
 })
 
-test('COMPONENT_FUNCTIONAL', async () => {
-  const func = {
-    name: 'Func',
-    functional: true,
-    props: {
-      x: String
-    },
-    inject: ['foo'],
-    render: (h: any, { data, props, injections, slots }: any) => {
-      return h('div', { id: props.x, class: data.class }, [
-        h('div', { class: 'inject' }, injections.foo),
-        h('div', { class: 'slot' }, slots().default)
-      ])
+describe('COMPONENT_FUNCTIONAL', () => {
+  test('basic usage', async () => {
+    const func = {
+      name: 'Func',
+      functional: true,
+      props: {
+        x: String
+      },
+      inject: ['foo'],
+      render: (h: any, { data, props, injections, slots }: any) => {
+        return h('div', { id: props.x, class: data.class }, [
+          h('div', { class: 'inject' }, injections.foo),
+          h('div', { class: 'slot' }, slots().default)
+        ])
+      }
     }
-  }
 
-  const vm = new Vue({
-    provide() {
-      return {
-        foo: 123
+    const vm = new Vue({
+      provide() {
+        return {
+          foo: 123
+        }
+      },
+      components: {
+        func
+      },
+      template: `<func class="foo" x="foo">hello</func>`
+    }).$mount()
+
+    expect(vm.$el.id).toBe('foo')
+    expect(vm.$el.className).toBe('foo')
+    expect(vm.$el.querySelector('.inject').textContent).toBe('123')
+    expect(vm.$el.querySelector('.slot').textContent).toBe('hello')
+    expect(vm.$el.outerHTML).toMatchInlineSnapshot(
+      `"<div id=\\"foo\\" class=\\"foo\\"><div class=\\"inject\\">123</div><div class=\\"slot\\">hello</div></div>"`
+    )
+
+    expect(
+      (
+        deprecationData[DeprecationTypes.COMPONENT_FUNCTIONAL]
+          .message as Function
+      )(func)
+    ).toHaveBeenWarned()
+  })
+
+  test('copies compatConfig option', () => {
+    const func = {
+      name: 'Func',
+      functional: true,
+      compatConfig: {
+        ATTR_FALSE_VALUE: 'suppress-warning' as const
+      },
+      render: (h: any) => {
+        // should not render required: false due to compatConfig
+        return h('div', { 'data-some-attr': false })
       }
-    },
-    components: {
-      func
-    },
-    template: `<func class="foo" x="foo">hello</func>`
-  }).$mount()
-
-  expect(vm.$el.id).toBe('foo')
-  expect(vm.$el.className).toBe('foo')
-  expect(vm.$el.querySelector('.inject').textContent).toBe('123')
-  expect(vm.$el.querySelector('.slot').textContent).toBe('hello')
-  expect(vm.$el.outerHTML).toMatchInlineSnapshot(
-    `"<div id=\\"foo\\" class=\\"foo\\"><div class=\\"inject\\">123</div><div class=\\"slot\\">hello</div></div>"`
-  )
-
-  expect(
-    (
-      deprecationData[DeprecationTypes.COMPONENT_FUNCTIONAL].message as Function
-    )(func)
-  ).toHaveBeenWarned()
+    }
+
+    const vm = new Vue({
+      components: { func },
+      template: `<func class="foo" x="foo">hello</func>`
+    }).$mount()
+
+    expect(vm.$el.outerHTML).toMatchInlineSnapshot(`"<div></div>"`)
+    expect(
+      (
+        deprecationData[DeprecationTypes.COMPONENT_FUNCTIONAL]
+          .message as Function
+      )(func)
+    ).toHaveBeenWarned()
+    expect(
+      (deprecationData[DeprecationTypes.ATTR_FALSE_VALUE].message as Function)(
+        func
+      )
+    ).not.toHaveBeenWarned()
+  })
 })