mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 14:54:12 +00:00
## Summary This PR improves keyboard event handling consistency and fixes an issue where pressing Escape in nested input components would unintentionally close parent modals/dialogs. ## Changes ### Keyboard Event Fixes **TagsInput Escape Key Handling** - Added `@keydown.escape.stop` handler to `TagsInputInput.vue` to prevent Escape from bubbling up and closing parent modals - The handler blurs the input and exits editing mode without propagating the event **EditableText keyup → keydown Migration** - Changed `@keyup.enter` to `@keydown.enter` and `@keyup.escape` to `@keydown.escape` - Using `keydown` is more consistent with how other UI frameworks handle these events and provides more responsive feedback - Updated corresponding unit tests to use `keydown` triggers ### Why keydown over keyup? - `keydown` fires immediately when the key is pressed, providing faster perceived response - Better consistency with browser/OS conventions for action triggers - Prevents default behaviors (like form submission) more reliably when needed - Aligns with other keyboard handlers in the codebase ## Testing - Updated `EditableText.test.ts` to use `keydown` events - Updated `NodeHeader.test.ts` to use `keydown.enter` - Manual testing: Escape in TagsInput no longer closes parent modal ## Checklist - [x] Unit tests updated - [x] Keyboard event handlers consistent - [x] No breaking changes to component API ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8204-fix-Consistent-keydown-handling-for-EditableText-and-TagsInput-escape-key-2ef6d73d365081f0ac6bed8bcae57657) by [Unito](https://www.unito.io)
141 lines
4.4 KiB
TypeScript
141 lines
4.4 KiB
TypeScript
import { mount } from '@vue/test-utils'
|
|
import PrimeVue from 'primevue/config'
|
|
import InputText from 'primevue/inputtext'
|
|
import { beforeAll, describe, expect, it } from 'vitest'
|
|
import { createApp } from 'vue'
|
|
|
|
import EditableText from './EditableText.vue'
|
|
|
|
describe('EditableText', () => {
|
|
beforeAll(() => {
|
|
// Create a Vue app instance for PrimeVue
|
|
const app = createApp({})
|
|
app.use(PrimeVue)
|
|
})
|
|
|
|
// @ts-expect-error fixme ts strict error
|
|
const mountComponent = (props, options = {}) => {
|
|
return mount(EditableText, {
|
|
global: {
|
|
plugins: [PrimeVue],
|
|
components: { InputText }
|
|
},
|
|
props,
|
|
...options
|
|
})
|
|
}
|
|
|
|
it('renders span with modelValue when not editing', () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Test Text',
|
|
isEditing: false
|
|
})
|
|
expect(wrapper.find('span').text()).toBe('Test Text')
|
|
expect(wrapper.findComponent(InputText).exists()).toBe(false)
|
|
})
|
|
|
|
it('renders input with modelValue when editing', () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Test Text',
|
|
isEditing: true
|
|
})
|
|
expect(wrapper.find('span').exists()).toBe(false)
|
|
expect(wrapper.findComponent(InputText).props()['modelValue']).toBe(
|
|
'Test Text'
|
|
)
|
|
})
|
|
|
|
it('emits edit event when input is submitted', async () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Test Text',
|
|
isEditing: true
|
|
})
|
|
await wrapper.findComponent(InputText).setValue('New Text')
|
|
await wrapper.findComponent(InputText).trigger('keydown.enter')
|
|
// Blur event should have been triggered
|
|
expect(wrapper.findComponent(InputText).element).not.toBe(
|
|
document.activeElement
|
|
)
|
|
})
|
|
|
|
it('finishes editing on blur', async () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Test Text',
|
|
isEditing: true
|
|
})
|
|
await wrapper.findComponent(InputText).trigger('blur')
|
|
expect(wrapper.emitted('edit')).toBeTruthy()
|
|
// @ts-expect-error fixme ts strict error
|
|
expect(wrapper.emitted('edit')[0]).toEqual(['Test Text'])
|
|
})
|
|
|
|
it('cancels editing on escape key', async () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Original Text',
|
|
isEditing: true
|
|
})
|
|
|
|
// Change the input value
|
|
await wrapper.findComponent(InputText).setValue('Modified Text')
|
|
|
|
// Press escape
|
|
await wrapper.findComponent(InputText).trigger('keydown.escape')
|
|
|
|
// Should emit cancel event
|
|
expect(wrapper.emitted('cancel')).toBeTruthy()
|
|
|
|
// Should NOT emit edit event
|
|
expect(wrapper.emitted('edit')).toBeFalsy()
|
|
|
|
// Input value should be reset to original
|
|
expect(wrapper.findComponent(InputText).props()['modelValue']).toBe(
|
|
'Original Text'
|
|
)
|
|
})
|
|
|
|
it('does not save changes when escape is pressed and blur occurs', async () => {
|
|
const wrapper = mountComponent({
|
|
modelValue: 'Original Text',
|
|
isEditing: true
|
|
})
|
|
|
|
// Change the input value
|
|
await wrapper.findComponent(InputText).setValue('Modified Text')
|
|
|
|
// Press escape (which triggers blur internally)
|
|
await wrapper.findComponent(InputText).trigger('keydown.escape')
|
|
|
|
// Manually trigger blur to simulate the blur that happens after escape
|
|
await wrapper.findComponent(InputText).trigger('blur')
|
|
|
|
// Should emit cancel but not edit
|
|
expect(wrapper.emitted('cancel')).toBeTruthy()
|
|
expect(wrapper.emitted('edit')).toBeFalsy()
|
|
})
|
|
|
|
it('saves changes on enter but not on escape', async () => {
|
|
// Test Enter key saves changes
|
|
const enterWrapper = mountComponent({
|
|
modelValue: 'Original Text',
|
|
isEditing: true
|
|
})
|
|
await enterWrapper.findComponent(InputText).setValue('Saved Text')
|
|
await enterWrapper.findComponent(InputText).trigger('keydown.enter')
|
|
// Trigger blur that happens after enter
|
|
await enterWrapper.findComponent(InputText).trigger('blur')
|
|
expect(enterWrapper.emitted('edit')).toBeTruthy()
|
|
// @ts-expect-error fixme ts strict error
|
|
expect(enterWrapper.emitted('edit')[0]).toEqual(['Saved Text'])
|
|
|
|
// Test Escape key cancels changes with a fresh wrapper
|
|
const escapeWrapper = mountComponent({
|
|
modelValue: 'Original Text',
|
|
isEditing: true
|
|
})
|
|
await escapeWrapper.findComponent(InputText).setValue('Cancelled Text')
|
|
await escapeWrapper.findComponent(InputText).trigger('keydown.escape')
|
|
expect(escapeWrapper.emitted('cancel')).toBeTruthy()
|
|
expect(escapeWrapper.emitted('edit')).toBeFalsy()
|
|
})
|
|
})
|