Commit fffa5640 authored by Phil Hughes's avatar Phil Hughes

fixed closing last file not opening the next available file

fixed right sidebar not being collapsible
parent 816b4202
...@@ -68,7 +68,6 @@ ...@@ -68,7 +68,6 @@
.ide-new-btn { .ide-new-btn {
display: none; display: none;
margin-top: -4px;
margin-bottom: -4px; margin-bottom: -4px;
margin-right: -8px; margin-right: -8px;
} }
...@@ -84,7 +83,6 @@ ...@@ -84,7 +83,6 @@
fill: $gl-text-color-secondary; fill: $gl-text-color-secondary;
} }
} }
} }
a { a {
...@@ -290,7 +288,7 @@ ...@@ -290,7 +288,7 @@
.margin-view-overlays .insert-sign, .margin-view-overlays .insert-sign,
.margin-view-overlays .delete-sign { .margin-view-overlays .delete-sign {
opacity: .4; opacity: 0.4;
} }
} }
} }
...@@ -548,7 +546,6 @@ ...@@ -548,7 +546,6 @@
height: 10px; height: 10px;
margin-left: 3px; margin-left: 3px;
} }
} }
.multi-file-commit-list-path { .multi-file-commit-list-path {
...@@ -626,7 +623,7 @@ ...@@ -626,7 +623,7 @@
top: 0; top: 0;
width: 100px; width: 100px;
height: 1px; height: 1px;
background-color: rgba($red-500, .5); background-color: rgba($red-500, 0.5);
} }
} }
} }
...@@ -697,8 +694,12 @@ ...@@ -697,8 +694,12 @@
} }
.multi-file-commit-panel .multi-file-commit-panel-inner-scroll { .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
max-height: calc(100vh - #{$header-height + $flash-height + $context-header-height}); max-height: calc(
min-height: calc(100vh - #{$header-height + $flash-height + $context-header-height}); 100vh - #{$header-height + $flash-height + $context-header-height}
);
min-height: calc(
100vh - #{$header-height + $flash-height + $context-header-height}
);
} }
} }
} }
...@@ -729,17 +730,24 @@ ...@@ -729,17 +730,24 @@
} }
.ide-view { .ide-view {
height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height}); height: calc(
100vh - #{$header-height + $performance-bar-height + $flash-height}
);
} }
.multi-file-commit-panel .multi-file-commit-panel-inner-scroll { .multi-file-commit-panel .multi-file-commit-panel-inner-scroll {
max-height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height + $context-header-height}); max-height: calc(
min-height: calc(100vh - #{$header-height + $performance-bar-height + $flash-height + $context-header-height}); 100vh - #{$header-height + $performance-bar-height + $flash-height +
$context-header-height}
);
min-height: calc(
100vh - #{$header-height + $performance-bar-height + $flash-height +
$context-header-height}
);
} }
} }
} }
.dragHandle { .dragHandle {
position: absolute; position: absolute;
top: 0; top: 0;
......
<script> <script>
import { mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue'; import panelResizer from '~/vue_shared/components/panel_resizer.vue';
import repoCommitSection from './repo_commit_section.vue'; import repoCommitSection from './repo_commit_section.vue';
import ResizablePanel from './resizable_panel.vue'; import ResizablePanel from './resizable_panel.vue';
export default { export default {
components: { components: {
repoCommitSection, repoCommitSection,
icon, icon,
panelResizer, panelResizer,
ResizablePanel, ResizablePanel,
},
props: {
noChangesStateSvgPath: {
type: String,
required: true,
}, },
props: { committedStateSvgPath: {
noChangesStateSvgPath: { type: String,
type: String, required: true,
required: true,
},
committedStateSvgPath: {
type: String,
required: true,
},
}, },
computed: { },
...mapState([ computed: {
'changedFiles', ...mapState(['changedFiles', 'rightPanelCollapsed']),
'rightPanelCollapsed', ...mapGetters(['currentIcon']),
]), },
...mapGetters([ methods: {
'currentIcon', ...mapActions(['setPanelCollapsedStatus']),
]), },
}, };
};
</script> </script>
<template> <template>
...@@ -66,7 +64,10 @@ ...@@ -66,7 +64,10 @@
<button <button
type="button" type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn" class="btn btn-transparent multi-file-commit-panel-collapse-btn"
@click.stop="toggleCollapsed" @click.stop="setPanelCollapsedStatus({
side: 'right',
collapsed: !rightPanelCollapsed,
})"
> >
<icon <icon
:name="currentIcon" :name="currentIcon"
......
<script> <script>
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import fileIcon from '~/vue_shared/components/file_icon.vue'; import fileIcon from '~/vue_shared/components/file_icon.vue';
import router from '../ide_router'; import router from '../ide_router';
import newDropdown from './new_dropdown/index.vue'; import newDropdown from './new_dropdown/index.vue';
import fileStatusIcon from './repo_file_status_icon.vue'; import fileStatusIcon from './repo_file_status_icon.vue';
import changedFileIcon from './changed_file_icon.vue'; import changedFileIcon from './changed_file_icon.vue';
export default { export default {
name: 'RepoFile', name: 'RepoFile',
components: { components: {
skeletonLoadingContainer, skeletonLoadingContainer,
newDropdown, newDropdown,
fileStatusIcon, fileStatusIcon,
fileIcon, fileIcon,
changedFileIcon, changedFileIcon,
},
props: {
file: {
type: Object,
required: true,
}, },
props: { level: {
file: { type: Number,
type: Object, required: true,
required: true,
},
level: {
type: Number,
required: true,
},
}, },
computed: { },
isTree() { computed: {
return this.file.type === 'tree'; isTree() {
}, return this.file.type === 'tree';
isBlob() {
return this.file.type === 'blob';
},
levelIndentation() {
return {
marginLeft: `${this.level * 16}px`,
};
},
fileClass() {
return {
'file-open': this.isBlob && this.file.opened,
'file-active': this.isBlob && this.file.active,
folder: this.isTree,
};
},
}, },
updated() { isBlob() {
if (this.file.type === 'blob' && this.file.active) { return this.file.type === 'blob';
this.$el.scrollIntoView(); },
} levelIndentation() {
return {
marginLeft: `${this.level * 16}px`,
};
},
fileClass() {
return {
'file-open': this.isBlob && this.file.opened,
'file-active': this.isBlob && this.file.active,
folder: this.isTree,
};
}, },
methods: { },
...mapActions(['toggleTreeOpen', 'updateDelayViewerUpdated']), updated() {
clickFile() { if (this.file.type === 'blob' && this.file.active) {
// Manual Action if a tree is selected/opened this.$el.scrollIntoView();
if ( }
this.isTree && },
this.$router.currentRoute.path === `/project${this.file.url}` methods: {
) { ...mapActions(['toggleTreeOpen', 'updateDelayViewerUpdated']),
this.toggleTreeOpen(this.file.path); clickFile() {
} // Manual Action if a tree is selected/opened
if (
this.isTree &&
this.$router.currentRoute.path === `/project${this.file.url}`
) {
this.toggleTreeOpen(this.file.path);
}
const delayPromise = this.file.changed const delayPromise = this.file.changed
? Promise.resolve() ? Promise.resolve()
: this.updateDelayViewerUpdated(true); : this.updateDelayViewerUpdated(true);
return delayPromise.then(() => { return delayPromise.then(() => {
router.push(`/project${this.file.url}`); router.push(`/project${this.file.url}`);
}); });
},
}, },
}; },
};
</script> </script>
<template> <template>
...@@ -101,16 +101,17 @@ ...@@ -101,16 +101,17 @@
:file="file" :file="file"
/> />
</span> </span>
<changed-file-icon
:file="file"
v-if="file.changed || file.tempFile"
class="prepend-top-5 pull-right"
/>
<new-dropdown <new-dropdown
v-if="isTree" v-if="isTree"
:project-id="file.projectId" :project-id="file.projectId"
:branch="file.branchId" :branch="file.branchId"
:path="file.path" :path="file.path"
/> class="pull-right prepend-left-8"
<changed-file-icon
:file="file"
v-if="file.changed || file.tempFile"
class="prepend-top-5"
/> />
</div> </div>
</div> </div>
......
...@@ -7,7 +7,7 @@ import router from '../../ide_router'; ...@@ -7,7 +7,7 @@ import router from '../../ide_router';
import { setPageTitle } from '../utils'; import { setPageTitle } from '../utils';
export const closeFile = ({ commit, state, getters, dispatch }, path) => { export const closeFile = ({ commit, state, getters, dispatch }, path) => {
const indexOfClosedFile = state.openFiles.indexOf(path); const indexOfClosedFile = state.openFiles.findIndex(f => f.path === path);
const file = state.entries[path]; const file = state.entries[path];
const fileWasActive = file.active; const fileWasActive = file.active;
...@@ -16,7 +16,7 @@ export const closeFile = ({ commit, state, getters, dispatch }, path) => { ...@@ -16,7 +16,7 @@ export const closeFile = ({ commit, state, getters, dispatch }, path) => {
if (state.openFiles.length > 0 && fileWasActive) { if (state.openFiles.length > 0 && fileWasActive) {
const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1; const nextIndexToOpen = indexOfClosedFile === 0 ? 0 : indexOfClosedFile - 1;
const nextFileToOpen = state.entries[state.openFiles[nextIndexToOpen]]; const nextFileToOpen = state.entries[state.openFiles[nextIndexToOpen].path];
router.push(`/project${nextFileToOpen.url}`); router.push(`/project${nextFileToOpen.url}`);
} else if (!state.openFiles.length) { } else if (!state.openFiles.length) {
......
...@@ -137,6 +137,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState }) = ...@@ -137,6 +137,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState }) =
} }
dispatch('setLastCommitMessage', data); dispatch('setLastCommitMessage', data);
dispatch('updateCommitMessage', '');
if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) { if (state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR) {
dispatch( dispatch(
......
...@@ -27,21 +27,24 @@ describe('Multi-file store file actions', () => { ...@@ -27,21 +27,24 @@ describe('Multi-file store file actions', () => {
store.state.entries[localFile.path] = localFile; store.state.entries[localFile.path] = localFile;
}); });
it('closes open files', (done) => { it('closes open files', done => {
store.dispatch('closeFile', localFile.path) store
.dispatch('closeFile', localFile.path)
.then(() => { .then(() => {
expect(localFile.opened).toBeFalsy(); expect(localFile.opened).toBeFalsy();
expect(localFile.active).toBeFalsy(); expect(localFile.active).toBeFalsy();
expect(store.state.openFiles.length).toBe(0); expect(store.state.openFiles.length).toBe(0);
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('closes file even if file has changes', (done) => { it('closes file even if file has changes', done => {
store.state.changedFiles.push(localFile); store.state.changedFiles.push(localFile);
store.dispatch('closeFile', localFile.path) store
.dispatch('closeFile', localFile.path)
.then(Vue.nextTick) .then(Vue.nextTick)
.then(() => { .then(() => {
expect(store.state.openFiles.length).toBe(0); expect(store.state.openFiles.length).toBe(0);
...@@ -51,6 +54,26 @@ describe('Multi-file store file actions', () => { ...@@ -51,6 +54,26 @@ describe('Multi-file store file actions', () => {
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('closes file & opens next available file', done => {
const f = {
...file('newOpenFile'),
url: '/newOpenFile',
};
store.state.openFiles.push(f);
store.state.entries[f.path] = f;
store
.dispatch('closeFile', localFile.path)
.then(Vue.nextTick)
.then(() => {
expect(router.push).toHaveBeenCalledWith(`/project${f.url}`);
done();
})
.catch(done.fail);
});
}); });
describe('setFileActive', () => { describe('setFileActive', () => {
...@@ -72,58 +95,68 @@ describe('Multi-file store file actions', () => { ...@@ -72,58 +95,68 @@ describe('Multi-file store file actions', () => {
store._actions.scrollToTab = oldScrollToTab; // eslint-disable-line store._actions.scrollToTab = oldScrollToTab; // eslint-disable-line
}); });
it('calls scrollToTab', (done) => { it('calls scrollToTab', done => {
store.dispatch('setFileActive', localFile.path) store
.dispatch('setFileActive', localFile.path)
.then(() => { .then(() => {
expect(scrollToTabSpy).toHaveBeenCalled(); expect(scrollToTabSpy).toHaveBeenCalled();
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets the file active', (done) => { it('sets the file active', done => {
store.dispatch('setFileActive', localFile.path) store
.dispatch('setFileActive', localFile.path)
.then(() => { .then(() => {
expect(localFile.active).toBeTruthy(); expect(localFile.active).toBeTruthy();
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('returns early if file is already active', (done) => { it('returns early if file is already active', done => {
localFile.active = true; localFile.active = true;
store.dispatch('setFileActive', localFile.path) store
.dispatch('setFileActive', localFile.path)
.then(() => { .then(() => {
expect(scrollToTabSpy).not.toHaveBeenCalled(); expect(scrollToTabSpy).not.toHaveBeenCalled();
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets current active file to not active', (done) => { it('sets current active file to not active', done => {
const f = file('newActive'); const f = file('newActive');
store.state.entries[f.path] = f; store.state.entries[f.path] = f;
localFile.active = true; localFile.active = true;
store.state.openFiles.push(localFile); store.state.openFiles.push(localFile);
store.dispatch('setFileActive', f.path) store
.dispatch('setFileActive', f.path)
.then(() => { .then(() => {
expect(localFile.active).toBeFalsy(); expect(localFile.active).toBeFalsy();
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('resets location.hash for line highlighting', (done) => { it('resets location.hash for line highlighting', done => {
location.hash = 'test'; location.hash = 'test';
store.dispatch('setFileActive', localFile.path) store
.dispatch('setFileActive', localFile.path)
.then(() => { .then(() => {
expect(location.hash).not.toBe('test'); expect(location.hash).not.toBe('test');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
}); });
...@@ -131,70 +164,83 @@ describe('Multi-file store file actions', () => { ...@@ -131,70 +164,83 @@ describe('Multi-file store file actions', () => {
let localFile; let localFile;
beforeEach(() => { beforeEach(() => {
spyOn(service, 'getFileData').and.returnValue(Promise.resolve({ spyOn(service, 'getFileData').and.returnValue(
headers: { Promise.resolve({
'page-title': 'testing getFileData', headers: {
}, 'page-title': 'testing getFileData',
json: () => Promise.resolve({ },
blame_path: 'blame_path', json: () =>
commits_path: 'commits_path', Promise.resolve({
permalink: 'permalink', blame_path: 'blame_path',
raw_path: 'raw_path', commits_path: 'commits_path',
binary: false, permalink: 'permalink',
html: '123', raw_path: 'raw_path',
render_error: '', binary: false,
html: '123',
render_error: '',
}),
}), }),
})); );
localFile = file(`newCreate-${Math.random()}`); localFile = file(`newCreate-${Math.random()}`);
localFile.url = 'getFileDataURL'; localFile.url = 'getFileDataURL';
store.state.entries[localFile.path] = localFile; store.state.entries[localFile.path] = localFile;
}); });
it('calls the service', (done) => { it('calls the service', done => {
store.dispatch('getFileData', localFile) store
.dispatch('getFileData', localFile)
.then(() => { .then(() => {
expect(service.getFileData).toHaveBeenCalledWith('getFileDataURL'); expect(service.getFileData).toHaveBeenCalledWith('getFileDataURL');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets the file data', (done) => { it('sets the file data', done => {
store.dispatch('getFileData', localFile) store
.dispatch('getFileData', localFile)
.then(() => { .then(() => {
expect(localFile.blamePath).toBe('blame_path'); expect(localFile.blamePath).toBe('blame_path');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets document title', (done) => { it('sets document title', done => {
store.dispatch('getFileData', localFile) store
.dispatch('getFileData', localFile)
.then(() => { .then(() => {
expect(document.title).toBe('testing getFileData'); expect(document.title).toBe('testing getFileData');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('sets the file as active', (done) => { it('sets the file as active', done => {
store.dispatch('getFileData', localFile) store
.dispatch('getFileData', localFile)
.then(() => { .then(() => {
expect(localFile.active).toBeTruthy(); expect(localFile.active).toBeTruthy();
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('adds the file to open files', (done) => { it('adds the file to open files', done => {
store.dispatch('getFileData', localFile) store
.dispatch('getFileData', localFile)
.then(() => { .then(() => {
expect(store.state.openFiles.length).toBe(1); expect(store.state.openFiles.length).toBe(1);
expect(store.state.openFiles[0].name).toBe(localFile.name); expect(store.state.openFiles[0].name).toBe(localFile.name);
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
}); });
...@@ -208,22 +254,26 @@ describe('Multi-file store file actions', () => { ...@@ -208,22 +254,26 @@ describe('Multi-file store file actions', () => {
store.state.entries[tmpFile.path] = tmpFile; store.state.entries[tmpFile.path] = tmpFile;
}); });
it('calls getRawFileData service method', (done) => { it('calls getRawFileData service method', done => {
store.dispatch('getRawFileData', tmpFile) store
.dispatch('getRawFileData', tmpFile)
.then(() => { .then(() => {
expect(service.getRawFileData).toHaveBeenCalledWith(tmpFile); expect(service.getRawFileData).toHaveBeenCalledWith(tmpFile);
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('updates file raw data', (done) => { it('updates file raw data', done => {
store.dispatch('getRawFileData', tmpFile) store
.dispatch('getRawFileData', tmpFile)
.then(() => { .then(() => {
expect(tmpFile.raw).toBe('raw'); expect(tmpFile.raw).toBe('raw');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
}); });
...@@ -235,60 +285,72 @@ describe('Multi-file store file actions', () => { ...@@ -235,60 +285,72 @@ describe('Multi-file store file actions', () => {
store.state.entries[tmpFile.path] = tmpFile; store.state.entries[tmpFile.path] = tmpFile;
}); });
it('updates file content', (done) => { it('updates file content', done => {
store.dispatch('changeFileContent', { store
path: tmpFile.path, .dispatch('changeFileContent', {
content: 'content', path: tmpFile.path,
}) content: 'content',
.then(() => { })
expect(tmpFile.content).toBe('content'); .then(() => {
expect(tmpFile.content).toBe('content');
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('adds file into changedFiles array', (done) => { it('adds file into changedFiles array', done => {
store.dispatch('changeFileContent', { store
path: tmpFile.path, .dispatch('changeFileContent', {
content: 'content', path: tmpFile.path,
}) content: 'content',
.then(() => { })
expect(store.state.changedFiles.length).toBe(1); .then(() => {
expect(store.state.changedFiles.length).toBe(1);
done(); done();
}).catch(done.fail); })
.catch(done.fail);
}); });
it('adds file once into changedFiles array', (done) => { it('adds file once into changedFiles array', done => {
store.dispatch('changeFileContent', { store
path: tmpFile.path, .dispatch('changeFileContent', {
content: 'content', path: tmpFile.path,
}) content: 'content',
.then(() => store.dispatch('changeFileContent', { })
path: tmpFile.path, .then(() =>
content: 'content 123', store.dispatch('changeFileContent', {
})) path: tmpFile.path,
.then(() => { content: 'content 123',
expect(store.state.changedFiles.length).toBe(1); }),
)
done(); .then(() => {
}).catch(done.fail); expect(store.state.changedFiles.length).toBe(1);
done();
})
.catch(done.fail);
}); });
it('removes file from changedFiles array if not changed', (done) => { it('removes file from changedFiles array if not changed', done => {
store.dispatch('changeFileContent', { store
path: tmpFile.path, .dispatch('changeFileContent', {
content: 'content', path: tmpFile.path,
}) content: 'content',
.then(() => store.dispatch('changeFileContent', { })
path: tmpFile.path, .then(() =>
content: '', store.dispatch('changeFileContent', {
})) path: tmpFile.path,
.then(() => { content: '',
expect(store.state.changedFiles.length).toBe(0); }),
)
done(); .then(() => {
}).catch(done.fail); expect(store.state.changedFiles.length).toBe(0);
done();
})
.catch(done.fail);
}); });
}); });
...@@ -305,51 +367,55 @@ describe('Multi-file store file actions', () => { ...@@ -305,51 +367,55 @@ describe('Multi-file store file actions', () => {
store.state.entries[tmpFile.path] = tmpFile; store.state.entries[tmpFile.path] = tmpFile;
}); });
it('resets file content', (done) => { it('resets file content', done => {
store.dispatch('discardFileChanges', tmpFile.path) store
.then(() => { .dispatch('discardFileChanges', tmpFile.path)
expect(tmpFile.content).not.toBe('testing'); .then(() => {
expect(tmpFile.content).not.toBe('testing');
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('removes file from changedFiles array', (done) => { it('removes file from changedFiles array', done => {
store.dispatch('discardFileChanges', tmpFile.path) store
.then(() => { .dispatch('discardFileChanges', tmpFile.path)
expect(store.state.changedFiles.length).toBe(0); .then(() => {
expect(store.state.changedFiles.length).toBe(0);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('closes temp file', (done) => { it('closes temp file', done => {
tmpFile.tempFile = true; tmpFile.tempFile = true;
tmpFile.opened = true; tmpFile.opened = true;
store.dispatch('discardFileChanges', tmpFile.path) store
.then(() => { .dispatch('discardFileChanges', tmpFile.path)
expect(tmpFile.opened).toBeFalsy(); .then(() => {
expect(tmpFile.opened).toBeFalsy();
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
it('does not re-open a closed temp file', (done) => { it('does not re-open a closed temp file', done => {
tmpFile.tempFile = true; tmpFile.tempFile = true;
expect(tmpFile.opened).toBeFalsy(); expect(tmpFile.opened).toBeFalsy();
store.dispatch('discardFileChanges', tmpFile.path) store
.then(() => { .dispatch('discardFileChanges', tmpFile.path)
expect(tmpFile.opened).toBeFalsy(); .then(() => {
expect(tmpFile.opened).toBeFalsy();
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
}); });
}); });
}); });
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