created constants file

added specs for ide.vue Mousetrap binds
This commit is contained in:
Phil Hughes 2018-04-16 14:22:13 +01:00
parent 8a076c1b5b
commit d9262b79fe
No known key found for this signature in database
GPG Key ID: 32245528C52E0F9F
6 changed files with 178 additions and 92 deletions

View File

@ -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:

View File

@ -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);

View File

@ -1,77 +1,82 @@
<script>
import { mapActions, mapState, mapGetters } from 'vuex';
import Mousetrap from 'mousetrap';
import ideSidebar from './ide_side_bar.vue';
import ideContextbar from './ide_context_bar.vue';
import repoTabs from './repo_tabs.vue';
import ideStatusBar from './ide_status_bar.vue';
import repoEditor from './repo_editor.vue';
import FindFile from './file_finder/index.vue';
import { mapActions, mapState, mapGetters } from 'vuex';
import Mousetrap from 'mousetrap';
import ideSidebar from './ide_side_bar.vue';
import ideContextbar from './ide_context_bar.vue';
import repoTabs from './repo_tabs.vue';
import ideStatusBar from './ide_status_bar.vue';
import repoEditor from './repo_editor.vue';
import FindFile from './file_finder/index.vue';
export default {
components: {
ideSidebar,
ideContextbar,
repoTabs,
ideStatusBar,
repoEditor,
FindFile,
},
props: {
emptyStateSvgPath: {
type: String,
required: true,
},
noChangesStateSvgPath: {
type: String,
required: true,
},
committedStateSvgPath: {
type: String,
required: true,
},
},
computed: {
...mapState([
'changedFiles',
'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;
const originalStopCallback = Mousetrap.stopCallback;
Object.assign(e, {
returnValue,
export default {
components: {
ideSidebar,
ideContextbar,
repoTabs,
ideStatusBar,
repoEditor,
FindFile,
},
props: {
emptyStateSvgPath: {
type: String,
required: true,
},
noChangesStateSvgPath: {
type: String,
required: true,
},
committedStateSvgPath: {
type: String,
required: true,
},
},
computed: {
...mapState([
'changedFiles',
'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, {
returnValue,
});
return returnValue;
};
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
if (e.preventDefault) {
e.preventDefault();
}
this.toggleFileFinder(!this.fileFindVisible);
});
return returnValue;
};
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
e.preventDefault();
this.toggleFileFinder(!this.fileFindVisible);
});
Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
},
methods: {
...mapActions(['toggleFileFinder']),
mousetrapStopCallback(e, el, combo) {
if (combo === 't' && el.classList.contains('dropdown-input-field')) {
return true;
} else if (combo === 'command+p' || combo === 'ctrl+p') {
return false;
}
const originalStopCallback = Mousetrap.stopCallback;
Mousetrap.stopCallback = (e, el, combo) => {
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);
};
},
methods: {
...mapActions(['toggleFileFinder']),
},
};
return originalStopCallback(e, el, combo);
},
},
};
</script>
<template>

View File

@ -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;

View File

@ -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;

View File

@ -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);
});
});
});