Skip to content

Commit

Permalink
fix: avoid applying props / data when compositing (#66)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktsn committed Oct 27, 2018
1 parent fd2fd4a commit f5f6d50
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 9 deletions.
58 changes: 58 additions & 0 deletions src/view/components/InputComposition.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<input
v-bind="$attrs"
:value="value"
v-on="listeners"
@compositionstart="compositing = true"
@compositionend="compositing = false"
>
</template>

<script lang="ts">
import Vue from 'vue'
import { mapValues } from '@/utils'
export default Vue.extend({
name: 'InputComposition',
inheritAttrs: false,
props: {
value: {
type: String,
required: true
}
},
data() {
return {
compositing: false
}
},
computed: {
listeners(): Record<string, ((event: Event) => void)[]> {
const convert = (
fn: Function | Function[]
): ((event: Event) => void)[] => {
if (!Array.isArray(fn)) {
return convert([fn])
}
return fn.map(f => {
return (event: Event) => {
const isKeyEvent = /^key/.test(event.type)
if (isKeyEvent && this.compositing) {
return
}
const value = (event.target as HTMLInputElement).value
f(value, event)
}
})
}
return mapValues(this.$listeners, convert)
}
}
})
</script>
30 changes: 21 additions & 9 deletions src/view/components/InputJson.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
v-if="!renamable || !editing"
class="input-json-label"
>{{ field.name }}</span>
<input
<InputComposition
v-else
v-model="editingName"
class="input-json-label editing"
type="text"
@keydown.enter="apply"
@keydown.esc="cancel"
>
@keydown="onKeydownInput"
/>

<!-- Object value -->
<span
Expand All @@ -23,14 +22,13 @@
>
{{ formattedValue }}
</span>
<input
<InputComposition
v-else
v-model="editingValue"
class="input-json-value editing"
type="text"
@keydown.enter="apply"
@keydown.esc="cancel"
>
@keydown="onKeydownInput"
/>

<!-- Actions -->
<div class="input-json-actions">
Expand Down Expand Up @@ -97,6 +95,7 @@
<script lang="ts">
import Vue from 'vue'
import BaseIcon from './BaseIcon.vue'
import InputComposition from './InputComposition.vue'
import { clone } from '@/utils'
type ValueType =
Expand All @@ -118,7 +117,8 @@ export default Vue.extend({
name: 'InputJson',
components: {
BaseIcon
BaseIcon,
InputComposition
},
props: {
Expand Down Expand Up @@ -244,6 +244,18 @@ export default Vue.extend({
this.onChange()
},
onKeydownInput(_: string, event: KeyboardEvent): void {
switch (event.key) {
case 'Enter':
this.apply()
break
case 'Escape':
this.cancel()
break
default:
}
},
onChange(): void {
if (!this.isEditingNameValid) return
Expand Down
61 changes: 61 additions & 0 deletions tests/view/InputComposition.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { shallowMount } from '@vue/test-utils'
import InputComposition from '@/view/components/InputComposition.vue'

describe('InputComposition', () => {
it('ports value to real input element', () => {
const spy = jest.fn()

const wrapper = shallowMount(InputComposition, {
propsData: {
value: 'test'
},
listeners: {
input: spy
}
})

const input = wrapper.find('input').element as HTMLInputElement

expect(input.value).toBe('test')

input.value = 'updated'
wrapper.find('input').trigger('input')

expect(spy).toHaveBeenCalled()
expect(spy.mock.calls[0][0]).toBe('updated')
})

it('does not emit key events during composition', () => {
const listeners = {
input: jest.fn(),
keydown: jest.fn()
}

const wrapper = shallowMount(InputComposition, {
propsData: {
value: 'test'
},
listeners: { ...listeners }
})

const input = wrapper.find('input')
input.trigger('input')
input.trigger('keydown')
expect(listeners.input).toHaveBeenCalledTimes(1)
expect(listeners.keydown).toHaveBeenCalledTimes(1)

input.trigger('compositionstart')
input.trigger('input')
input.trigger('keydown')

expect(listeners.input).toHaveBeenCalledTimes(2)
expect(listeners.keydown).toHaveBeenCalledTimes(1)

input.trigger('compositionend')
input.trigger('input')
input.trigger('keydown')

expect(listeners.input).toHaveBeenCalledTimes(3)
expect(listeners.keydown).toHaveBeenCalledTimes(2)
})
})

0 comments on commit f5f6d50

Please sign in to comment.