diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue
index a7b1911fa72..843632c5c70 100644
--- a/app/assets/javascripts/ide/components/file_finder/index.vue
+++ b/app/assets/javascripts/ide/components/file_finder/index.vue
@@ -4,8 +4,15 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus';
import VirtualList from 'vue-virtual-scroll-list';
import Item from './item.vue';
import router from '../../ide_router';
-
-const MAX_RESULTS = 40;
+import {
+ MAX_FILE_FINDER_RESULTS,
+ FILE_FINDER_ROW_HEIGHT,
+ FILE_FINDER_EMPTY_ROW_HEIGHT,
+ UP_KEY_CODE,
+ DOWN_KEY_CODE,
+ ENTER_KEY_CODE,
+ ESC_KEY_CODE,
+} from '../../constants';
export default {
components: {
@@ -25,12 +32,14 @@ export default {
filteredBlobs() {
const searchText = this.searchText.trim();
- if (searchText === '') return this.allBlobs.slice(0, MAX_RESULTS);
+ if (searchText === '') {
+ return this.allBlobs.slice(0, MAX_FILE_FINDER_RESULTS);
+ }
return fuzzaldrinPlus
.filter(this.allBlobs, searchText, {
key: 'path',
- maxResults: MAX_RESULTS,
+ maxResults: MAX_FILE_FINDER_RESULTS,
})
.sort((a, b) => b.lastOpenedAt - a.lastOpenedAt);
},
@@ -38,12 +47,10 @@ export default {
return this.filteredBlobs.length;
},
listShowCount() {
- if (!this.filteredBlobsLength) return 1;
-
- return this.filteredBlobsLength > 5 ? 5 : this.filteredBlobsLength;
+ return this.filteredBlobsLength ? Math.min(this.filteredBlobsLength, 5) : 1;
},
listHeight() {
- return this.filteredBlobsLength ? 55 : 33;
+ return this.filteredBlobsLength ? FILE_FINDER_ROW_HEIGHT : FILE_FINDER_EMPTY_ROW_HEIGHT;
},
showClearInputButton() {
return this.searchText.trim() !== '';
@@ -57,7 +64,9 @@ export default {
} else {
this.focusedIndex = 0;
- this.$refs.searchInput.focus();
+ if (this.$refs.searchInput) {
+ this.$refs.searchInput.focus();
+ }
}
});
},
@@ -85,8 +94,7 @@ export default {
},
onKeydown(e) {
switch (e.keyCode) {
- case 38:
- // UP
+ case UP_KEY_CODE:
e.preventDefault();
this.mouseOver = false;
if (this.focusedIndex > 0) {
@@ -95,8 +103,7 @@ export default {
this.focusedIndex = this.filteredBlobsLength - 1;
}
break;
- case 40:
- // DOWN
+ case DOWN_KEY_CODE:
e.preventDefault();
this.mouseOver = false;
if (this.focusedIndex < this.filteredBlobsLength - 1) {
@@ -111,12 +118,10 @@ export default {
},
onKeyup(e) {
switch (e.keyCode) {
- case 13:
- // ENTER
+ case ENTER_KEY_CODE:
this.openFile(this.filteredBlobs[this.focusedIndex]);
break;
- case 27:
- // ESC
+ case ESC_KEY_CODE:
this.toggleFileFinder(false);
break;
default:
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
index 3941a247234..0170f4837f8 100644
--- a/app/assets/javascripts/ide/components/file_finder/item.vue
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -30,9 +30,11 @@ export default {
},
computed: {
pathWithEllipsis() {
- return this.file.path.length < MAX_PATH_LENGTH
- ? this.file.path
- : `...${this.file.path.substr(this.file.path.length - MAX_PATH_LENGTH)}`;
+ const path = this.file.path;
+
+ return path.length < MAX_PATH_LENGTH
+ ? path
+ : `...${path.substr(path.length - MAX_PATH_LENGTH)}`;
},
nameSearchTextOccurences() {
return fuzzaldrinPlus.match(this.file.name, this.searchText);
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index 1dc162d2eb4..0274fc7d299 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,77 +1,82 @@
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
new file mode 100644
index 00000000000..e4e54f967aa
--- /dev/null
+++ b/app/assets/javascripts/ide/constants.js
@@ -0,0 +1,8 @@
+// Fuzzy file finder
+export const MAX_FILE_FINDER_RESULTS = 40;
+export const FILE_FINDER_ROW_HEIGHT = 55;
+export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
+export const UP_KEY_CODE = 38;
+export const DOWN_KEY_CODE = 40;
+export const ENTER_KEY_CODE = 13;
+export const ESC_KEY_CODE = 27;
diff --git a/spec/javascripts/ide/components/file_finder/index_spec.js b/spec/javascripts/ide/components/file_finder/index_spec.js
index 070a25fc781..594427ba14e 100644
--- a/spec/javascripts/ide/components/file_finder/index_spec.js
+++ b/spec/javascripts/ide/components/file_finder/index_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import FindFileComponent from '~/ide/components/file_finder/index.vue';
+import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/ide/constants';
import router from '~/ide/ide_router';
import { file, resetStore } from '../../helpers';
import { mountComponentWithStore } from '../../../helpers/vue_mount_component_helper';
@@ -214,7 +215,7 @@ describe('IDE File finder item spec', () => {
describe('onKeyup', () => {
it('opens file on enter key', done => {
const event = new CustomEvent('keyup');
- event.keyCode = 13;
+ event.keyCode = ENTER_KEY_CODE;
spyOn(vm, 'openFile');
@@ -229,7 +230,7 @@ describe('IDE File finder item spec', () => {
it('closes file finder on esc key', done => {
const event = new CustomEvent('keyup');
- event.keyCode = 27;
+ event.keyCode = ESC_KEY_CODE;
spyOn(vm, 'toggleFileFinder');
@@ -252,7 +253,7 @@ describe('IDE File finder item spec', () => {
describe('up key', () => {
const event = new CustomEvent('keydown');
- event.keyCode = 38;
+ event.keyCode = UP_KEY_CODE;
it('resets to last index when at top', () => {
el.dispatchEvent(event);
@@ -271,7 +272,7 @@ describe('IDE File finder item spec', () => {
describe('down key', () => {
const event = new CustomEvent('keydown');
- event.keyCode = 40;
+ event.keyCode = DOWN_KEY_CODE;
it('resets to first index when at bottom', () => {
vm.focusedIndex = 1;
diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js
index 5bd890094cc..7bfcfc90572 100644
--- a/spec/javascripts/ide/components/ide_spec.js
+++ b/spec/javascripts/ide/components/ide_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import Mousetrap from 'mousetrap';
import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
@@ -38,4 +39,68 @@ describe('ide component', () => {
done();
});
});
+
+ describe('file finder', () => {
+ beforeEach(done => {
+ spyOn(vm, 'toggleFileFinder');
+
+ vm.$store.state.fileFindVisible = true;
+
+ vm.$nextTick(done);
+ });
+
+ it('calls toggleFileFinder on `t` key press', done => {
+ Mousetrap.trigger('t');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls toggleFileFinder on `command+p` key press', done => {
+ Mousetrap.trigger('command+p');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls toggleFileFinder on `ctrl+p` key press', done => {
+ Mousetrap.trigger('ctrl+p');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('always allows `command+p` to trigger toggleFileFinder', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 'command+p'),
+ ).toBe(false);
+ });
+
+ it('always allows `ctrl+p` to trigger toggleFileFinder', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 'ctrl+p'),
+ ).toBe(false);
+ });
+
+ it('onlys handles `t` when focused in input-field', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 't'),
+ ).toBe(true);
+ });
+ });
});