Skip to content

Commit

Permalink
fix(vue): decorator event props not work in vue2 (#3884)
Browse files Browse the repository at this point in the history
  • Loading branch information
MeetzhDing authored Jul 6, 2023
1 parent 1e62b24 commit 8528067
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 16 deletions.
82 changes: 79 additions & 3 deletions packages/vue/src/__tests__/schema.json.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createForm } from '@formily/core'
import { createForm, Field } from '@formily/core'
import { observer } from '@formily/reactive-vue'
import { Schema } from '@formily/json-schema'
import { render, waitFor } from '@testing-library/vue'
import { fireEvent, render, waitFor } from '@testing-library/vue'
import { mount } from '@vue/test-utils'
import Vue, { FunctionalComponentOptions } from 'vue'
import {
Expand Down Expand Up @@ -46,10 +46,32 @@ const Input2: FunctionalComponentOptions = {
},
}

const FormItem: FunctionalComponentOptions = {
functional: true,
render(h, { props, slots, data }) {
return h(
'div',
{
...data,
style: {
width: '300px',
height: '30px',
background: 'yellow',
},
attrs: {
'data-testid': 'formitem',
...data.attrs,
},
},
[props.label || 'unknown ', slots().default]
)
},
}

const ArrayItems = observer(
defineComponent({
setup() {
const fieldRef = useField()
const fieldRef = useField<Field>()
const schemaRef = useFieldSchema()

return () => {
Expand Down Expand Up @@ -1259,3 +1281,57 @@ describe('schema controlled', () => {
})
})
})

describe('x-decorator', () => {
test('x-decorator-props', async () => {
const form = createForm()
const { SchemaField } = createSchemaField({
components: {
Input,
FormItem,
},
})

const atBlurFn = jest.fn()
const onClickFn = jest.fn()
const atClickFn = jest.fn()
const { queryByTestId, getByText } = render({
components: { SchemaField },
data() {
return {
form,
schema: new Schema({
type: 'string',
'x-component': 'Input',
'x-component-props': {
'@blur': function atBlur() {
atBlurFn()
},
},
'x-decorator': 'FormItem',
'x-decorator-props': {
label: 'Label ',
onClick: function onClick() {
onClickFn()
},
'@click': function atClick() {
atClickFn()
},
},
}),
}
},
template: `
<FormProvider :form="form">
<SchemaField
name="string"
:schema="schema"
/>
</FormProvider>`,
})
expect(queryByTestId('formitem')).toBeVisible()
await fireEvent.click(getByText('Label'))
expect(atClickFn).toBeCalledTimes(1)
expect(onClickFn).toBeCalledTimes(0)
})
})
44 changes: 31 additions & 13 deletions packages/vue/src/components/ReactiveField.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { inject, provide, Ref, ref, shallowRef, watch, isVue2 } from 'vue-demi'
import { GeneralField, isVoidField } from '@formily/core'
import { FormPath } from '@formily/shared'
import { each, FormPath } from '@formily/shared'
import { observer } from '@formily/reactive-vue'
import { toJS, reaction } from '@formily/reactive'
import { SchemaOptionsSymbol, FieldSymbol, h, Fragment } from '../shared'
Expand Down Expand Up @@ -160,10 +160,28 @@ export default observer({
FormPath.getIn(options?.components, field.decoratorType as string) ??
field.decoratorType
const componentAttrs = toJS(field.decorator[1]) || {}

const events: Record<string, any> = {}
each(componentAttrs, (value, eventKey) => {
const onEvent = eventKey.startsWith('on')
const atEvent = eventKey.startsWith('@')
if (!onEvent && !atEvent) return
if (onEvent) {
const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}`
// '@xxx' has higher priority
events[eventName] = events[eventName] || value
} else if (atEvent) {
const eventName = eventKey.slice(1)
events[eventName] = value
delete componentAttrs[eventKey]
}
})

const componentData = {
attrs: componentAttrs,
style: componentAttrs?.style,
class: componentAttrs?.class,
on: events,
}
delete componentData.attrs.style
delete componentData.attrs.class
Expand All @@ -186,20 +204,20 @@ export default observer({
const originFocus = originData['@focus'] || originData['onFocus']
const originBlur = originData['@blur'] || originData['onBlur']

// '@xxx' has higher priority
Object.keys(originData)
.filter((key) => key.startsWith('on'))
.forEach((eventKey) => {
each(originData, (value, eventKey) => {
const onEvent = eventKey.startsWith('on')
const atEvent = eventKey.startsWith('@')
if (!onEvent && !atEvent) return
if (onEvent) {
const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}`
events[eventName] = originData[eventKey]
})

Object.keys(originData)
.filter((key) => key.startsWith('@'))
.forEach((eventKey) => {
events[eventKey.slice(1)] = originData[eventKey]
// '@xxx' has higher priority
events[eventName] = events[eventName] || value
} else if (atEvent) {
const eventName = eventKey.slice(1)
events[eventName] = value
delete originData[eventKey]
})
}
})

events.change = (...args: any[]) => {
if (!isVoidField(field)) field.onInput(...args)
Expand Down

0 comments on commit 8528067

Please sign in to comment.