UrlInput emits update:modelValue only on blur (#2331)

This commit is contained in:
Chenlei Hu
2025-01-23 14:55:12 -05:00
committed by GitHub
parent 157475cb2e
commit e8136ff0ae
2 changed files with 33 additions and 17 deletions

View File

@@ -2,11 +2,11 @@
<IconField class="w-full">
<InputText
v-bind="$attrs"
:model-value="modelValue"
:model-value="internalValue"
class="w-full"
:invalid="validationState === UrlValidationState.INVALID"
@update:model-value="handleInput"
@blur="validateUrl"
@blur="handleBlur"
/>
<InputIcon
:class="{
@@ -25,7 +25,7 @@
import IconField from 'primevue/iconfield'
import InputIcon from 'primevue/inputicon'
import InputText from 'primevue/inputtext'
import { ref } from 'vue'
import { ref, watch } from 'vue'
import { isValidUrl } from '@/utils/formatUtil'
import { checkUrlReachable } from '@/utils/networkUtil'
@@ -48,12 +48,30 @@ enum UrlValidationState {
const validationState = ref<UrlValidationState>(UrlValidationState.IDLE)
// Add internal value state
const internalValue = ref(props.modelValue)
// Watch for external modelValue changes
watch(
() => props.modelValue,
async (newValue) => {
internalValue.value = newValue
await validateUrl()
}
)
const handleInput = (value: string) => {
emit('update:modelValue', value)
// Update internal value without emitting
internalValue.value = value
// Reset validation state when user types
validationState.value = UrlValidationState.IDLE
}
const handleBlur = async () => {
// Emit the update only on blur
emit('update:modelValue', internalValue.value)
}
// Default validation implementation
const defaultValidateUrl = async (url: string): Promise<boolean> => {
if (!isValidUrl(url)) return false

View File

@@ -35,7 +35,7 @@ describe('UrlInput', () => {
expect(wrapper.find('input').attributes('disabled')).toBe('')
})
it('emits update:modelValue on input', async () => {
it('emits update:modelValue on blur', async () => {
const wrapper = mountComponent({
modelValue: '',
placeholder: 'Enter URL'
@@ -43,6 +43,7 @@ describe('UrlInput', () => {
const input = wrapper.find('input')
await input.setValue('https://test.com')
await input.trigger('blur')
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([
'https://test.com'
@@ -51,7 +52,7 @@ describe('UrlInput', () => {
it('renders spinner when validation is loading', async () => {
const wrapper = mountComponent({
modelValue: 'https://test.com',
modelValue: '',
placeholder: 'Enter URL',
validateUrlFn: () =>
new Promise(() => {
@@ -59,9 +60,8 @@ describe('UrlInput', () => {
})
})
const input = wrapper.findComponent(InputText)
await input.trigger('blur')
wrapper.setProps({ modelValue: 'https://test.com' })
await nextTick()
await nextTick()
expect(wrapper.find('.pi-spinner').exists()).toBe(true)
@@ -69,14 +69,13 @@ describe('UrlInput', () => {
it('renders check icon when validation is valid', async () => {
const wrapper = mountComponent({
modelValue: 'https://test.com',
modelValue: '',
placeholder: 'Enter URL',
validateUrlFn: () => Promise.resolve(true)
})
const input = wrapper.findComponent(InputText)
await input.trigger('blur')
wrapper.setProps({ modelValue: 'https://test.com' })
await nextTick()
await nextTick()
expect(wrapper.find('.pi-check').exists()).toBe(true)
@@ -84,14 +83,13 @@ describe('UrlInput', () => {
it('renders cross icon when validation is invalid', async () => {
const wrapper = mountComponent({
modelValue: 'https://test.com',
modelValue: '',
placeholder: 'Enter URL',
validateUrlFn: () => Promise.resolve(false)
})
const input = wrapper.findComponent(InputText)
await input.trigger('blur')
wrapper.setProps({ modelValue: 'https://test.com' })
await nextTick()
await nextTick()
expect(wrapper.find('.pi-times').exists()).toBe(true)