Commit d9262b79 authored by Phil Hughes's avatar Phil Hughes

created constants file

added specs for ide.vue Mousetrap binds
parent 8a076c1b
...@@ -4,8 +4,15 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus'; ...@@ -4,8 +4,15 @@ import fuzzaldrinPlus from 'fuzzaldrin-plus';
import VirtualList from 'vue-virtual-scroll-list'; import VirtualList from 'vue-virtual-scroll-list';
import Item from './item.vue'; import Item from './item.vue';
import router from '../../ide_router'; import router from '../../ide_router';
import {
const MAX_RESULTS = 40; 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 { export default {
components: { components: {
...@@ -25,12 +32,14 @@ export default { ...@@ -25,12 +32,14 @@ export default {
filteredBlobs() { filteredBlobs() {
const searchText = this.searchText.trim(); 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 return fuzzaldrinPlus
.filter(this.allBlobs, searchText, { .filter(this.allBlobs, searchText, {
key: 'path', key: 'path',
maxResults: MAX_RESULTS, maxResults: MAX_FILE_FINDER_RESULTS,
}) })
.sort((a, b) => b.lastOpenedAt - a.lastOpenedAt); .sort((a, b) => b.lastOpenedAt - a.lastOpenedAt);
}, },
...@@ -38,12 +47,10 @@ export default { ...@@ -38,12 +47,10 @@ export default {
return this.filteredBlobs.length; return this.filteredBlobs.length;
}, },
listShowCount() { listShowCount() {
if (!this.filteredBlobsLength) return 1; return this.filteredBlobsLength ? Math.min(this.filteredBlobsLength, 5) : 1;
return this.filteredBlobsLength > 5 ? 5 : this.filteredBlobsLength;
}, },
listHeight() { listHeight() {
return this.filteredBlobsLength ? 55 : 33; return this.filteredBlobsLength ? FILE_FINDER_ROW_HEIGHT : FILE_FINDER_EMPTY_ROW_HEIGHT;
}, },
showClearInputButton() { showClearInputButton() {
return this.searchText.trim() !== ''; return this.searchText.trim() !== '';
...@@ -57,7 +64,9 @@ export default { ...@@ -57,7 +64,9 @@ export default {
} else { } else {
this.focusedIndex = 0; this.focusedIndex = 0;
this.$refs.searchInput.focus(); if (this.$refs.searchInput) {
this.$refs.searchInput.focus();
}
} }
}); });
}, },
...@@ -85,8 +94,7 @@ export default { ...@@ -85,8 +94,7 @@ export default {
}, },
onKeydown(e) { onKeydown(e) {
switch (e.keyCode) { switch (e.keyCode) {
case 38: case UP_KEY_CODE:
// UP
e.preventDefault(); e.preventDefault();
this.mouseOver = false; this.mouseOver = false;
if (this.focusedIndex > 0) { if (this.focusedIndex > 0) {
...@@ -95,8 +103,7 @@ export default { ...@@ -95,8 +103,7 @@ export default {
this.focusedIndex = this.filteredBlobsLength - 1; this.focusedIndex = this.filteredBlobsLength - 1;
} }
break; break;
case 40: case DOWN_KEY_CODE:
// DOWN
e.preventDefault(); e.preventDefault();
this.mouseOver = false; this.mouseOver = false;
if (this.focusedIndex < this.filteredBlobsLength - 1) { if (this.focusedIndex < this.filteredBlobsLength - 1) {
...@@ -111,12 +118,10 @@ export default { ...@@ -111,12 +118,10 @@ export default {
}, },
onKeyup(e) { onKeyup(e) {
switch (e.keyCode) { switch (e.keyCode) {
case 13: case ENTER_KEY_CODE:
// ENTER
this.openFile(this.filteredBlobs[this.focusedIndex]); this.openFile(this.filteredBlobs[this.focusedIndex]);
break; break;
case 27: case ESC_KEY_CODE:
// ESC
this.toggleFileFinder(false); this.toggleFileFinder(false);
break; break;
default: default:
......
...@@ -30,9 +30,11 @@ export default { ...@@ -30,9 +30,11 @@ export default {
}, },
computed: { computed: {
pathWithEllipsis() { pathWithEllipsis() {
return this.file.path.length < MAX_PATH_LENGTH const path = this.file.path;
? this.file.path
: `...${this.file.path.substr(this.file.path.length - MAX_PATH_LENGTH)}`; return path.length < MAX_PATH_LENGTH
? path
: `...${path.substr(path.length - MAX_PATH_LENGTH)}`;
}, },
nameSearchTextOccurences() { nameSearchTextOccurences() {
return fuzzaldrinPlus.match(this.file.name, this.searchText); return fuzzaldrinPlus.match(this.file.name, this.searchText);
......
<script> <script>
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import Mousetrap from 'mousetrap'; import Mousetrap from 'mousetrap';
import ideSidebar from './ide_side_bar.vue'; import ideSidebar from './ide_side_bar.vue';
import ideContextbar from './ide_context_bar.vue'; import ideContextbar from './ide_context_bar.vue';
import repoTabs from './repo_tabs.vue'; import repoTabs from './repo_tabs.vue';
import ideStatusBar from './ide_status_bar.vue'; import ideStatusBar from './ide_status_bar.vue';
import repoEditor from './repo_editor.vue'; import repoEditor from './repo_editor.vue';
import FindFile from './file_finder/index.vue'; import FindFile from './file_finder/index.vue';
export default { const originalStopCallback = Mousetrap.stopCallback;
components: {
ideSidebar, export default {
ideContextbar, components: {
repoTabs, ideSidebar,
ideStatusBar, ideContextbar,
repoEditor, repoTabs,
FindFile, ideStatusBar,
}, repoEditor,
props: { FindFile,
emptyStateSvgPath: {
type: String,
required: true,
}, },
noChangesStateSvgPath: { props: {
type: String, emptyStateSvgPath: {
required: true, type: String,
required: true,
},
noChangesStateSvgPath: {
type: String,
required: true,
},
committedStateSvgPath: {
type: String,
required: true,
},
}, },
committedStateSvgPath: { computed: {
type: String, ...mapState([
required: true, 'changedFiles',
'openFiles',
'viewer',
'currentMergeRequestId',
'fileFindVisible',
]),
...mapGetters(['activeFile', 'hasChanges']),
}, },
}, mounted() {
computed: { const returnValue = 'Are you sure you want to lose unsaved changes?';
...mapState([ window.onbeforeunload = e => {
'changedFiles', if (!this.changedFiles.length) return undefined;
'openFiles',
'viewer',
'currentMergeRequestId',
'fileFindVisible',
]),
...mapGetters(['activeFile', 'hasChanges']),
},
mounted() {
const returnValue = 'Are you sure you want to lose unsaved changes?';
window.onbeforeunload = e => {
if (!this.changedFiles.length) return undefined;
Object.assign(e, { Object.assign(e, {
returnValue, returnValue,
}); });
return returnValue; return returnValue;
}; };
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
if (e.preventDefault) {
e.preventDefault();
}
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => { this.toggleFileFinder(!this.fileFindVisible);
e.preventDefault(); });
this.toggleFileFinder(!this.fileFindVisible);
});
const originalStopCallback = Mousetrap.stopCallback; Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
Mousetrap.stopCallback = (e, el, combo) => { },
if (combo === 't' && el.classList.contains('dropdown-input-field')) { methods: {
return true; ...mapActions(['toggleFileFinder']),
} else if (combo === 'command+p' || combo === 'ctrl+p') { mousetrapStopCallback(e, el, combo) {
return false; if (combo === 't' && el.classList.contains('dropdown-input-field')) {
} return true;
} else if (combo === 'command+p' || combo === 'ctrl+p') {
return false;
}
return originalStopCallback(e, el, combo); return originalStopCallback(e, el, combo);
}; },
}, },
methods: { };
...mapActions(['toggleFileFinder']),
},
};
</script> </script>
<template> <template>
......
// 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;
import Vue from 'vue'; import Vue from 'vue';
import store from '~/ide/stores'; import store from '~/ide/stores';
import FindFileComponent from '~/ide/components/file_finder/index.vue'; 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 router from '~/ide/ide_router';
import { file, resetStore } from '../../helpers'; import { file, resetStore } from '../../helpers';
import { mountComponentWithStore } from '../../../helpers/vue_mount_component_helper'; import { mountComponentWithStore } from '../../../helpers/vue_mount_component_helper';
...@@ -214,7 +215,7 @@ describe('IDE File finder item spec', () => { ...@@ -214,7 +215,7 @@ describe('IDE File finder item spec', () => {
describe('onKeyup', () => { describe('onKeyup', () => {
it('opens file on enter key', done => { it('opens file on enter key', done => {
const event = new CustomEvent('keyup'); const event = new CustomEvent('keyup');
event.keyCode = 13; event.keyCode = ENTER_KEY_CODE;
spyOn(vm, 'openFile'); spyOn(vm, 'openFile');
...@@ -229,7 +230,7 @@ describe('IDE File finder item spec', () => { ...@@ -229,7 +230,7 @@ describe('IDE File finder item spec', () => {
it('closes file finder on esc key', done => { it('closes file finder on esc key', done => {
const event = new CustomEvent('keyup'); const event = new CustomEvent('keyup');
event.keyCode = 27; event.keyCode = ESC_KEY_CODE;
spyOn(vm, 'toggleFileFinder'); spyOn(vm, 'toggleFileFinder');
...@@ -252,7 +253,7 @@ describe('IDE File finder item spec', () => { ...@@ -252,7 +253,7 @@ describe('IDE File finder item spec', () => {
describe('up key', () => { describe('up key', () => {
const event = new CustomEvent('keydown'); const event = new CustomEvent('keydown');
event.keyCode = 38; event.keyCode = UP_KEY_CODE;
it('resets to last index when at top', () => { it('resets to last index when at top', () => {
el.dispatchEvent(event); el.dispatchEvent(event);
...@@ -271,7 +272,7 @@ describe('IDE File finder item spec', () => { ...@@ -271,7 +272,7 @@ describe('IDE File finder item spec', () => {
describe('down key', () => { describe('down key', () => {
const event = new CustomEvent('keydown'); const event = new CustomEvent('keydown');
event.keyCode = 40; event.keyCode = DOWN_KEY_CODE;
it('resets to first index when at bottom', () => { it('resets to first index when at bottom', () => {
vm.focusedIndex = 1; vm.focusedIndex = 1;
......
import Vue from 'vue'; import Vue from 'vue';
import Mousetrap from 'mousetrap';
import store from '~/ide/stores'; import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue'; import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
...@@ -38,4 +39,68 @@ describe('ide component', () => { ...@@ -38,4 +39,68 @@ describe('ide component', () => {
done(); 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);
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment