created constants file
added specs for ide.vue Mousetrap binds
This commit is contained in:
parent
8a076c1b5b
commit
d9262b79fe
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue