Skip to content
代码片段 群组 项目
未验证 提交 23dca1a7 编辑于 作者: Simon Knox's avatar Simon Knox 提交者: GitLab
浏览文件

Merge branch 'tor/maintenance/capture-finalize-strokes-related-mrs' into 'master'

Capture Tabs and Enters when in the blocking MRs field to tokenize the input

See merge request https://gitlab.com/gitlab-org/gitlab/-/merge_requests/180069



Merged-by: default avatarSimon Knox <simon@gitlab.com>
Approved-by: default avatarMinahil Nichols <minahilnichols@gitlab.com>
Approved-by: default avatarMichael Le <mle@gitlab.com>
Approved-by: default avatarSimon Knox <simon@gitlab.com>
Reviewed-by: default avatarSimon Knox <simon@gitlab.com>
Reviewed-by: default avatarMichael Le <mle@gitlab.com>
Co-authored-by: default avatarThomas Randolph <trandolph@gitlab.com>
No related branches found
No related tags found
无相关合并请求
......@@ -8,6 +8,7 @@ import {
inputPlaceholderConfidentialTextMap,
inputPlaceholderTextMap,
} from '../constants';
import { ENTER_KEY, TAB_KEY } from '../../lib/utils/keys';
import IssueToken from './issue_token.vue';
const SPACE_FACTOR = 1;
......@@ -167,6 +168,13 @@ export default {
onFocus() {
this.isInputFocused = true;
},
onKeydown(event) {
if ([ENTER_KEY, TAB_KEY].includes(event.key)) {
const { value } = this.$refs.input;
this.$emit('addIssuableFinishEntry', { value, event });
}
},
setupAutoComplete() {
const $input = $(this.$refs.input);
......@@ -231,6 +239,7 @@ export default {
@input="onInput"
@focus="onFocus"
@blur="onBlur"
@keydown="onKeydown"
@keyup.escape.exact="$emit('addIssuableFormCancel')"
/>
</li>
......
<script>
import { ENTER_KEY, TAB_KEY } from '~/lib/utils/keys';
import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
import { TYPE_MERGE_REQUEST } from '~/issues/constants';
......@@ -60,6 +61,18 @@ export default {
this.inputValue = '';
}
},
onKeyFinish({ value, event }) {
const isTab = event.key === TAB_KEY;
const isEnter = event.key === ENTER_KEY;
const isUnmodifiedEnter = isEnter && !(event.metaKey || event.ctrlKey);
if (isTab || isUnmodifiedEnter) {
event.preventDefault();
event.stopPropagation();
this.onBlur(value);
}
},
},
};
</script>
......@@ -75,6 +88,7 @@ export default {
@addIssuableFormInput="onAddIssuable"
@pendingIssuableRemoveRequest="removeReference"
@addIssuableFormBlur="onBlur"
@addIssuableFinishEntry="onKeyFinish"
/>
<input
v-for="ref in references"
......
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { ENTER_KEY, TAB_KEY } from '~/lib/utils/keys';
import BlockingMrInputRoot from 'ee/projects/merge_requests/blocking_mr_input_root.vue';
import RelatedIssuableInput from '~/related_issues/components/related_issuable_input.vue';
......@@ -71,6 +72,84 @@ describe('blocking mr input root', () => {
expect(wrapper.vm.references).toHaveLength(0);
});
describe('"finish" keystrokes (Enter or Tab)', () => {
const mockEvent = {
preventDefault: jest.fn(),
stopPropagation: jest.fn(),
ctrlKey: false,
key: ENTER_KEY,
metaKey: false,
};
beforeEach(() => {
mockEvent.ctrlKey = false;
mockEvent.key = ENTER_KEY;
mockEvent.metaKey = false;
mockEvent.preventDefault.mockReset();
mockEvent.stopPropagation.mockReset();
});
it.each`
description | event
${'tabs'} | ${mockEvent}
${'enters'} | ${mockEvent}
`('prevent the default event behavior for $description', ({ event }) => {
createComponent();
getInput().vm.$emit('addIssuableFinishEntry', { value: 'x', event });
expect(event.preventDefault).toHaveBeenCalledTimes(1);
expect(event.stopPropagation).toHaveBeenCalledTimes(1);
});
it('do not add empty references', () => {
createComponent();
getInput().vm.$emit('addIssuableFinishEntry', { value: '', event: mockEvent });
expect(wrapper.vm.references).toHaveLength(0);
});
it('add new tokens', () => {
createComponent();
getInput().vm.$emit('addIssuableFinishEntry', { value: '!1', event: mockEvent });
getInput().vm.$emit('addIssuableFinishEntry', { value: '!2', event: mockEvent });
expect(wrapper.vm.references).toEqual(['!1', '!2']);
});
describe('with modifiers', () => {
it.each`
modifier | event
${'Cmd'} | ${{ ...mockEvent, metaKey: true, key: TAB_KEY }}
${'Ctrl'} | ${{ ...mockEvent, ctrlKey: true, key: TAB_KEY }}
`('$modifier does not affect the Tab handler', ({ event }) => {
createComponent();
getInput().vm.$emit('addIssuableFinishEntry', { value: '!1', event });
expect(event.preventDefault).toHaveBeenCalledTimes(1);
expect(event.stopPropagation).toHaveBeenCalledTimes(1);
expect(wrapper.vm.references).toEqual(['!1']);
});
it.each`
modifier | event
${'Cmd'} | ${{ ...mockEvent, metaKey: true }}
${'Ctrl'} | ${{ ...mockEvent, ctrlKey: true }}
`('$modifier skips the special handler for Enter', ({ event }) => {
createComponent();
getInput().vm.$emit('addIssuableFinishEntry', { value: '!1', event });
expect(event.preventDefault).toHaveBeenCalledTimes(0);
expect(event.stopPropagation).toHaveBeenCalledTimes(0);
expect(wrapper.vm.references).toEqual([]);
});
});
});
describe('hidden inputs', () => {
const createHiddenInputExpectation = (selector) => (bool) => {
expect(wrapper.find(selector).element.value).toBe(`${bool}`);
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册