Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions packages/design-system/src/components/OcModal/OcModal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ describe('OcModal', () => {
expect(wrapper.html()).toMatchSnapshot()
})

describe('enter key on the input', () => {
const mountInputModal = () =>
mount(Modal, {
global: {
renderStubDefaultSlot: true,
plugins: [...defaultPlugins()],
stubs: { 'focus-trap': true }
},
props: inputProps
})

it('emits confirm', async () => {
const wrapper = mountInputModal()
await wrapper.find('.oc-modal-body-input input').trigger('keydown', { key: 'Enter' })
expect(wrapper.emitted('confirm')).toEqual([['New folder']])
})

it('does not emit confirm while an IME composition session is active', async () => {
const wrapper = mountInputModal()
await wrapper
.find('.oc-modal-body-input input')
.trigger('keydown', { key: 'Enter', isComposing: true })
expect(wrapper.emitted('confirm')).toBeUndefined()
})

it('does not emit confirm when the keydown event has keyCode 229', async () => {
const wrapper = mountInputModal()
await wrapper
.find('.oc-modal-body-input input')
.trigger('keydown', { key: 'Enter', keyCode: 229 })
expect(wrapper.emitted('confirm')).toBeUndefined()
})
})

it('displays loading state', async () => {
const waitForSpinnerToShow = async () => {
await wrapper.vm.$nextTick()
Expand Down
10 changes: 9 additions & 1 deletion packages/design-system/src/components/OcModal/OcModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
:fix-message-line="true"
:selection-range="inputSelectionRange"
@update:model-value="inputOnInput"
@keydown.enter.prevent="confirm"
@keydown.enter.prevent="onInputKeydownEnter"
/>
</template>
</div>
Expand Down Expand Up @@ -336,6 +336,14 @@ const confirm = () => {
emit('confirm', unref(userInputValue))
}

const onInputKeydownEnter = (event: KeyboardEvent) => {
// ignore the enter confirming an IME composition (keyCode 229 for Safari)
if (event.isComposing || event.keyCode === 229) {
return
}
confirm()
}

const inputOnInput = (value: string) => {
emit('input', value)
}
Expand Down
10 changes: 9 additions & 1 deletion packages/web-app-external/src/components/FileNameModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
:error-message="errorMessage"
:fix-message-line="true"
:selection-range="inputSelectionRange"
@keydown.enter.prevent="emit('confirm')"
@keydown.enter.prevent="onKeydownEnter"
/>
<input type="submit" class="hidden" />
</form>
Expand All @@ -23,6 +23,7 @@ import {
SpaceResource
} from '@opencloud-eu/web-client'
import {
isComposingEvent,
Modal,
resolveFileNameDuplicate,
useClientService,
Expand Down Expand Up @@ -90,6 +91,13 @@ const errorMessage = computed(() => {
return undefined
})

const onKeydownEnter = (event: KeyboardEvent) => {
if (isComposingEvent(event)) {
return
}
emit('confirm')
}

const onConfirm = async () => {
await callbackFn(unref(newFileName))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
required-mark
@password-challenge-completed="$emit('update:confirmDisabled', false)"
@password-challenge-failed="$emit('update:confirmDisabled', true)"
@keydown.enter.prevent="$emit('confirm')"
@keydown.enter.prevent="onKeydownEnter"
@update:model-value="onInput"
/>
</template>
Expand All @@ -21,6 +21,7 @@ import { defineComponent, ref, unref, PropType } from 'vue'
import { useGettext } from 'vue3-gettext'
import { upperFirst } from 'lodash-es'
import {
isComposingEvent,
Modal,
useClientService,
useMessages,
Expand Down Expand Up @@ -54,6 +55,13 @@ export default defineComponent({

emit('update:confirmDisabled', true)

const onKeydownEnter = (event: KeyboardEvent) => {
if (isComposingEvent(event)) {
return
}
emit('confirm')
}

const onInput = (value: string) => {
password.value = value
errorMessage.value = undefined
Expand Down Expand Up @@ -97,6 +105,7 @@ export default defineComponent({
return {
password,
onInput,
onKeydownEnter,
errorMessage,
passwordPolicyService,
inputPasswordPolicy: passwordPolicyService.getPolicy({ enforcePassword: true }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ describe('SetLinkPasswordModal', () => {
const emitted = wrapper.emitted('update:confirmDisabled')!
expect(emitted[emitted.length - 1]).toEqual([true])
})
describe('method "onKeydownEnter"', () => {
it('emits confirm', () => {
const { wrapper } = getWrapper()
wrapper.vm.onKeydownEnter(new KeyboardEvent('keydown', { key: 'Enter' }))

expect(wrapper.emitted('confirm')).toBeTruthy()
})
it('does not emit confirm while an IME composition session is active', () => {
const { wrapper } = getWrapper()
wrapper.vm.onKeydownEnter(new KeyboardEvent('keydown', { key: 'Enter', isComposing: true }))

expect(wrapper.emitted('confirm')).toBeUndefined()
})
})
describe('method "onConfirm"', () => {
it('updates the link', async () => {
const { wrapper } = getWrapper()
Expand Down
6 changes: 3 additions & 3 deletions packages/web-pkg/src/components/CreateShortcutModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ import { useGettext } from 'vue3-gettext'
import DOMPurify from 'dompurify'
import Mark from 'mark.js'
import { OcDrop } from '@opencloud-eu/design-system/components'
import { resolveFileNameDuplicate } from '../helpers'
import { isComposingEvent, resolveFileNameDuplicate } from '../helpers'
import { useTask } from 'vue-concurrency'
import { debounce } from 'lodash-es'
import ResourcePreview from './Search/ResourcePreview.vue'
Expand Down Expand Up @@ -320,8 +320,8 @@ export default defineComponent({
unref(dropRef).hide()
}

const onKeyEnterDrop = (e: Event) => {
if (!unref(isDropOpen)) {
const onKeyEnterDrop = (e: KeyboardEvent) => {
if (isComposingEvent(e) || !unref(isDropOpen)) {
return
}

Expand Down
1 change: 1 addition & 0 deletions packages/web-pkg/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './folderVault'
export * from './vaultEngine'
export * from './filesize'
export * from './fuse'
export * from './keyboard'
export * from './locale'
export * from './path'
export * from './permissions'
Expand Down
5 changes: 5 additions & 0 deletions packages/web-pkg/src/helpers/keyboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Whether the event is part of an IME composition session, e.g. the Enter confirming a CJK
// conversion. keyCode 229 covers browsers where `isComposing` is already unset (e.g. Safari).
export const isComposingEvent = (event: KeyboardEvent) => {
return event.isComposing || event.keyCode === 229
}
22 changes: 22 additions & 0 deletions packages/web-pkg/tests/unit/components/CreateShortcutModal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ describe('CreateShortcutModal', () => {
expect(showErrorMessage).toHaveBeenCalled()
})
})
describe('method "onKeyEnterDrop"', () => {
it('handles the event when the drop is open', () => {
const { wrapper } = getWrapper()
wrapper.vm.onShowDrop()
// move the active drop item index out of range to not interact with the stubbed drop
wrapper.vm.onKeyUpDrop()
const event = new KeyboardEvent('keydown', { key: 'Enter' })
const stopPropagation = vi.spyOn(event, 'stopPropagation')
wrapper.vm.onKeyEnterDrop(event)

expect(stopPropagation).toHaveBeenCalled()
})
it('ignores the event while an IME composition session is active', () => {
const { wrapper } = getWrapper()
wrapper.vm.onShowDrop()
const event = new KeyboardEvent('keydown', { key: 'Enter', isComposing: true })
const stopPropagation = vi.spyOn(event, 'stopPropagation')
wrapper.vm.onKeyEnterDrop(event)

expect(stopPropagation).not.toHaveBeenCalled()
})
})
describe('method "searchTask"', () => {
it('should set "searchResult" correctly', async () => {
const { wrapper } = getWrapper()
Expand Down
18 changes: 18 additions & 0 deletions packages/web-pkg/tests/unit/helpers/keyboard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { isComposingEvent } from '../../../src/helpers'

describe('isComposingEvent', () => {
it('returns true when the event is part of an IME composition session', () => {
const event = new KeyboardEvent('keydown', { key: 'Enter', isComposing: true })
expect(isComposingEvent(event)).toBe(true)
})

it('returns true when the event has keyCode 229', () => {
const event = new KeyboardEvent('keydown', { key: 'Enter', keyCode: 229 } as KeyboardEventInit)
expect(isComposingEvent(event)).toBe(true)
})

it('returns false for a regular keyboard event', () => {
const event = new KeyboardEvent('keydown', { key: 'Enter' })
expect(isComposingEvent(event)).toBe(false)
})
})