3 changed files with 163 additions and 7 deletions
@ -0,0 +1,5 @@ |
|||||
|
--- |
||||
|
'@vben/common-ui': patch |
||||
|
--- |
||||
|
|
||||
|
fix: forward ApiComponent updates for custom model value props |
||||
@ -0,0 +1,131 @@ |
|||||
|
import { mount } from '@vue/test-utils'; |
||||
|
import { defineComponent, h, markRaw, nextTick, ref } from 'vue'; |
||||
|
|
||||
|
import { describe, expect, it, vi } from 'vitest'; |
||||
|
|
||||
|
import ApiComponent from '../api-component.vue'; |
||||
|
|
||||
|
const ValueInput = defineComponent({ |
||||
|
name: 'ValueInput', |
||||
|
props: { |
||||
|
value: { type: String, default: undefined }, |
||||
|
}, |
||||
|
emits: ['update:value'], |
||||
|
setup(props, { emit }) { |
||||
|
return () => |
||||
|
h( |
||||
|
'button', |
||||
|
{ onClick: () => emit('update:value', 'selected') }, |
||||
|
props.value, |
||||
|
); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const ModelValueInput = defineComponent({ |
||||
|
name: 'ModelValueInput', |
||||
|
props: { |
||||
|
modelValue: { type: String, default: undefined }, |
||||
|
}, |
||||
|
emits: ['update:modelValue'], |
||||
|
setup(props, { emit }) { |
||||
|
return () => |
||||
|
h( |
||||
|
'button', |
||||
|
{ onClick: () => emit('update:modelValue', 'selected') }, |
||||
|
props.modelValue, |
||||
|
); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const KebabModelInput = defineComponent({ |
||||
|
name: 'KebabModelInput', |
||||
|
props: { |
||||
|
modelValue: { type: String, default: undefined }, |
||||
|
}, |
||||
|
emits: ['update:modelValue'], |
||||
|
setup(props, { emit }) { |
||||
|
return () => |
||||
|
h( |
||||
|
'button', |
||||
|
{ onClick: () => emit('update:modelValue', 'selected') }, |
||||
|
props.modelValue, |
||||
|
); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
describe('api-component.vue', () => { |
||||
|
it('bridges a custom model prop in both directions', async () => { |
||||
|
const outerValue = ref('initial'); |
||||
|
const handleUpdate = vi.fn((value: string) => { |
||||
|
outerValue.value = value; |
||||
|
}); |
||||
|
const Harness = defineComponent({ |
||||
|
setup() { |
||||
|
return () => |
||||
|
h(ApiComponent, { |
||||
|
component: markRaw(ValueInput), |
||||
|
modelPropName: 'value', |
||||
|
value: outerValue.value, |
||||
|
'onUpdate:value': handleUpdate, |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
const wrapper = mount(Harness); |
||||
|
const input = wrapper.findComponent(ValueInput); |
||||
|
|
||||
|
expect(input.props('value')).toBe('initial'); |
||||
|
|
||||
|
await input.trigger('click'); |
||||
|
await nextTick(); |
||||
|
expect(handleUpdate).toHaveBeenCalledWith('selected'); |
||||
|
expect(input.props('value')).toBe('selected'); |
||||
|
|
||||
|
outerValue.value = 'external'; |
||||
|
await nextTick(); |
||||
|
expect(input.props('value')).toBe('external'); |
||||
|
}); |
||||
|
|
||||
|
it('preserves the default modelValue protocol', async () => { |
||||
|
const wrapper = mount(ApiComponent, { |
||||
|
props: { |
||||
|
component: markRaw(ModelValueInput), |
||||
|
modelValue: 'initial', |
||||
|
}, |
||||
|
}); |
||||
|
const input = wrapper.findComponent(ModelValueInput); |
||||
|
|
||||
|
expect(input.props('modelValue')).toBe('initial'); |
||||
|
|
||||
|
await input.trigger('click'); |
||||
|
await nextTick(); |
||||
|
expect(wrapper.emitted('update:modelValue')).toEqual([['selected']]); |
||||
|
expect(input.props('modelValue')).toBe('selected'); |
||||
|
}); |
||||
|
|
||||
|
it('bridges a kebab-case custom model prop', async () => { |
||||
|
const outerValue = ref('initial'); |
||||
|
const handleUpdate = vi.fn((value: string) => { |
||||
|
outerValue.value = value; |
||||
|
}); |
||||
|
const Harness = defineComponent({ |
||||
|
setup() { |
||||
|
return () => |
||||
|
h(ApiComponent, { |
||||
|
component: markRaw(KebabModelInput), |
||||
|
modelPropName: 'model-value', |
||||
|
'model-value': outerValue.value, |
||||
|
'onUpdate:model-value': handleUpdate, |
||||
|
}); |
||||
|
}, |
||||
|
}); |
||||
|
const wrapper = mount(Harness); |
||||
|
const input = wrapper.findComponent(KebabModelInput); |
||||
|
|
||||
|
expect(input.props('modelValue')).toBe('initial'); |
||||
|
|
||||
|
await input.trigger('click'); |
||||
|
await nextTick(); |
||||
|
expect(handleUpdate).toHaveBeenCalledWith('selected'); |
||||
|
expect(input.props('modelValue')).toBe('selected'); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue