Commit 5f54aa75 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '24629' into 'master'

Highlight currently focused/viewed file in file tree

See merge request gitlab-org/gitlab!27703
parents 7d8ed431 b3f45856
...@@ -58,6 +58,9 @@ export default { ...@@ -58,6 +58,9 @@ export default {
hasDiff() { hasDiff() {
return hasDiff(this.file); return hasDiff(this.file);
}, },
isActive() {
return this.currentDiffFileId === this.file.file_hash;
},
isFileTooLarge() { isFileTooLarge() {
return this.file.viewer.error === diffViewerErrors.too_large; return this.file.viewer.error === diffViewerErrors.too_large;
}, },
...@@ -143,7 +146,7 @@ export default { ...@@ -143,7 +146,7 @@ export default {
<div <div
:id="file.file_hash" :id="file.file_hash"
:class="{ :class="{
'is-active': currentDiffFileId === file.file_hash, 'is-active': isActive,
}" }"
class="diff-file file-holder" class="diff-file file-holder"
> >
...@@ -153,6 +156,7 @@ export default { ...@@ -153,6 +156,7 @@ export default {
:collapsible="true" :collapsible="true"
:expanded="!isCollapsed" :expanded="!isCollapsed"
:add-merge-request-buttons="true" :add-merge-request-buttons="true"
:is-active="isActive"
class="js-file-title file-title" class="js-file-title file-title"
@toggleFile="handleToggle" @toggleFile="handleToggle"
@showForkMessage="showForkMessage" @showForkMessage="showForkMessage"
......
...@@ -55,6 +55,11 @@ export default { ...@@ -55,6 +55,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
isActive: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']), ...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']),
...@@ -158,6 +163,9 @@ export default { ...@@ -158,6 +163,9 @@ export default {
<div <div
ref="header" ref="header"
class="js-file-title file-title file-title-flex-parent" class="js-file-title file-title file-title-flex-parent"
:class="{
'is-active': isActive,
}"
@click.self="handleToggleFile" @click.self="handleToggleFile"
> >
<div class="file-header-content"> <div class="file-header-content">
......
...@@ -26,7 +26,7 @@ export default { ...@@ -26,7 +26,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState('diffs', ['tree', 'renderTreeList']), ...mapState('diffs', ['tree', 'renderTreeList', 'currentDiffFileId', 'viewedDiffFileIds']),
...mapGetters('diffs', ['allBlobs']), ...mapGetters('diffs', ['allBlobs']),
filteredTreeList() { filteredTreeList() {
const search = this.search.toLowerCase().trim(); const search = this.search.toLowerCase().trim();
...@@ -96,6 +96,8 @@ export default { ...@@ -96,6 +96,8 @@ export default {
:level="0" :level="0"
:hide-file-stats="hideFileStats" :hide-file-stats="hideFileStats"
:file-row-component="$options.DiffFileRow" :file-row-component="$options.DiffFileRow"
:active-file="currentDiffFileId"
:viewed-files="viewedDiffFileIds"
@toggleTreeOpen="toggleTreeOpen" @toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile" @clickFile="scrollToFile"
/> />
......
...@@ -26,6 +26,7 @@ export default () => ({ ...@@ -26,6 +26,7 @@ export default () => ({
showTreeList: true, showTreeList: true,
currentDiffFileId: '', currentDiffFileId: '',
projectPath: '', projectPath: '',
viewedDiffFileIds: [],
commentForms: [], commentForms: [],
highlightedRow: null, highlightedRow: null,
renderTreeList: true, renderTreeList: true,
......
...@@ -284,6 +284,9 @@ export default { ...@@ -284,6 +284,9 @@ export default {
}, },
[types.UPDATE_CURRENT_DIFF_FILE_ID](state, fileId) { [types.UPDATE_CURRENT_DIFF_FILE_ID](state, fileId) {
state.currentDiffFileId = fileId; state.currentDiffFileId = fileId;
if (!state.viewedDiffFileIds.includes(fileId)) {
state.viewedDiffFileIds = [fileId, ...state.viewedDiffFileIds];
}
}, },
[types.OPEN_DIFF_FILE_COMMENT_FORM](state, formData) { [types.OPEN_DIFF_FILE_COMMENT_FORM](state, formData) {
state.commentForms.push({ state.commentForms.push({
......
...@@ -18,6 +18,16 @@ export default { ...@@ -18,6 +18,16 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
activeFile: {
type: String,
required: false,
default: '',
},
viewedFiles: {
type: Array,
required: false,
default: () => [],
},
}, },
computed: { computed: {
isTree() { isTree() {
...@@ -34,8 +44,8 @@ export default { ...@@ -34,8 +44,8 @@ export default {
fileClass() { fileClass() {
return { return {
'file-open': this.isBlob && this.file.opened, 'file-open': this.isBlob && this.file.opened,
'is-active': this.isBlob && this.file.active, 'is-active': this.isBlob && (this.file.active || this.activeFile === this.file.fileHash),
folder: this.isTree, 'is-viewed': this.isBlob && this.viewedFiles.includes(this.file.fileHash),
'is-open': this.file.opened, 'is-open': this.file.opened,
}; };
}, },
...@@ -107,15 +117,23 @@ export default { ...@@ -107,15 +117,23 @@ export default {
v-else v-else
:class="fileClass" :class="fileClass"
:title="file.name" :title="file.name"
class="file-row" class="file-row text-left px-1 py-2 ml-n2 d-flex align-items-center"
role="button" role="button"
@click="clickFile" @click="clickFile"
@mouseleave="$emit('mouseleave', $event)" @mouseleave="$emit('mouseleave', $event)"
> >
<div class="file-row-name-container"> <div class="file-row-name-container w-100 d-flex align-items-center">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated"> <span
ref="textOutput"
:style="levelIndentation"
class="file-row-name str-truncated d-inline-block"
:class="[
{ 'folder font-weight-normal': isTree },
fileClass['is-viewed'] ? 'font-weight-normal' : 'font-weight-bold',
]"
>
<file-icon <file-icon
class="file-row-icon" class="file-row-icon align-middle mr-1"
:class="{ 'text-secondary': file.type === 'tree' }" :class="{ 'text-secondary': file.type === 'tree' }"
:file-name="file.name" :file-name="file.name"
:loading="file.loading" :loading="file.loading"
...@@ -132,14 +150,8 @@ export default { ...@@ -132,14 +150,8 @@ export default {
<style> <style>
.file-row { .file-row {
display: flex;
align-items: center;
height: 32px; height: 32px;
padding: 4px 8px;
margin-left: -8px;
margin-right: -8px;
border-radius: 3px; border-radius: 3px;
text-align: left;
cursor: pointer; cursor: pointer;
} }
...@@ -157,24 +169,15 @@ export default { ...@@ -157,24 +169,15 @@ export default {
} }
.file-row-name-container { .file-row-name-container {
display: flex;
width: 100%;
align-items: center;
overflow: visible; overflow: visible;
} }
.file-row-name { .file-row-name {
display: inline-block;
flex: 1; flex: 1;
max-width: inherit; max-width: inherit;
height: 19px; height: 20px;
line-height: 16px; line-height: 16px;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.file-row-name .file-row-icon {
margin-right: 2px;
vertical-align: middle;
}
</style> </style>
...@@ -69,6 +69,11 @@ ...@@ -69,6 +69,11 @@
} }
} }
.file-title.is-active,
.file-title-flex-parent.is-active {
background-color: $gray-200;
}
@media (min-width: map-get($grid-breakpoints, md)) { @media (min-width: map-get($grid-breakpoints, md)) {
&.conflict .file-title, &.conflict .file-title,
&.conflict .file-title-flex-parent { &.conflict .file-title-flex-parent {
......
---
title: Highlight currently focused/viewed file in file tree
merge_request: 27703
author:
type: changed
...@@ -61,6 +61,7 @@ describe('DiffFileHeader component', () => { ...@@ -61,6 +61,7 @@ describe('DiffFileHeader component', () => {
const findTitleLink = () => wrapper.find({ ref: 'titleWrapper' }); const findTitleLink = () => wrapper.find({ ref: 'titleWrapper' });
const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' }); const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' });
const findFileActions = () => wrapper.find('.file-actions'); const findFileActions = () => wrapper.find('.file-actions');
const findActiveHeader = () => wrapper.find('.is-active');
const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' }); const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' });
const findLfsLabel = () => wrapper.find('.label-lfs'); const findLfsLabel = () => wrapper.find('.label-lfs');
const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' }); const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' });
...@@ -143,6 +144,11 @@ describe('DiffFileHeader component', () => { ...@@ -143,6 +144,11 @@ describe('DiffFileHeader component', () => {
expect(wrapper.find(ClipboardButton).exists()).toBe(true); expect(wrapper.find(ClipboardButton).exists()).toBe(true);
}); });
it('contains a active header class if this is the active file header', () => {
createComponent({ isActive: true });
expect(findActiveHeader().exists()).toBe(true);
});
describe('for submodule', () => { describe('for submodule', () => {
const submoduleDiffFile = { const submoduleDiffFile = {
...diffFile, ...diffFile,
......
...@@ -72,6 +72,19 @@ describe('File row component', () => { ...@@ -72,6 +72,19 @@ describe('File row component', () => {
}); });
}); });
it('is marked as viewed if clicked', () => {
createComponent({
file: {
...file(),
type: 'blob',
fileHash: '#123456789',
},
level: 0,
viewedFiles: ['#123456789'],
});
expect(wrapper.classes()).toContain('is-viewed');
});
it('indents row based on level', () => { it('indents row based on level', () => {
createComponent({ createComponent({
file: file('t4'), file: file('t4'),
......
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