Read about our upcoming Code of Conduct on this issue

Commit 10e3342a authored by Fatih Acet's avatar Fatih Acet
Browse files

Merge branch 'ide-fix-back-btn' into 'master'

Repo Editor : Fixes Back Button for files + line number jumping for preview and editor

See merge request gitlab-org/gitlab-ce!14508
......@@ -54,12 +54,14 @@ LineHighlighter.prototype.bindEvents = function() {
$fileHolder.on('highlight:line', this.highlightHash);
};
LineHighlighter.prototype.highlightHash = function() {
var range;
LineHighlighter.prototype.highlightHash = function(newHash) {
let range;
if (newHash && typeof newHash === 'string') this._hash = newHash;
this.clearHighlight();
if (this._hash !== '') {
range = this.hashToRange(this._hash);
if (range[0]) {
this.highlightRange(range);
const lineSelector = `#L${range[0]}`;
......
......@@ -63,12 +63,7 @@ const RepoEditor = {
const lineNumber = e.target.position.lineNumber;
if (e.target.element.classList.contains('line-numbers')) {
location.hash = `L${lineNumber}`;
Store.activeLine = lineNumber;
Helper.monacoInstance.setPosition({
lineNumber: this.activeLine,
column: 1,
});
Store.setActiveLine(lineNumber);
}
},
},
......@@ -101,6 +96,15 @@ const RepoEditor = {
this.setupEditor();
}
},
activeLine() {
if (Helper.monacoInstance) {
Helper.monacoInstance.setPosition({
lineNumber: this.activeLine,
column: 1,
});
}
},
},
computed: {
shouldHideEditor() {
......
......@@ -14,6 +14,11 @@ export default {
highlightFile() {
$(this.$el).find('.file-content').syntaxHighlight();
},
highlightLine() {
if (Store.activeLine > -1) {
this.lineHighlighter.highlightHash(`#L${Store.activeLine}`);
}
},
},
mounted() {
this.highlightFile();
......@@ -26,8 +31,12 @@ export default {
html() {
this.$nextTick(() => {
this.highlightFile();
this.highlightLine();
});
},
activeLine() {
this.highlightLine();
},
},
};
</script>
......
......@@ -18,22 +18,40 @@ export default {
},
created() {
this.addPopEventListener();
window.addEventListener('popstate', this.checkHistory);
},
destroyed() {
window.removeEventListener('popstate', this.checkHistory);
},
data: () => Store,
methods: {
addPopEventListener() {
window.addEventListener('popstate', () => {
if (location.href.indexOf('#') > -1) return;
this.linkClicked({
checkHistory() {
let selectedFile = this.files.find(file => location.pathname.indexOf(file.url) > -1);
if (!selectedFile) {
// Maybe it is not in the current tree but in the opened tabs
selectedFile = Helper.getFileFromPath(location.pathname);
}
let lineNumber = null;
if (location.hash.indexOf('#L') > -1) lineNumber = Number(location.hash.substr(2));
if (selectedFile) {
if (selectedFile.url !== this.activeFile.url) {
this.fileClicked(selectedFile, lineNumber);
} else {
Store.setActiveLine(lineNumber);
}
} else {
// Not opened at all lets open new tab
this.fileClicked({
url: location.href,
});
});
}, lineNumber);
}
},
fileClicked(clickedFile) {
fileClicked(clickedFile, lineNumber) {
let file = clickedFile;
if (file.loading) return;
file.loading = true;
......@@ -41,17 +59,20 @@ export default {
if (file.type === 'tree' && file.opened) {
file = Store.removeChildFilesOfTree(file);
file.loading = false;
Store.setActiveLine(lineNumber);
} else {
const openFile = Helper.getFileFromPath(file.url);
if (openFile) {
file.loading = false;
Store.setActiveFiles(openFile);
Store.setActiveLine(lineNumber);
} else {
Service.url = file.url;
Helper.getContent(file)
.then(() => {
file.loading = false;
Helper.scrollTabsRight();
Store.setActiveLine(lineNumber);
})
.catch(Helper.loadingError);
}
......
......@@ -254,7 +254,9 @@ const RepoHelper = {
RepoHelper.key = RepoHelper.genKey();
history.pushState({ key: RepoHelper.key }, '', url);
if (document.location.pathname !== url) {
history.pushState({ key: RepoHelper.key }, '', url);
}
if (title) {
document.title = title;
......
......@@ -26,7 +26,7 @@ const RepoStore = {
},
activeFile: Helper.getDefaultActiveFile(),
activeFileIndex: 0,
activeLine: 0,
activeLine: -1,
activeFileLabel: 'Raw',
files: [],
isCommitable: false,
......@@ -85,6 +85,7 @@ const RepoStore = {
if (!file.loading) Helper.updateHistoryEntry(file.url, file.pageTitle || file.name);
RepoStore.binary = file.binary;
RepoStore.setActiveLine(-1);
},
setFileActivity(file, openedFile, i) {
......@@ -101,6 +102,10 @@ const RepoStore = {
RepoStore.activeFileIndex = i;
},
setActiveLine(activeLine) {
if (!isNaN(activeLine)) RepoStore.activeLine = activeLine;
},
setActiveToRaw() {
RepoStore.activeFile.raw = false;
// can't get vue to listen to raw for some reason so RepoStore for now.
......
......@@ -29,15 +29,17 @@ describe('RepoFile', () => {
}).$mount();
}
beforeEach(() => {
spyOn(repoFile.mixins[0].methods, 'timeFormated').and.returnValue(updated);
});
it('renders link, icon, name and last commit details', () => {
const vm = createComponent({
file,
activeFile,
const RepoFile = Vue.extend(repoFile);
const vm = new RepoFile({
propsData: {
file,
activeFile,
},
});
spyOn(vm, 'timeFormated').and.returnValue(updated);
vm.$mount();
const name = vm.$el.querySelector('.repo-file-name');
const fileIcon = vm.$el.querySelector('.file-icon');
......
......@@ -5,18 +5,26 @@ import RepoStore from '~/repo/stores/repo_store';
import repoSidebar from '~/repo/components/repo_sidebar.vue';
describe('RepoSidebar', () => {
let vm;
function createComponent() {
const RepoSidebar = Vue.extend(repoSidebar);
return new RepoSidebar().$mount();
}
afterEach(() => {
vm.$destroy();
});
it('renders a sidebar', () => {
RepoStore.files = [{
id: 0,
}];
RepoStore.openedFiles = [];
const vm = createComponent();
RepoStore.isRoot = false;
vm = createComponent();
const thead = vm.$el.querySelector('thead');
const tbody = vm.$el.querySelector('tbody');
......@@ -35,7 +43,7 @@ describe('RepoSidebar', () => {
RepoStore.openedFiles = [{
id: 0,
}];
const vm = createComponent();
vm = createComponent();
expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy();
expect(vm.$el.querySelector('thead')).toBeFalsy();
......@@ -47,7 +55,7 @@ describe('RepoSidebar', () => {
tree: true,
};
RepoStore.files = [];
const vm = createComponent();
vm = createComponent();
expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5);
});
......@@ -57,7 +65,7 @@ describe('RepoSidebar', () => {
id: 0,
}];
RepoStore.isRoot = true;
const vm = createComponent();
vm = createComponent();
expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy();
});
......@@ -72,7 +80,7 @@ describe('RepoSidebar', () => {
};
RepoStore.files = [file1];
RepoStore.isRoot = true;
const vm = createComponent();
vm = createComponent();
vm.fileClicked(file1);
......@@ -87,7 +95,7 @@ describe('RepoSidebar', () => {
spyOn(Helper, 'getFileFromPath').and.returnValue(file);
spyOn(RepoStore, 'setActiveFiles');
const vm = createComponent();
vm = createComponent();
vm.fileClicked(file);
expect(RepoStore.setActiveFiles).toHaveBeenCalledWith(file);
......@@ -103,7 +111,7 @@ describe('RepoSidebar', () => {
};
RepoStore.files = [file1];
RepoStore.isRoot = true;
const vm = createComponent();
vm = createComponent();
vm.fileClicked(file1);
......@@ -114,12 +122,48 @@ describe('RepoSidebar', () => {
describe('goToPreviousDirectoryClicked', () => {
it('should hide files in directory if already open', () => {
const prevUrl = 'foo/bar';
const vm = createComponent();
vm = createComponent();
vm.goToPreviousDirectoryClicked(prevUrl);
expect(RepoService.url).toEqual(prevUrl);
});
});
describe('back button', () => {
const file1 = {
id: 1,
url: 'file1',
};
const file2 = {
id: 2,
url: 'file2',
};
RepoStore.files = [file1, file2];
RepoStore.openedFiles = [file1, file2];
RepoStore.isRoot = true;
vm = createComponent();
vm.fileClicked(file1);
it('render previous file when using back button', () => {
spyOn(Helper, 'getContent').and.callThrough();
vm.fileClicked(file2);
expect(Helper.getContent).toHaveBeenCalledWith(file2);
Helper.getContent.calls.reset();
history.pushState({
key: Math.random(),
}, '', file1.url);
const popEvent = document.createEvent('Event');
popEvent.initEvent('popstate', true, true);
window.dispatchEvent(popEvent);
expect(Helper.getContent.calls.mostRecent().args[0].url).toContain(file1.url);
window.history.pushState({}, null, '/');
});
});
});
});
......@@ -31,6 +31,7 @@ describe('MRWidgetService', () => {
});
it('should have methods defined', () => {
window.history.pushState({}, null, '/');
const service = new MRWidgetService(mr);
expect(service.merge()).toBeDefined();
......
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