Commit f57b06ca authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'multi-file-editor-css-fixes' into 'master'

Added IDE commit panel

Closes #40041

See merge request gitlab-org/gitlab-ce!15583
parents 29298dc5 e9c70152
<script>
import icon from '../../../vue_shared/components/icon.vue';
import listItem from './list_item.vue';
import listCollapsed from './list_collapsed.vue';
export default {
components: {
icon,
listItem,
listCollapsed,
},
props: {
title: {
type: String,
required: true,
},
fileList: {
type: Array,
required: true,
},
collapsed: {
type: Boolean,
required: true,
},
},
methods: {
toggleCollapsed() {
this.$emit('toggleCollapsed');
},
},
};
</script>
<template>
<div class="multi-file-commit-panel-section">
<header
class="multi-file-commit-panel-header"
:class="{
'is-collapsed': collapsed,
}"
>
<icon
name="list-bulleted"
:size="18"
css-classes="append-right-default"
/>
<template v-if="!collapsed">
{{ title }}
<button
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-right"
>
</i>
</button>
</template>
</header>
<div class="multi-file-commit-list">
<list-collapsed
v-if="collapsed"
/>
<template v-else>
<ul
v-if="fileList.length"
class="list-unstyled append-bottom-0"
>
<li
v-for="file in fileList"
:key="file.key"
>
<list-item
:file="file"
/>
</li>
</ul>
<div
v-else
class="help-block prepend-top-0"
>
No changes
</div>
</template>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
computed: {
...mapGetters([
'addedFiles',
'modifiedFiles',
]),
},
};
</script>
<template>
<div
class="multi-file-commit-list-collapsed text-center"
>
<icon
name="file-addition"
:size="18"
css-classes="multi-file-addition append-bottom-10"
/>
{{ addedFiles.length }}
<icon
name="file-modified"
:size="18"
css-classes="multi-file-modified prepend-top-10 append-bottom-10"
/>
{{ modifiedFiles.length }}
</div>
</template>
<script>
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
props: {
file: {
type: Object,
required: true,
},
},
computed: {
iconName() {
return this.file.tempFile ? 'file-addition' : 'file-modified';
},
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
},
};
</script>
<template>
<div class="multi-file-commit-list-item">
<icon
:name="iconName"
:size="16"
:css-classes="iconClass"
/>
<span class="multi-file-commit-list-path">
{{ file.path }}
</span>
</div>
</template>
...@@ -40,20 +40,24 @@ export default { ...@@ -40,20 +40,24 @@ export default {
</script> </script>
<template> <template>
<div class="repository-view"> <div
<div class="tree-content-holder" :class="{'tree-content-holder-mini' : isCollapsed}"> class="multi-file"
:class="{
'is-collapsed': isCollapsed
}"
>
<repo-sidebar/> <repo-sidebar/>
<div <div
v-if="isCollapsed" v-if="isCollapsed"
class="panel-right" class="multi-file-edit-pane"
> >
<repo-tabs/> <repo-tabs />
<component <component
class="multi-file-edit-pane-content"
:is="currentBlobView" :is="currentBlobView"
/> />
<repo-file-buttons/> <repo-file-buttons />
</div>
</div> </div>
<repo-commit-section v-if="changedFiles.length" /> <repo-commit-section />
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import { n__ } from '../../locale'; import commitFilesList from './commit_sidebar/list.vue';
export default { export default {
components: { components: {
PopupDialog, PopupDialog,
icon,
commitFilesList,
},
directives: {
tooltip,
}, },
data() { data() {
return { return {
...@@ -13,6 +20,7 @@ export default { ...@@ -13,6 +20,7 @@ export default {
submitCommitsLoading: false, submitCommitsLoading: false,
startNewMR: false, startNewMR: false,
commitMessage: '', commitMessage: '',
collapsed: true,
}; };
}, },
computed: { computed: {
...@@ -23,10 +31,10 @@ export default { ...@@ -23,10 +31,10 @@ export default {
'changedFiles', 'changedFiles',
]), ]),
commitButtonDisabled() { commitButtonDisabled() {
return !this.commitMessage || this.submitCommitsLoading; return this.commitMessage === '' || this.submitCommitsLoading || !this.changedFiles.length;
}, },
commitButtonText() { commitMessageCount() {
return n__('Commit %d file', 'Commit %d files', this.changedFiles.length); return this.commitMessage.length;
}, },
}, },
methods: { methods: {
...@@ -77,12 +85,20 @@ export default { ...@@ -77,12 +85,20 @@ export default {
this.submitCommitsLoading = false; this.submitCommitsLoading = false;
}); });
}, },
toggleCollapsed() {
this.collapsed = !this.collapsed;
},
}, },
}; };
</script> </script>
<template> <template>
<div id="commit-area"> <div
class="multi-file-commit-panel"
:class="{
'is-collapsed': collapsed,
}"
>
<popup-dialog <popup-dialog
v-if="showNewBranchDialog" v-if="showNewBranchDialog"
:primary-button-label="__('Create new branch')" :primary-button-label="__('Create new branch')"
...@@ -92,78 +108,71 @@ export default { ...@@ -92,78 +108,71 @@ export default {
@toggle="showNewBranchDialog = false" @toggle="showNewBranchDialog = false"
@submit="makeCommit(true)" @submit="makeCommit(true)"
/> />
<button
v-if="collapsed"
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn is-collapsed prepend-top-10 append-bottom-10"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-left"
>
</i>
</button>
<commit-files-list
title="Staged"
:file-list="changedFiles"
:collapsed="collapsed"
@toggleCollapsed="toggleCollapsed"
/>
<form <form
class="form-horizontal" class="form-horizontal multi-file-commit-form"
@submit.prevent="tryCommit()"> @submit.prevent="tryCommit"
<fieldset> v-if="!collapsed"
<div class="form-group"> >
<label class="col-md-4 control-label staged-files"> <div class="multi-file-commit-fieldset">
Staged files ({{changedFiles.length}})
</label>
<div class="col-md-6">
<ul class="list-unstyled changed-files">
<li
v-for="(file, index) in changedFiles"
:key="index">
<span class="help-block">
{{ file.path }}
</span>
</li>
</ul>
</div>
</div>
<div class="form-group">
<label
class="col-md-4 control-label"
for="commit-message">
Commit message
</label>
<div class="col-md-6">
<textarea <textarea
id="commit-message" class="form-control multi-file-commit-message"
class="form-control"
name="commit-message" name="commit-message"
v-model="commitMessage"> v-model="commitMessage"
placeholder="Commit message"
>
</textarea> </textarea>
</div> </div>
</div> <div class="multi-file-commit-fieldset">
<div class="form-group target-branch">
<label <label
class="col-md-4 control-label" v-tooltip
for="target-branch"> title="Create a new merge request with these changes"
Target branch data-container="body"
data-placement="top"
>
<input
type="checkbox"
v-model="startNewMR"
/>
Merge Request
</label> </label>
<div class="col-md-6">
<span class="help-block">
{{currentBranch}}
</span>
</div>
</div>
<div class="col-md-offset-4 col-md-6">
<button <button
type="submit" type="submit"
:disabled="commitButtonDisabled" :disabled="commitButtonDisabled"
class="btn btn-success"> class="btn btn-default btn-sm append-right-10 prepend-left-10"
>
<i <i
v-if="submitCommitsLoading" v-if="submitCommitsLoading"
class="js-commit-loading-icon fa fa-spinner fa-spin" class="js-commit-loading-icon fa fa-spinner fa-spin"
aria-hidden="true" aria-hidden="true"
aria-label="loading"> aria-label="loading"
>
</i> </i>
<span class="commit-summary"> Commit
{{ commitButtonText }}
</span>
</button> </button>
</div> <div
<div class="col-md-offset-4 col-md-6"> class="multi-file-commit-message-count"
<div class="checkbox"> >
<label> {{ commitMessageCount }}
<input type="checkbox" v-model="startNewMR">
<span>Start a <strong>new merge request</strong> with these changes</span>
</label>
</div> </div>
</div> </div>
</fieldset>
</form> </form>
</div> </div>
</template> </template>
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
class="file" class="file"
@click.prevent="clickedTreeRow(file)"> @click.prevent="clickedTreeRow(file)">
<td <td
class="multi-file-table-col-name" class="multi-file-table-name"
:colspan="submoduleColSpan" :colspan="submoduleColSpan"
> >
<i <i
...@@ -85,12 +85,11 @@ ...@@ -85,12 +85,11 @@
</td> </td>
<template v-if="!isCollapsed && !isSubmodule"> <template v-if="!isCollapsed && !isSubmodule">
<td class="hidden-sm hidden-xs"> <td class="multi-file-table-col-commit-message hidden-sm hidden-xs">
<a <a
v-if="file.lastCommit.message" v-if="file.lastCommit.message"
@click.stop @click.stop
:href="file.lastCommit.url" :href="file.lastCommit.url"
class="commit-message"
> >
{{ file.lastCommit.message }} {{ file.lastCommit.message }}
</a> </a>
......
...@@ -22,12 +22,12 @@ export default { ...@@ -22,12 +22,12 @@ export default {
<template> <template>
<div <div
v-if="showButtons" v-if="showButtons"
class="repo-file-buttons" class="multi-file-editor-btn-group"
> >
<a <a
:href="activeFile.rawPath" :href="activeFile.rawPath"
target="_blank" target="_blank"
class="btn btn-default raw" class="btn btn-default btn-sm raw"
rel="noopener noreferrer"> rel="noopener noreferrer">
{{ rawDownloadButtonLabel }} {{ rawDownloadButtonLabel }}
</a> </a>
...@@ -38,17 +38,17 @@ export default { ...@@ -38,17 +38,17 @@ export default {
aria-label="File actions"> aria-label="File actions">
<a <a
:href="activeFile.blamePath" :href="activeFile.blamePath"
class="btn btn-default blame"> class="btn btn-default btn-sm blame">
Blame Blame
</a> </a>
<a <a
:href="activeFile.commitsPath" :href="activeFile.commitsPath"
class="btn btn-default history"> class="btn btn-default btn-sm history">
History History
</a> </a>
<a <a
:href="activeFile.permalink" :href="activeFile.permalink"
class="btn btn-default permalink"> class="btn btn-default btn-sm permalink">
Permalink Permalink
</a> </a>
</div> </div>
......
...@@ -32,10 +32,12 @@ export default { ...@@ -32,10 +32,12 @@ export default {
</script> </script>
<template> <template>
<div class="blob-viewer-container"> <div>
<div <div
v-if="!activeFile.renderError" v-if="!activeFile.renderError"
v-html="activeFile.html"> v-html="activeFile.html"
class="multi-file-preview-holder"
>
</div> </div>
<div <div
v-else-if="activeFile.tempFile" v-else-if="activeFile.tempFile"
......
...@@ -44,20 +44,16 @@ export default { ...@@ -44,20 +44,16 @@ export default {
</script> </script>
<template> <template>
<div id="sidebar" :class="{'sidebar-mini' : isCollapsed}"> <div class="ide-file-list">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th <th
v-if="isCollapsed" v-if="isCollapsed"
class="repo-file-options title"
> >
<strong class="clgray">
{{ projectName }}
</strong>
</th> </th>
<template v-else> <template v-else>
<th class="name multi-file-table-col-name"> <th class="name multi-file-table-name">
Name Name
</th> </th>
<th class="hidden-sm hidden-xs last-commit"> <th class="hidden-sm hidden-xs last-commit">
...@@ -79,7 +75,7 @@ export default { ...@@ -79,7 +75,7 @@ export default {
:key="n" :key="n"
/> />
<repo-file <repo-file
v-for="(file, index) in treeList" v-for="file in treeList"
:key="file.key" :key="file.key"
:file="file" :file="file"
/> />
......
...@@ -36,27 +36,32 @@ export default { ...@@ -36,27 +36,32 @@ export default {
<template> <template>
<li <li
:class="{ active : tab.active }"
@click="setFileActive(tab)" @click="setFileActive(tab)"
> >
<button <button
type="button" type="button"
class="close-btn" class="multi-file-tab-close"
@click.stop.prevent="closeFile({ file: tab })" @click.stop.prevent="closeFile({ file: tab })"
:aria-label="closeLabel"> :aria-label="closeLabel"
:class="{
'modified': tab.changed,
}"
:disabled="tab.changed"
>
<i <i
class="fa" class="fa"
:class="changedClass" :class="changedClass"
aria-hidden="true"> aria-hidden="true"
>
</i> </i>
</button> </button>
<a <div
href="#" class="multi-file-tab"
class="repo-tab" :class="{active : tab.active }"
:title="tab.url" :title="tab.url"
@click.prevent.stop="setFileActive(tab)"> >
{{tab.name}} {{ tab.name }}
</a> </div>
</li> </li>
</template> </template>
...@@ -16,14 +16,12 @@ ...@@ -16,14 +16,12 @@
<template> <template>
<ul <ul
id="tabs" class="multi-file-tabs list-unstyled append-bottom-0"
class="list-unstyled"
> >
<repo-tab <repo-tab
v-for="tab in openFiles" v-for="tab in openFiles"
:key="tab.id" :key="tab.id"
:tab="tab" :tab="tab"
/> />
<li class="tabs-divider" />
</ul> </ul>
</template> </template>
...@@ -34,3 +34,7 @@ export const canEditFile = (state) => { ...@@ -34,3 +34,7 @@ export const canEditFile = (state) => {
openedFiles.length && openedFiles.length &&
(currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary); (currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary);
}; };
export const addedFiles = state => changedFiles(state).filter(f => f.tempFile);
export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile);
...@@ -35,126 +35,87 @@ ...@@ -35,126 +35,87 @@
} }
} }
.repository-view { .multi-file {
border: 1px solid $border-color;
border-radius: $border-radius-default;
color: $almost-black;
.code.white pre .hll {
background-color: $well-light-border !important;
}
.tree-content-holder {
display: -webkit-flex;
display: flex; display: flex;
min-height: 300px; height: calc(100vh - 145px);
} border-top: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
.tree-content-holder-mini { &.is-collapsed {
height: 100vh; .ide-file-list {
max-width: 250px;
} }
.panel-right {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
width: 80%;
height: 100%;
.monaco-editor.vs {
.current-line {
border: 0;
background: $well-light-border;
} }
}
.line-numbers { .ide-file-list {
cursor: pointer; flex: 1;
overflow: scroll;
&:hover { .file {
text-decoration: underline; cursor: pointer;
}
}
} }
.blob-no-preview { a {
.vertical-center { color: $gl-text-color;
justify-content: center;
width: 100%;
}
} }
&.blob-editor-container { th {
overflow: hidden; position: sticky;
top: 0;
} }
}
.blob-viewer-container { .multi-file-table-name,
-webkit-flex: 1; .multi-file-table-col-commit-message {
flex: 1; white-space: nowrap;
overflow: auto; overflow: hidden;
text-overflow: ellipsis;
> div, max-width: 0;
.file-content:not(.wiki) { }
display: flex;
}
> div, .multi-file-table-name {
.file-content, width: 350px;
.blob-viewer, }
.line-number,
.blob-content,
.code {
min-height: 100%;
width: 100%;
}
.line-numbers { .multi-file-table-col-commit-message {
min-width: 44px; width: 50%;
} }
.blob-content { .multi-file-edit-pane {
display: flex;
flex-direction: column;
flex: 1; flex: 1;
overflow-x: auto; border-left: 1px solid $white-dark;
} overflow: hidden;
} }
#tabs { .multi-file-tabs {
position: relative;
flex-shrink: 0;
display: flex; display: flex;
width: 100%; overflow: scroll;
padding-left: 0; background-color: $white-normal;
margin-bottom: 0; box-shadow: inset 0 -1px $white-dark;
white-space: nowrap;
overflow-y: hidden;
overflow-x: auto;
li { > li {
position: relative; position: relative;
background: $gray-normal; }
padding: #{$gl-padding / 2} $gl-padding; }
.multi-file-tab {
@include str-truncated(150px);
padding: ($gl-padding / 2) ($gl-padding + 12) ($gl-padding / 2) $gl-padding;
background-color: $gray-normal;
border-right: 1px solid $white-dark; border-right: 1px solid $white-dark;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
cursor: pointer; cursor: pointer;
&.active { &.active {
background: $white-light; background-color: $white-light;
border-bottom: 0; border-bottom-color: $white-light;
}
a {
@include str-truncated(100px);
color: $gl-text-color;
vertical-align: middle;
text-decoration: none;
margin-right: 12px;
&:focus {
outline: none;
}
} }
}
.close-btn { .multi-file-tab-close {
position: absolute; position: absolute;
right: 8px; right: 8px;
top: 50%; top: 50%;
...@@ -162,143 +123,155 @@ ...@@ -162,143 +123,155 @@
background: none; background: none;
border: 0; border: 0;
font-size: $gl-font-size; font-size: $gl-font-size;
color: $gray-darkest;
transform: translateY(-50%); transform: translateY(-50%);
}
.close-icon:hover { &:not(.modified):hover,
&:not(.modified):focus {
color: $hint-color; color: $hint-color;
} }
.close-icon, &.modified {
.unsaved-icon { color: $indigo-700;
color: $gray-darkest;
}
.unsaved-icon {
color: $brand-success;
} }
}
&.tabs-divider { .multi-file-edit-pane-content {
width: 100%; flex: 1;
background-color: $white-light; height: 0;
border-right: 0; }
border-top-right-radius: 2px;
}
}
}
.repo-file-buttons { .multi-file-editor-btn-group {
background-color: $white-light; padding: $grid-size;
padding: 5px 10px; border-top: 1px solid $white-dark;
border-top: 1px solid $white-normal; }
}
#binary-viewer { // Not great, but this is to deal with our current output
height: 80vh; .multi-file-preview-holder {
overflow: auto; height: 100%;
margin: 0; overflow: scroll;
.blob-viewer { .blob-viewer {
padding-top: 20px; height: 100%;
padding-left: 20px;
} }
.binary-unknown { .file-content.code {
text-align: center; display: flex;
padding-top: 100px;
background: $gray-light;
height: 100%;
font-size: 17px;
span { i {
display: block; margin-left: -10px;
} }
} }
.line-numbers {
min-width: 50px;
} }
.file-content,
.line-numbers,
.blob-content,
.code {
min-height: 100%;
} }
}
#commit-area { .multi-file-commit-panel {
background: $gray-light; display: flex;
padding: 20px; flex-direction: column;
height: 100%;
width: 290px;
padding: $gl-padding;
background-color: $gray-light;
border-left: 1px solid $white-dark;
.help-block { &.is-collapsed {
padding-top: 7px; width: 60px;
margin-top: 0; padding: 0;
}
} }
}
#view-toggler { .multi-file-commit-panel-section {
height: 41px; display: flex;
position: relative; flex-direction: column;
display: block; flex: 1;
border-bottom: 1px solid $white-normal; }
background: $white-light;
margin-top: -5px; .multi-file-commit-panel-header {
} display: flex;
align-items: center;
padding: 0 0 12px;
margin-bottom: 12px;
border-bottom: 1px solid $white-dark;
&.is-collapsed {
border-bottom: 1px solid $white-dark;
#binary-viewer { svg {
img { margin-left: auto;
max-width: 100%; margin-right: auto;
} }
} }
}
#sidebar { .multi-file-commit-panel-collapse-btn {
flex: 1; padding-top: 0;
height: 100%; padding-bottom: 0;
margin-left: auto;
font-size: 20px;
&.sidebar-mini { &.is-collapsed {
width: 20%; margin-right: auto;
border-right: 1px solid $white-normal;
overflow: auto;
} }
}
.table { .multi-file-commit-list {
margin-bottom: 0; flex: 1;
} overflow: scroll;
}
tr { .multi-file-commit-list-item {
.repo-file-options { display: flex;
padding: 2px 16px; align-items: center;
width: 100%; }
}
.title { .multi-file-addition {
font-size: 10px; fill: $green-500;
text-transform: uppercase; }
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
.file-icon { .multi-file-modified {
margin-right: 5px; fill: $orange-500;
} }
td { .multi-file-commit-list-collapsed {
white-space: nowrap; display: flex;
} flex-direction: column;
}
.file { > svg {
cursor: pointer; margin-left: auto;
margin-right: auto;
} }
}
a { .multi-file-commit-list-path {
@include str-truncated(250px); @include str-truncated(100%);
color: $almost-black; }
}
} .multi-file-commit-form {
padding-top: 12px;
border-top: 1px solid $white-dark;
} }
.render-error { .multi-file-commit-fieldset {
min-height: calc(100vh - 62px); display: flex;
align-items: center;
padding-bottom: 12px;
p { .btn {
width: 100%; flex: 1;
} }
} }
.multi-file-table-col-name { .multi-file-commit-message.form-control {
width: 350px; height: 80px;
resize: none;
} }
...@@ -11,6 +11,6 @@ ...@@ -11,6 +11,6 @@
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'repo' = webpack_bundle_tag 'repo'
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [(container_class unless show_new_repo?), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push' = render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
- @no_container = true;
#repo{ data: { root: @path.empty?.to_s, #repo{ data: { root: @path.empty?.to_s,
root_url: project_tree_path(project), root_url: project_tree_path(project),
url: content_url, url: content_url,
......
...@@ -26,9 +26,11 @@ feature 'Multi-file editor new directory', :js do ...@@ -26,9 +26,11 @@ feature 'Multi-file editor new directory', :js do
click_button('Create directory') click_button('Create directory')
end end
find('.multi-file-commit-panel-collapse-btn').click
fill_in('commit-message', with: 'commit message') fill_in('commit-message', with: 'commit message')
click_button('Commit 1 file') click_button('Commit')
expect(page).to have_selector('td', text: 'commit message') expect(page).to have_selector('td', text: 'commit message')
end end
......
...@@ -26,9 +26,11 @@ feature 'Multi-file editor new file', :js do ...@@ -26,9 +26,11 @@ feature 'Multi-file editor new file', :js do
click_button('Create file') click_button('Create file')
end end
find('.multi-file-commit-panel-collapse-btn').click
fill_in('commit-message', with: 'commit message') fill_in('commit-message', with: 'commit message')
click_button('Commit 1 file') click_button('Commit')
expect(page).to have_selector('td', text: 'commit message') expect(page).to have_selector('td', text: 'commit message')
end end
......
...@@ -26,7 +26,7 @@ feature 'Multi-file editor upload file', :js do ...@@ -26,7 +26,7 @@ feature 'Multi-file editor upload file', :js do
find('.add-to-tree').click find('.add-to-tree').click
expect(page).to have_selector('.repo-tab', text: 'doc_sample.txt') expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline)) expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
end end
...@@ -39,7 +39,7 @@ feature 'Multi-file editor upload file', :js do ...@@ -39,7 +39,7 @@ feature 'Multi-file editor upload file', :js do
find('.add-to-tree').click find('.add-to-tree').click
expect(page).to have_selector('.repo-tab', text: 'dk.png') expect(page).to have_selector('.multi-file-tab', text: 'dk.png')
expect(page).not_to have_selector('.monaco-editor') expect(page).not_to have_selector('.monaco-editor')
expect(page).to have_content('The source could not be displayed for this temporary file.') expect(page).to have_content('The source could not be displayed for this temporary file.')
end end
......
import Vue from 'vue';
import store from '~/repo/stores';
import listCollapsed from '~/repo/components/commit_sidebar/list_collapsed.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list collapsed', () => {
let vm;
beforeEach(() => {
const Component = Vue.extend(listCollapsed);
vm = createComponentWithStore(Component, store);
vm.$store.state.openFiles.push(file(), file());
vm.$store.state.openFiles[0].tempFile = true;
vm.$store.state.openFiles.forEach((f) => {
Object.assign(f, {
changed: true,
});
});
vm.$mount();
});
afterEach(() => {
vm.$destroy();
});
it('renders added & modified files count', () => {
expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1');
});
});
import Vue from 'vue';
import listItem from '~/repo/components/commit_sidebar/list_item.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list item', () => {
let vm;
let f;
beforeEach(() => {
const Component = Vue.extend(listItem);
f = file();
vm = mountComponent(Component, {
file: f,
});
});
afterEach(() => {
vm.$destroy();
});
it('renders file path', () => {
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
});
describe('computed', () => {
describe('iconName', () => {
it('returns modified when not a tempFile', () => {
expect(vm.iconName).toBe('file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
expect(vm.iconName).toBe('file-addition');
});
});
describe('iconClass', () => {
it('returns modified when not a tempFile', () => {
expect(vm.iconClass).toContain('multi-file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
expect(vm.iconClass).toContain('multi-file-addition');
});
});
});
});
import Vue from 'vue';
import store from '~/repo/stores';
import commitSidebarList from '~/repo/components/commit_sidebar/list.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list', () => {
let vm;
beforeEach(() => {
const Component = Vue.extend(commitSidebarList);
vm = createComponentWithStore(Component, store, {
title: 'Staged',
fileList: [],
collapsed: false,
}).$mount();
});
afterEach(() => {
vm.$destroy();
});
describe('empty file list', () => {
it('renders no changes text', () => {
expect(vm.$el.querySelector('.help-block').textContent.trim()).toBe('No changes');
});
});
describe('with a list of files', () => {
beforeEach((done) => {
const f = file('file name');
f.changed = true;
vm.fileList.push(f);
Vue.nextTick(done);
});
it('renders list', () => {
expect(vm.$el.querySelectorAll('li').length).toBe(1);
});
});
describe('collapsed', () => {
beforeEach((done) => {
vm.collapsed = true;
Vue.nextTick(done);
});
it('adds collapsed class', () => {
expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
});
it('hides list', () => {
expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
expect(vm.$el.querySelector('.help-block')).toBeNull();
});
it('hides collapse button', () => {
expect(vm.$el.querySelector('.multi-file-commit-panel-collapse-btn')).toBeNull();
});
});
it('clicking toggle collapse button emits toggle event', () => {
spyOn(vm, '$emit');
vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed');
});
});
...@@ -25,8 +25,12 @@ describe('RepoCommitSection', () => { ...@@ -25,8 +25,12 @@ describe('RepoCommitSection', () => {
return comp.$mount(); return comp.$mount();
} }
beforeEach(() => { beforeEach((done) => {
vm = createComponent(); vm = createComponent();
vm.collapsed = false;
Vue.nextTick(done);
}); });
afterEach(() => { afterEach(() => {
...@@ -36,12 +40,11 @@ describe('RepoCommitSection', () => { ...@@ -36,12 +40,11 @@ describe('RepoCommitSection', () => {
}); });
it('renders a commit section', () => { it('renders a commit section', () => {
const changedFileElements = [...vm.$el.querySelectorAll('.changed-files > li')]; const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const submitCommit = vm.$el.querySelector('.btn'); const submitCommit = vm.$el.querySelector('form .btn');
const targetBranch = vm.$el.querySelector('.target-branch');
expect(vm.$el.querySelector(':scope > form')).toBeTruthy(); expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(vm.$el.querySelector('.staged-files').textContent.trim()).toEqual('Staged files (2)'); expect(vm.$el.querySelector('.multi-file-commit-panel-section header').textContent.trim()).toEqual('Staged');
expect(changedFileElements.length).toEqual(2); expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => { changedFileElements.forEach((changedFile, i) => {
...@@ -49,10 +52,7 @@ describe('RepoCommitSection', () => { ...@@ -49,10 +52,7 @@ describe('RepoCommitSection', () => {
}); });
expect(submitCommit.disabled).toBeTruthy(); expect(submitCommit.disabled).toBeTruthy();
expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeFalsy(); expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
expect(vm.$el.querySelector('.commit-summary').textContent.trim()).toEqual('Commit 2 files');
expect(targetBranch.querySelector(':scope > label').textContent.trim()).toEqual('Target branch');
expect(targetBranch.querySelector('.help-block').textContent.trim()).toEqual('master');
}); });
describe('when submitting', () => { describe('when submitting', () => {
...@@ -69,7 +69,7 @@ describe('RepoCommitSection', () => { ...@@ -69,7 +69,7 @@ describe('RepoCommitSection', () => {
}); });
it('allows you to submit', () => { it('allows you to submit', () => {
expect(vm.$el.querySelector('.btn').disabled).toBeTruthy(); expect(vm.$el.querySelector('form .btn').disabled).toBeTruthy();
}); });
it('submits commit', (done) => { it('submits commit', (done) => {
......
...@@ -29,7 +29,6 @@ describe('RepoSidebar', () => { ...@@ -29,7 +29,6 @@ describe('RepoSidebar', () => {
const thead = vm.$el.querySelector('thead'); const thead = vm.$el.querySelector('thead');
const tbody = vm.$el.querySelector('tbody'); const tbody = vm.$el.querySelector('tbody');
expect(vm.$el.id).toEqual('sidebar');
expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy(); expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy();
expect(thead.querySelector('.name').textContent.trim()).toEqual('Name'); expect(thead.querySelector('.name').textContent.trim()).toEqual('Name');
expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit'); expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit');
...@@ -40,18 +39,6 @@ describe('RepoSidebar', () => { ...@@ -40,18 +39,6 @@ describe('RepoSidebar', () => {
expect(tbody.querySelector('.file')).toBeTruthy(); expect(tbody.querySelector('.file')).toBeTruthy();
}); });
it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', (done) => {
vm.$store.state.openFiles.push(vm.$store.state.tree[0]);
Vue.nextTick(() => {
expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy();
expect(vm.$el.querySelector('thead')).toBeTruthy();
expect(vm.$el.querySelector('thead .repo-file-options')).toBeTruthy();
done();
});
});
it('renders 5 loading files if tree is loading', (done) => { it('renders 5 loading files if tree is loading', (done) => {
vm.$store.state.tree = []; vm.$store.state.tree = [];
vm.$store.state.loading = true; vm.$store.state.loading = true;
......
...@@ -24,8 +24,8 @@ describe('RepoTab', () => { ...@@ -24,8 +24,8 @@ describe('RepoTab', () => {
tab: file(), tab: file(),
}); });
vm.$store.state.openFiles.push(vm.tab); vm.$store.state.openFiles.push(vm.tab);
const close = vm.$el.querySelector('.close-btn'); const close = vm.$el.querySelector('.multi-file-tab-close');
const name = vm.$el.querySelector(`a[title="${vm.tab.url}"]`); const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`);
expect(close.querySelector('.fa-times')).toBeTruthy(); expect(close.querySelector('.fa-times')).toBeTruthy();
expect(name.textContent.trim()).toEqual(vm.tab.name); expect(name.textContent.trim()).toEqual(vm.tab.name);
...@@ -50,7 +50,7 @@ describe('RepoTab', () => { ...@@ -50,7 +50,7 @@ describe('RepoTab', () => {
spyOn(vm, 'closeFile'); spyOn(vm, 'closeFile');
vm.$el.querySelector('.close-btn').click(); vm.$el.querySelector('.multi-file-tab-close').click();
expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab }); expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab });
}); });
...@@ -62,7 +62,7 @@ describe('RepoTab', () => { ...@@ -62,7 +62,7 @@ describe('RepoTab', () => {
tab, tab,
}); });
expect(vm.$el.querySelector('.close-btn .fa-circle')).toBeTruthy(); expect(vm.$el.querySelector('.multi-file-tab-close .fa-circle')).not.toBeNull();
}); });
describe('methods', () => { describe('methods', () => {
...@@ -77,7 +77,7 @@ describe('RepoTab', () => { ...@@ -77,7 +77,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab); vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab); vm.$store.dispatch('setFileActive', tab);
vm.$el.querySelector('.close-btn').click(); vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => { vm.$nextTick(() => {
expect(tab.opened).toBeTruthy(); expect(tab.opened).toBeTruthy();
...@@ -95,7 +95,7 @@ describe('RepoTab', () => { ...@@ -95,7 +95,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab); vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab); vm.$store.dispatch('setFileActive', tab);
vm.$el.querySelector('.close-btn').click(); vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => { vm.$nextTick(() => {
expect(tab.opened).toBeFalsy(); expect(tab.opened).toBeFalsy();
......
...@@ -25,12 +25,11 @@ describe('RepoTabs', () => { ...@@ -25,12 +25,11 @@ describe('RepoTabs', () => {
vm.$store.state.openFiles = openedFiles; vm.$store.state.openFiles = openedFiles;
vm.$nextTick(() => { vm.$nextTick(() => {
const tabs = [...vm.$el.querySelectorAll(':scope > li')]; const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
expect(tabs.length).toEqual(3); expect(tabs.length).toEqual(2);
expect(tabs[0].classList.contains('active')).toBeTruthy(); expect(tabs[0].classList.contains('active')).toBeTruthy();
expect(tabs[1].classList.contains('active')).toBeFalsy(); expect(tabs[1].classList.contains('active')).toBeFalsy();
expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy();
done(); done();
}); });
......
...@@ -116,4 +116,31 @@ describe('Multi-file store getters', () => { ...@@ -116,4 +116,31 @@ describe('Multi-file store getters', () => {
expect(getters.canEditFile(localState)).toBeFalsy(); expect(getters.canEditFile(localState)).toBeFalsy();
}); });
}); });
describe('modifiedFiles', () => {
it('returns a list of modified files', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('changed'));
localState.openFiles[1].changed = true;
const modifiedFiles = getters.modifiedFiles(localState);
expect(modifiedFiles.length).toBe(1);
expect(modifiedFiles[0].name).toBe('changed');
});
});
describe('addedFiles', () => {
it('returns a list of added files', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('added'));
localState.openFiles[1].changed = true;
localState.openFiles[1].tempFile = true;
const modifiedFiles = getters.addedFiles(localState);
expect(modifiedFiles.length).toBe(1);
expect(modifiedFiles[0].name).toBe('added');
});
});
}); });
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