Commit c269a1b4 authored by Bryce Johnson's avatar Bryce Johnson Committed by Eric Eastwood

Repo Editor Fixes

Conflicts:
	app/assets/javascripts/repo/components/repo_edit_button.vue
	app/assets/javascripts/repo/components/repo_editor.vue
	app/assets/javascripts/repo/components/repo_tab.vue
	app/assets/javascripts/repo/components/repo_tabs.vue
	app/assets/javascripts/repo/helpers/repo_helper.js
	app/assets/javascripts/repo/services/repo_service.js
	app/assets/javascripts/vue_shared/components/popup_dialog.vue
	app/assets/stylesheets/pages/repo.scss
	app/views/shared/repo/_repo.html.haml
	spec/javascripts/repo/components/repo_edit_button_spec.js
parent f034190e
...@@ -142,7 +142,7 @@ import Cookies from 'js-cookie'; ...@@ -142,7 +142,7 @@ import Cookies from 'js-cookie';
var action = $form.attr('action'); var action = $form.attr('action');
var divider = action.indexOf('?') === -1 ? '?' : '&'; var divider = action.indexOf('?') === -1 ? '?' : '&';
if (shouldVisit) { if (shouldVisit) {
gl.utils.visitUrl(action + '' + divider + '' + $form.serialize()); gl.utils.visitUrl(`${action}${divider}${$form.serialize()}`);
} }
} }
} }
......
...@@ -4,7 +4,7 @@ import Store from '../stores/repo_store'; ...@@ -4,7 +4,7 @@ import Store from '../stores/repo_store';
import RepoMixin from '../mixins/repo_mixin'; import RepoMixin from '../mixins/repo_mixin';
import Service from '../services/repo_service'; import Service from '../services/repo_service';
const RepoCommitSection = { export default {
data: () => Store, data: () => Store,
mixins: [RepoMixin], mixins: [RepoMixin],
...@@ -54,8 +54,6 @@ const RepoCommitSection = { ...@@ -54,8 +54,6 @@ const RepoCommitSection = {
}, },
}, },
}; };
export default RepoCommitSection;
</script> </script>
<template> <template>
......
...@@ -26,12 +26,15 @@ export default { ...@@ -26,12 +26,15 @@ export default {
this.editMode = !this.editMode; this.editMode = !this.editMode;
Store.toggleBlobView(); Store.toggleBlobView();
}, },
toggleProjectRefsForm() {
$('.project-refs-form').toggleClass('disabled', this.editMode);
$('.js-tree-ref-target-holder').toggle(this.editMode);
},
}, },
watch: { watch: {
editMode() { editMode() {
$('.project-refs-form').toggleClass('disabled', this.editMode); this.toggleProjectRefsForm();
$('.js-tree-ref-target-holder').toggle(this.editMode);
}, },
}, },
}; };
......
...@@ -74,10 +74,6 @@ const RepoEditor = { ...@@ -74,10 +74,6 @@ const RepoEditor = {
}, },
watch: { watch: {
activeFileLabel() {
this.showHide();
},
dialog: { dialog: {
handler(obj) { handler(obj) {
const newObj = obj; const newObj = obj;
...@@ -100,29 +96,22 @@ const RepoEditor = { ...@@ -100,29 +96,22 @@ const RepoEditor = {
deep: true, deep: true,
}, },
isTree() {
this.showHide();
},
openedFiles() {
this.showHide();
},
binary() {
this.showHide();
},
blobRaw() { blobRaw() {
if (Helper.monacoInstance && !this.isTree) { if (Helper.monacoInstance && !this.isTree) {
this.setupEditor(); this.setupEditor();
} }
}, },
}, },
computed: {
shouldHideEditor() {
return !this.openedFiles.length || (this.binary && !this.activeFile.raw);
},
},
}; };
export default RepoEditor; export default RepoEditor;
</script> </script>
<template> <template>
<div id="ide"></div> <div id="ide" v-if='!shouldHideEditor'></div>
</template> </template>
...@@ -8,7 +8,7 @@ import RepoFile from './repo_file.vue'; ...@@ -8,7 +8,7 @@ import RepoFile from './repo_file.vue';
import RepoLoadingFile from './repo_loading_file.vue'; import RepoLoadingFile from './repo_loading_file.vue';
import RepoMixin from '../mixins/repo_mixin'; import RepoMixin from '../mixins/repo_mixin';
const RepoSidebar = { export default {
mixins: [RepoMixin], mixins: [RepoMixin],
components: { components: {
'repo-file-options': RepoFileOptions, 'repo-file-options': RepoFileOptions,
...@@ -59,8 +59,6 @@ const RepoSidebar = { ...@@ -59,8 +59,6 @@ const RepoSidebar = {
}, },
}, },
}; };
export default RepoSidebar;
</script> </script>
<template> <template>
......
...@@ -28,9 +28,9 @@ const RepoTab = { ...@@ -28,9 +28,9 @@ const RepoTab = {
methods: { methods: {
tabClicked: Store.setActiveFiles, tabClicked: Store.setActiveFiles,
xClicked(file) { closeTab(file) {
if (file.changed) return; if (file.changed) return;
this.$emit('xclicked', file); this.$emit('tabclosed', file);
}, },
}, },
}; };
...@@ -43,7 +43,7 @@ export default RepoTab; ...@@ -43,7 +43,7 @@ export default RepoTab;
<a <a
href="#0" href="#0"
class="close" class="close"
@click.stop.prevent="xClicked(tab)" @click.stop.prevent="closeTab(tab)"
:aria-label="closeLabel"> :aria-label="closeLabel">
<i <i
class="fa" class="fa"
......
...@@ -13,7 +13,7 @@ const RepoTabs = { ...@@ -13,7 +13,7 @@ const RepoTabs = {
data: () => Store, data: () => Store,
methods: { methods: {
xClicked(file) { tabClosed(file) {
Store.removeFromOpenedFiles(file); Store.removeFromOpenedFiles(file);
}, },
}, },
...@@ -23,9 +23,14 @@ export default RepoTabs; ...@@ -23,9 +23,14 @@ export default RepoTabs;
</script> </script>
<template> <template>
<ul <ul id="tabs">
id="tabs"> <repo-tab
<repo-tab v-for="tab in openedFiles" :key="tab.id" :tab="tab" :class="{'active' : tab.active}" @xclicked="xClicked"/> v-for="tab in openedFiles"
:key="tab.id"
:tab="tab"
:class="{'active' : tab.active}"
@tabclosed="tabClosed"
/>
<li class="tabs-divider" /> <li class="tabs-divider" />
</ul> </ul>
</template> </template>
...@@ -64,7 +64,7 @@ const RepoHelper = { ...@@ -64,7 +64,7 @@ const RepoHelper = {
file.opened = true; file.opened = true;
file.icon = 'fa-folder-open'; file.icon = 'fa-folder-open';
RepoHelper.toURL(file.url, file.name); RepoHelper.updateHistoryEntry(file.url, file.name);
return file; return file;
}, },
...@@ -247,7 +247,7 @@ const RepoHelper = { ...@@ -247,7 +247,7 @@ const RepoHelper = {
return RepoHelper.Time.now().toFixed(3); return RepoHelper.Time.now().toFixed(3);
}, },
toURL(url, title) { updateHistoryEntry(url, title) {
const history = window.history; const history = window.history;
RepoHelper.key = RepoHelper.genKey(); RepoHelper.key = RepoHelper.genKey();
......
...@@ -45,6 +45,9 @@ function initRepo(el) { ...@@ -45,6 +45,9 @@ function initRepo(el) {
components: { components: {
repo: Repo, repo: Repo,
}, },
render(createElement) {
return createElement('repo');
},
}); });
} }
......
...@@ -84,7 +84,7 @@ const RepoStore = { ...@@ -84,7 +84,7 @@ const RepoStore = {
}).catch(Helper.loadingError); }).catch(Helper.loadingError);
} }
if (!file.loading) Helper.toURL(file.url, file.name); if (!file.loading) Helper.updateHistoryEntry(file.url, file.name);
RepoStore.binary = file.binary; RepoStore.binary = file.binary;
}, },
......
<script> <script>
const PopupDialog = { export default {
name: 'popup-dialog', name: 'popup-dialog',
props: { props: {
title: String, title: {
body: String, type: String,
required: true,
},
body: {
type: String,
required: true,
},
kind: { kind: {
type: String, type: String,
required: false,
default: 'primary', default: 'primary',
}, },
closeButtonLabel: { closeButtonLabel: {
type: String, type: String,
required: false,
default: 'Cancel', default: 'Cancel',
}, },
primaryButtonLabel: { primaryButtonLabel: {
type: String, type: String,
default: 'Save changes', required: true,
}, },
}, },
computed: { computed: {
typeOfClass() { btnKindClass() {
const className = `btn-${this.kind}`; return {
const returnObj = {}; [`btn-${this.kind}`]: true,
returnObj[className] = true; };
return returnObj;
}, },
}, },
...@@ -32,33 +39,48 @@ const PopupDialog = { ...@@ -32,33 +39,48 @@ const PopupDialog = {
close() { close() {
this.$emit('toggle', false); this.$emit('toggle', false);
}, },
emitSubmit(status) {
yesClick() { this.$emit('submit', status);
this.$emit('submit', true);
},
noClick() {
this.$emit('submit', false);
}, },
}, },
}; };
export default PopupDialog;
</script> </script>
<template> <template>
<div class="modal popup-dialog" tabindex="-1" v-show="open" role="dialog"> <div
<div class="modal-dialog" role="document"> class="modal popup-dialog"
role="dialog"
tabindex="-1">
<div
class="modal-dialog"
role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" @click="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button"
class="close"
@click="close"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">{{this.title}}</h4> <h4 class="modal-title">{{this.title}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>{{this.body}}</p> <p>{{this.body}}</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" @click="noClick">{{closeButtonLabel}}</button> <button
<button type="button" class="btn" :class="typeOfClass" @click="yesClick">{{primaryButtonLabel}}</button> type="button"
class="btn btn-default"
@click="emitSubmit(false)">
{{closeButtonLabel}}
</button>
<button
type="button"
class="btn"
:class="btnKindClass"
@click="emitSubmit(true)">
{{primaryButtonLabel}}
</button>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -28,11 +28,6 @@ ...@@ -28,11 +28,6 @@
.project-refs-form, .project-refs-form,
.project-refs-target-form { .project-refs-target-form {
display: inline-block; display: inline-block;
&.disabled {
opacity: 0.5;
pointer-events: none;
}
} }
.fade-enter, .fade-enter,
...@@ -134,7 +129,6 @@ ...@@ -134,7 +129,6 @@
a { a {
@include str-truncated(100px); @include str-truncated(100px);
color: $black; color: $black;
display: inline-block;
width: 100px; width: 100px;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
......
#repo{ data: { url: content_url, project_name: project.name, refs_url: refs_project_path(project, format: :json), project_url: project_path(project), project_id: project.id, can_commit: (!!can_push_branch?(project, @ref)).to_s, on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s } } #repo{ data: { url: content_url,
%repo project_name: project.name,
refs_url: refs_project_path(project, format: :json),
project_url: project_path(project),
project_id: project.id,
can_commit: (!!can_push_branch?(project, @ref)).to_s,
on_top_of_branch: (!!on_top_of_branch?(project, @ref)).to_s } }
...@@ -21,11 +21,13 @@ describe('RepoEditButton', () => { ...@@ -21,11 +21,13 @@ describe('RepoEditButton', () => {
expect(vm.$el.textContent).toMatch('Edit'); expect(vm.$el.textContent).toMatch('Edit');
spyOn(vm, 'editCancelClicked').and.callThrough(); spyOn(vm, 'editCancelClicked').and.callThrough();
spyOn(vm, 'toggleProjectRefsForm');
vm.$el.click(); vm.$el.click();
Vue.nextTick(() => { Vue.nextTick(() => {
expect(vm.editCancelClicked).toHaveBeenCalled(); expect(vm.editCancelClicked).toHaveBeenCalled();
expect(vm.toggleProjectRefsForm).toHaveBeenCalled();
expect(vm.$el.textContent).toMatch('Cancel edit'); expect(vm.$el.textContent).toMatch('Cancel edit');
done(); done();
}); });
......
import Vue from 'vue'; import Vue from 'vue';
import repoEditor from '~/repo/components/repo_editor.vue'; import repoEditor from '~/repo/components/repo_editor.vue';
import RepoStore from '~/repo/stores/repo_store';
describe('RepoEditor', () => { describe('RepoEditor', () => {
function createComponent() { beforeEach(() => {
const RepoEditor = Vue.extend(repoEditor); const RepoEditor = Vue.extend(repoEditor);
return new RepoEditor().$mount(); this.vm = new RepoEditor().$mount();
} });
it('renders an ide container', (done) => {
this.vm.openedFiles = ['idiidid'];
this.vm.binary = false;
it('renders an ide container', () => { Vue.nextTick(() => {
const monacoInstance = jasmine.createSpyObj('monacoInstance', ['onMouseUp', 'onKeyUp', 'setModel', 'updateOptions']); expect(this.vm.shouldHideEditor).toBe(false);
const monaco = { expect(this.vm.$el.id).toEqual('ide');
editor: jasmine.createSpyObj('editor', ['create']), expect(this.vm.$el.tagName).toBe('DIV');
}; done();
RepoStore.monaco = monaco; });
});
monaco.editor.create.and.returnValue(monacoInstance); describe('when there are no open files', () => {
spyOn(repoEditor.watch, 'blobRaw'); it('does not render the ide', (done) => {
this.vm.openedFiles = [];
Vue.nextTick(() => {
expect(this.vm.shouldHideEditor).toBe(true);
expect(this.vm.$el.tagName).not.toBeDefined();
done();
});
});
});
const vm = createComponent(); describe('when open file is binary and not raw', () => {
it('does not render the IDE', (done) => {
this.vm.binary = true;
this.vm.activeFile = {
raw: false,
};
expect(vm.$el.id).toEqual('ide'); Vue.nextTick(() => {
expect(this.vm.shouldHideEditor).toBe(true);
expect(this.vm.$el.tagName).not.toBeDefined();
done();
});
});
}); });
}); });
...@@ -23,6 +23,7 @@ describe('RepoFileButtons', () => { ...@@ -23,6 +23,7 @@ describe('RepoFileButtons', () => {
RepoStore.activeFile = activeFile; RepoStore.activeFile = activeFile;
RepoStore.activeFileLabel = activeFileLabel; RepoStore.activeFileLabel = activeFileLabel;
RepoStore.editMode = true; RepoStore.editMode = true;
RepoStore.binary = false;
const vm = createComponent(); const vm = createComponent();
const raw = vm.$el.querySelector('.raw'); const raw = vm.$el.querySelector('.raw');
......
...@@ -21,7 +21,7 @@ describe('RepoTab', () => { ...@@ -21,7 +21,7 @@ describe('RepoTab', () => {
const close = vm.$el.querySelector('.close'); const close = vm.$el.querySelector('.close');
const name = vm.$el.querySelector(`a[title="${tab.url}"]`); const name = vm.$el.querySelector(`a[title="${tab.url}"]`);
spyOn(vm, 'xClicked'); spyOn(vm, 'closeTab');
spyOn(vm, 'tabClicked'); spyOn(vm, 'tabClicked');
expect(close.querySelector('.fa-times')).toBeTruthy(); expect(close.querySelector('.fa-times')).toBeTruthy();
...@@ -30,7 +30,7 @@ describe('RepoTab', () => { ...@@ -30,7 +30,7 @@ describe('RepoTab', () => {
close.click(); close.click();
name.click(); name.click();
expect(vm.xClicked).toHaveBeenCalledWith(tab); expect(vm.closeTab).toHaveBeenCalledWith(tab);
expect(vm.tabClicked).toHaveBeenCalledWith(tab); expect(vm.tabClicked).toHaveBeenCalledWith(tab);
}); });
...@@ -48,22 +48,22 @@ describe('RepoTab', () => { ...@@ -48,22 +48,22 @@ describe('RepoTab', () => {
}); });
describe('methods', () => { describe('methods', () => {
describe('xClicked', () => { describe('closeTab', () => {
const vm = jasmine.createSpyObj('vm', ['$emit']); const vm = jasmine.createSpyObj('vm', ['$emit']);
it('returns undefined and does not $emit if file is changed', () => { it('returns undefined and does not $emit if file is changed', () => {
const file = { changed: true }; const file = { changed: true };
const returnVal = repoTab.methods.xClicked.call(vm, file); const returnVal = repoTab.methods.closeTab.call(vm, file);
expect(returnVal).toBeUndefined(); expect(returnVal).toBeUndefined();
expect(vm.$emit).not.toHaveBeenCalled(); expect(vm.$emit).not.toHaveBeenCalled();
}); });
it('$emits xclicked event with file obj', () => { it('$emits tabclosed event with file obj', () => {
const file = { changed: false }; const file = { changed: false };
repoTab.methods.xClicked.call(vm, file); repoTab.methods.closeTab.call(vm, file);
expect(vm.$emit).toHaveBeenCalledWith('xclicked', file); expect(vm.$emit).toHaveBeenCalledWith('tabclosed', file);
}); });
}); });
}); });
......
...@@ -30,13 +30,13 @@ describe('RepoTabs', () => { ...@@ -30,13 +30,13 @@ describe('RepoTabs', () => {
}); });
describe('methods', () => { describe('methods', () => {
describe('xClicked', () => { describe('tabClosed', () => {
it('calls removeFromOpenedFiles with file obj', () => { it('calls removeFromOpenedFiles with file obj', () => {
const file = {}; const file = {};
spyOn(RepoStore, 'removeFromOpenedFiles'); spyOn(RepoStore, 'removeFromOpenedFiles');
repoTabs.methods.xClicked(file); repoTabs.methods.tabClosed(file);
expect(RepoStore.removeFromOpenedFiles).toHaveBeenCalledWith(file); expect(RepoStore.removeFromOpenedFiles).toHaveBeenCalledWith(file);
}); });
......
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