Merge branch 'ide-workbench-bar' into '44846-improve-web-ide-left-panel-and-modes'
Add left links sidebar to IDE See merge request gitlab-org/gitlab-ce!18368
This commit is contained in:
commit
26c241d955
43 changed files with 724 additions and 919 deletions
71
app/assets/javascripts/ide/components/activity_bar.vue
Normal file
71
app/assets/javascripts/ide/components/activity_bar.vue
Normal file
|
@ -0,0 +1,71 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import { activityBarViews } from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentProject']),
|
||||
...mapState(['currentActivityView']),
|
||||
goBackUrl() {
|
||||
return document.referrer || this.currentProject.web_url;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateActivityBarView']),
|
||||
},
|
||||
activityBarViews,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="ide-activity-bar">
|
||||
<ul class="list-unstyled">
|
||||
<li v-once>
|
||||
<a
|
||||
:href="goBackUrl"
|
||||
class="ide-sidebar-link"
|
||||
:aria-label="s__('IDE|Go back')"
|
||||
>
|
||||
<icon
|
||||
:size="16"
|
||||
name="go-back"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="ide-sidebar-link js-ide-edit-mode"
|
||||
:class="{
|
||||
active: currentActivityView === $options.activityBarViews.edit
|
||||
}"
|
||||
@click.prevent="updateActivityBarView($options.activityBarViews.edit)"
|
||||
:aria-label="s__('IDE|Edit mode')"
|
||||
>
|
||||
<icon
|
||||
name="code"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="ide-sidebar-link js-ide-commit-mode"
|
||||
:class="{
|
||||
active: currentActivityView === $options.activityBarViews.commit
|
||||
}"
|
||||
@click.prevent="updateActivityBarView($options.activityBarViews.commit)"
|
||||
:aria-label="s__('IDE|Commit mode')"
|
||||
>
|
||||
<icon
|
||||
name="commit"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</template>
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import { mapState } from 'vuex';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
|
||||
|
@ -10,26 +10,12 @@ export default {
|
|||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
noChangesStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
committedStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['lastCommitMsg', 'rightPanelCollapsed']),
|
||||
...mapGetters(['collapseButtonIcon', 'collapseButtonTooltip']),
|
||||
...mapState(['lastCommitMsg', 'noChangesStateSvgPath', 'committedStateSvgPath']),
|
||||
statusSvg() {
|
||||
return this.lastCommitMsg ? this.committedStateSvgPath : this.noChangesStateSvgPath;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['toggleRightPanelCollapsed']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -37,31 +23,8 @@ export default {
|
|||
<div
|
||||
class="multi-file-commit-panel-section ide-commit-empty-state js-empty-state"
|
||||
>
|
||||
<header
|
||||
class="multi-file-commit-panel-header"
|
||||
:class="{
|
||||
'is-collapsed': rightPanelCollapsed,
|
||||
}"
|
||||
>
|
||||
<button
|
||||
v-tooltip
|
||||
:title="collapseButtonTooltip"
|
||||
data-container="body"
|
||||
data-placement="left"
|
||||
type="button"
|
||||
class="btn btn-transparent multi-file-commit-panel-collapse-btn"
|
||||
:aria-label="__('Toggle sidebar')"
|
||||
@click.stop="toggleRightPanelCollapsed"
|
||||
>
|
||||
<icon
|
||||
:name="collapseButtonIcon"
|
||||
:size="18"
|
||||
/>
|
||||
</button>
|
||||
</header>
|
||||
<div
|
||||
class="ide-commit-empty-state-container"
|
||||
v-if="!rightPanelCollapsed"
|
||||
>
|
||||
<div class="svg-content svg-80">
|
||||
<img :src="statusSvg" />
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
<script>
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import { mapActions } from 'vuex';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import ListItem from './list_item.vue';
|
||||
import ListCollapsed from './list_collapsed.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
ListItem,
|
||||
ListCollapsed,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
|
@ -24,11 +22,6 @@ export default {
|
|||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
showToggle: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
iconName: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -52,8 +45,6 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['rightPanelCollapsed']),
|
||||
...mapGetters(['collapseButtonIcon', 'collapseButtonTooltip']),
|
||||
titleText() {
|
||||
return sprintf(__('%{title} changes'), {
|
||||
title: this.title,
|
||||
|
@ -61,7 +52,7 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['toggleRightPanelCollapsed', 'stageAllChanges', 'unstageAllChanges']),
|
||||
...mapActions(['stageAllChanges', 'unstageAllChanges']),
|
||||
actionBtnClicked() {
|
||||
this[this.action]();
|
||||
},
|
||||
|
@ -72,19 +63,12 @@ export default {
|
|||
<template>
|
||||
<div
|
||||
class="ide-commit-list-container"
|
||||
:class="{
|
||||
'is-collapsed': rightPanelCollapsed,
|
||||
}"
|
||||
>
|
||||
<header
|
||||
class="multi-file-commit-panel-header"
|
||||
>
|
||||
<div
|
||||
v-if="!rightPanelCollapsed"
|
||||
class="multi-file-commit-panel-header-title"
|
||||
:class="{
|
||||
'append-right-10': showToggle,
|
||||
}"
|
||||
>
|
||||
<icon
|
||||
v-once
|
||||
|
@ -100,52 +84,28 @@ export default {
|
|||
{{ actionBtnText }}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="showToggle"
|
||||
v-tooltip
|
||||
:title="collapseButtonTooltip"
|
||||
data-container="body"
|
||||
data-placement="left"
|
||||
type="button"
|
||||
class="btn btn-transparent multi-file-commit-panel-collapse-btn"
|
||||
:aria-label="__('Toggle sidebar')"
|
||||
@click.stop="toggleRightPanelCollapsed"
|
||||
>
|
||||
<icon
|
||||
:name="collapseButtonIcon"
|
||||
:size="18"
|
||||
/>
|
||||
</button>
|
||||
</header>
|
||||
<list-collapsed
|
||||
v-if="rightPanelCollapsed"
|
||||
:files="fileList"
|
||||
:icon-name="iconName"
|
||||
:title="title"
|
||||
/>
|
||||
<template v-else>
|
||||
<ul
|
||||
v-if="fileList.length"
|
||||
class="multi-file-commit-list list-unstyled append-bottom-0"
|
||||
<ul
|
||||
v-if="fileList.length"
|
||||
class="multi-file-commit-list list-unstyled append-bottom-0"
|
||||
>
|
||||
<li
|
||||
v-for="file in fileList"
|
||||
:key="file.key"
|
||||
>
|
||||
<li
|
||||
v-for="file in fileList"
|
||||
:key="file.key"
|
||||
>
|
||||
<list-item
|
||||
:file="file"
|
||||
:action-component="itemActionComponent"
|
||||
:key-prefix="title"
|
||||
:staged-list="stagedList"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="multi-file-commit-list help-block"
|
||||
>
|
||||
{{ __('No changes') }}
|
||||
</p>
|
||||
</template>
|
||||
<list-item
|
||||
:file="file"
|
||||
:action-component="itemActionComponent"
|
||||
:key-prefix="title"
|
||||
:staged-list="stagedList"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
v-else
|
||||
class="multi-file-commit-list help-block"
|
||||
>
|
||||
{{ __('No changes') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,82 +1,67 @@
|
|||
<script>
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import ideSidebar from './ide_side_bar.vue';
|
||||
import ideContextbar from './ide_context_bar.vue';
|
||||
import repoTabs from './repo_tabs.vue';
|
||||
import ideStatusBar from './ide_status_bar.vue';
|
||||
import repoEditor from './repo_editor.vue';
|
||||
import FindFile from './file_finder/index.vue';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import IdeSidebar from './ide_side_bar.vue';
|
||||
import RepoTabs from './repo_tabs.vue';
|
||||
import IdeStatusBar from './ide_status_bar.vue';
|
||||
import RepoEditor from './repo_editor.vue';
|
||||
import FindFile from './file_finder/index.vue';
|
||||
|
||||
const originalStopCallback = Mousetrap.stopCallback;
|
||||
const originalStopCallback = Mousetrap.stopCallback;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ideSidebar,
|
||||
ideContextbar,
|
||||
repoTabs,
|
||||
ideStatusBar,
|
||||
repoEditor,
|
||||
FindFile,
|
||||
},
|
||||
props: {
|
||||
emptyStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
noChangesStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
committedStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'changedFiles',
|
||||
'openFiles',
|
||||
'viewer',
|
||||
'currentMergeRequestId',
|
||||
'fileFindVisible',
|
||||
]),
|
||||
...mapGetters(['activeFile', 'hasChanges']),
|
||||
},
|
||||
mounted() {
|
||||
const returnValue = 'Are you sure you want to lose unsaved changes?';
|
||||
window.onbeforeunload = e => {
|
||||
if (!this.changedFiles.length) return undefined;
|
||||
export default {
|
||||
components: {
|
||||
IdeSidebar,
|
||||
RepoTabs,
|
||||
IdeStatusBar,
|
||||
RepoEditor,
|
||||
FindFile,
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'changedFiles',
|
||||
'openFiles',
|
||||
'viewer',
|
||||
'currentMergeRequestId',
|
||||
'fileFindVisible',
|
||||
'emptyStateSvgPath',
|
||||
]),
|
||||
...mapGetters(['activeFile', 'hasChanges']),
|
||||
},
|
||||
mounted() {
|
||||
const returnValue = 'Are you sure you want to lose unsaved changes?';
|
||||
window.onbeforeunload = e => {
|
||||
if (!this.changedFiles.length) return undefined;
|
||||
|
||||
Object.assign(e, {
|
||||
returnValue,
|
||||
});
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.toggleFileFinder(!this.fileFindVisible);
|
||||
Object.assign(e, {
|
||||
returnValue,
|
||||
});
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['toggleFileFinder']),
|
||||
mousetrapStopCallback(e, el, combo) {
|
||||
if (combo === 't' && el.classList.contains('dropdown-input-field')) {
|
||||
return true;
|
||||
} else if (combo === 'command+p' || combo === 'ctrl+p') {
|
||||
return false;
|
||||
}
|
||||
Mousetrap.bind(['t', 'command+p', 'ctrl+p'], e => {
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return originalStopCallback(e, el, combo);
|
||||
},
|
||||
this.toggleFileFinder(!this.fileFindVisible);
|
||||
});
|
||||
|
||||
Mousetrap.stopCallback = (e, el, combo) => this.mousetrapStopCallback(e, el, combo);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['toggleFileFinder']),
|
||||
mousetrapStopCallback(e, el, combo) {
|
||||
if (combo === 't' && el.classList.contains('dropdown-input-field')) {
|
||||
return true;
|
||||
} else if (combo === 'command+p' || combo === 'ctrl+p') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return originalStopCallback(e, el, combo);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -136,9 +121,5 @@
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<ide-contextbar
|
||||
:no-changes-state-svg-path="noChangesStateSvgPath"
|
||||
:committed-state-svg-path="committedStateSvgPath"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
<script>
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
|
||||
import repoCommitSection from './repo_commit_section.vue';
|
||||
import ResizablePanel from './resizable_panel.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
repoCommitSection,
|
||||
icon,
|
||||
panelResizer,
|
||||
ResizablePanel,
|
||||
},
|
||||
props: {
|
||||
noChangesStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
committedStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<resizable-panel
|
||||
:collapsible="true"
|
||||
:initial-width="340"
|
||||
side="right"
|
||||
>
|
||||
<div
|
||||
class="multi-file-commit-panel-section"
|
||||
>
|
||||
<repo-commit-section
|
||||
:no-changes-state-svg-path="noChangesStateSvgPath"
|
||||
:committed-state-svg-path="committedStateSvgPath"
|
||||
/>
|
||||
</div>
|
||||
</resizable-panel>
|
||||
</template>
|
|
@ -1,43 +0,0 @@
|
|||
<script>
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
props: {
|
||||
projectUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
goBackUrl() {
|
||||
return document.referrer || this.projectUrl;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="ide-external-links"
|
||||
v-once
|
||||
>
|
||||
<p>
|
||||
<a
|
||||
:href="goBackUrl"
|
||||
class="ide-sidebar-link"
|
||||
>
|
||||
<icon
|
||||
:size="16"
|
||||
class="append-right-8"
|
||||
name="go-back"
|
||||
/>
|
||||
<span class="ide-external-links-text">
|
||||
{{ s__('Go back') }}
|
||||
</span>
|
||||
</a>
|
||||
</p>
|
||||
</nav>
|
||||
</template>
|
|
@ -1,47 +0,0 @@
|
|||
<script>
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import repoTree from './ide_repo_tree.vue';
|
||||
import newDropdown from './new_dropdown/index.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
repoTree,
|
||||
icon,
|
||||
newDropdown,
|
||||
},
|
||||
props: {
|
||||
projectId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
branch: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="branch-container">
|
||||
<div class="branch-header">
|
||||
<div class="branch-header-title str-truncated ref-name">
|
||||
<icon
|
||||
name="branch"
|
||||
:size="12"
|
||||
/>
|
||||
{{ branch.name }}
|
||||
</div>
|
||||
<div class="branch-header-btns">
|
||||
<new-dropdown
|
||||
:project-id="projectId"
|
||||
:branch="branch.name"
|
||||
path=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<repo-tree
|
||||
:tree="branch.tree"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -1,65 +0,0 @@
|
|||
<script>
|
||||
import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
|
||||
import Identicon from '../../vue_shared/components/identicon.vue';
|
||||
import BranchesTree from './ide_project_branches_tree.vue';
|
||||
import ExternalLinks from './ide_external_links.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BranchesTree,
|
||||
ExternalLinks,
|
||||
ProjectAvatarImage,
|
||||
Identicon,
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="projects-sidebar">
|
||||
<div class="context-header">
|
||||
<a
|
||||
:title="project.name"
|
||||
:href="project.web_url"
|
||||
>
|
||||
<div
|
||||
v-if="project.avatar_url"
|
||||
class="avatar-container s40 project-avatar"
|
||||
>
|
||||
<project-avatar-image
|
||||
class="avatar-container project-avatar"
|
||||
:link-href="project.path"
|
||||
:img-src="project.avatar_url"
|
||||
:img-alt="project.name"
|
||||
:img-size="40"
|
||||
/>
|
||||
</div>
|
||||
<identicon
|
||||
v-else
|
||||
size-class="s40"
|
||||
:entity-id="project.id"
|
||||
:entity-name="project.name"
|
||||
/>
|
||||
<div class="sidebar-context-title">
|
||||
{{ project.name }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<external-links
|
||||
:project-url="project.web_url"
|
||||
/>
|
||||
<div class="multi-file-commit-panel-inner-scroll">
|
||||
<branches-tree
|
||||
v-for="branch in project.branches"
|
||||
:key="branch.name"
|
||||
:project-id="project.path_with_namespace"
|
||||
:branch="branch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,36 +1,43 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import icon from '~/vue_shared/components/icon.vue';
|
||||
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
|
||||
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import projectTree from './ide_project_tree.vue';
|
||||
import ResizablePanel from './resizable_panel.vue';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import ProjectAvatarImage from '~/vue_shared/components/project_avatar/image.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
|
||||
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import Identicon from '../../vue_shared/components/identicon.vue';
|
||||
import IdeTree from './ide_tree.vue';
|
||||
import ResizablePanel from './resizable_panel.vue';
|
||||
import ActivityBar from './activity_bar.vue';
|
||||
import CommitSection from './repo_commit_section.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
projectTree,
|
||||
icon,
|
||||
panelResizer,
|
||||
skeletonLoadingContainer,
|
||||
ResizablePanel,
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
'loading',
|
||||
]),
|
||||
...mapGetters([
|
||||
'projectsWithTrees',
|
||||
]),
|
||||
},
|
||||
};
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
PanelResizer,
|
||||
SkeletonLoadingContainer,
|
||||
ResizablePanel,
|
||||
ActivityBar,
|
||||
ProjectAvatarImage,
|
||||
Identicon,
|
||||
CommitSection,
|
||||
IdeTree,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['loading', 'currentBranchId', 'currentActivityView']),
|
||||
...mapGetters(['currentProject']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<resizable-panel
|
||||
:collapsible="false"
|
||||
:initial-width="290"
|
||||
:initial-width="340"
|
||||
side="left"
|
||||
>
|
||||
<activity-bar
|
||||
v-if="!loading"
|
||||
/>
|
||||
<div class="multi-file-commit-panel-inner">
|
||||
<template v-if="loading">
|
||||
<div
|
||||
|
@ -41,11 +48,50 @@
|
|||
<skeleton-loading-container />
|
||||
</div>
|
||||
</template>
|
||||
<project-tree
|
||||
v-for="project in projectsWithTrees"
|
||||
:key="project.id"
|
||||
:project="project"
|
||||
/>
|
||||
<template v-else>
|
||||
<div class="context-header ide-context-header">
|
||||
<a
|
||||
:href="currentProject.web_url"
|
||||
>
|
||||
<div
|
||||
v-if="currentProject.avatar_url"
|
||||
class="avatar-container s40 project-avatar"
|
||||
>
|
||||
<project-avatar-image
|
||||
class="avatar-container project-avatar"
|
||||
:link-href="currentProject.path"
|
||||
:img-src="currentProject.avatar_url"
|
||||
:img-alt="currentProject.name"
|
||||
:img-size="40"
|
||||
/>
|
||||
</div>
|
||||
<identicon
|
||||
v-else
|
||||
size-class="s40"
|
||||
:entity-id="currentProject.id"
|
||||
:entity-name="currentProject.name"
|
||||
/>
|
||||
<div class="ide-sidebar-project-title">
|
||||
<div class="sidebar-context-title">
|
||||
{{ currentProject.name }}
|
||||
</div>
|
||||
<div
|
||||
class="sidebar-context-title ide-sidebar-branch-title"
|
||||
>
|
||||
<icon
|
||||
name="branch"
|
||||
css-classes="append-right-5"
|
||||
/>{{ currentBranchId }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="multi-file-commit-panel-inner-scroll">
|
||||
<component
|
||||
:is="currentActivityView"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</resizable-panel>
|
||||
</template>
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
<script>
|
||||
import { mapGetters, mapState } from 'vuex';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
import RepoFile from './repo_file.vue';
|
||||
import NewDropdown from './new_dropdown/index.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
RepoFile,
|
||||
SkeletonLoadingContainer,
|
||||
NewDropdown,
|
||||
},
|
||||
props: {
|
||||
tree: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['currentBranchId']),
|
||||
...mapGetters(['currentProject', 'currentTree']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -20,7 +23,7 @@ export default {
|
|||
<div
|
||||
class="ide-file-list"
|
||||
>
|
||||
<template v-if="tree.loading">
|
||||
<template v-if="!currentTree || currentTree.loading">
|
||||
<div
|
||||
class="multi-file-loading-container"
|
||||
v-for="n in 3"
|
||||
|
@ -30,8 +33,16 @@ export default {
|
|||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<header class="ide-tree-header">
|
||||
{{ __('Edit') }}
|
||||
<new-dropdown
|
||||
:project-id="currentProject.name_with_namespace"
|
||||
:branch="currentBranchId"
|
||||
path=""
|
||||
/>
|
||||
</header>
|
||||
<repo-file
|
||||
v-for="file in tree.tree"
|
||||
v-for="file in currentTree.tree"
|
||||
:key="file.key"
|
||||
:file="file"
|
||||
:level="0"
|
|
@ -23,18 +23,8 @@ export default {
|
|||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
noChangesStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
committedStateSvgPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['changedFiles', 'stagedFiles', 'rightPanelCollapsed']),
|
||||
...mapState(['changedFiles', 'stagedFiles']),
|
||||
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
|
||||
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled', 'branchName']),
|
||||
},
|
||||
|
@ -86,13 +76,11 @@ export default {
|
|||
action="unstageAllChanges"
|
||||
:action-btn-text="__('Unstage all')"
|
||||
item-action-component="unstage-button"
|
||||
:show-toggle="false"
|
||||
:staged-list="true"
|
||||
/>
|
||||
<form
|
||||
class="form-horizontal multi-file-commit-form"
|
||||
@submit.prevent.stop="commitChanges"
|
||||
v-if="!rightPanelCollapsed"
|
||||
>
|
||||
<commit-message-field
|
||||
:text="commitMessage"
|
||||
|
@ -120,8 +108,6 @@ export default {
|
|||
</template>
|
||||
<empty-state
|
||||
v-else
|
||||
:no-changes-state-svg-path="noChangesStateSvgPath"
|
||||
:committed-state-svg-path="committedStateSvgPath"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -66,10 +66,26 @@ export default {
|
|||
|
||||
<template>
|
||||
<li
|
||||
:class="{
|
||||
active: tab.active
|
||||
}"
|
||||
@click="clickFile(tab)"
|
||||
@mouseover="mouseOverTab"
|
||||
@mouseout="mouseOutTab"
|
||||
>
|
||||
<div
|
||||
class="multi-file-tab"
|
||||
:title="tab.url"
|
||||
>
|
||||
<file-icon
|
||||
:file-name="tab.name"
|
||||
:size="16"
|
||||
/>
|
||||
{{ tab.name }}
|
||||
<file-status-icon
|
||||
:file="tab"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="multi-file-tab-close"
|
||||
|
@ -86,22 +102,5 @@ export default {
|
|||
:file="tab"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="multi-file-tab"
|
||||
:class="{
|
||||
active: tab.active
|
||||
}"
|
||||
:title="tab.url"
|
||||
>
|
||||
<file-icon
|
||||
:file-name="tab.name"
|
||||
:size="16"
|
||||
/>
|
||||
{{ tab.name }}
|
||||
<file-status-icon
|
||||
:file="tab"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
|
|
@ -6,3 +6,8 @@ export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
|
|||
// Commit message textarea
|
||||
export const MAX_TITLE_LENGTH = 50;
|
||||
export const MAX_BODY_LENGTH = 72;
|
||||
|
||||
export const activityBarViews = {
|
||||
edit: 'ide-tree',
|
||||
commit: 'commit-section',
|
||||
};
|
||||
|
|
|
@ -63,6 +63,8 @@ router.beforeEach((to, from, next) => {
|
|||
const fullProjectId = `${to.params.namespace}/${to.params.project}`;
|
||||
|
||||
if (to.params.branch) {
|
||||
store.dispatch('setCurrentBranchId', to.params.branch);
|
||||
|
||||
store.dispatch('getBranchData', {
|
||||
projectId: fullProjectId,
|
||||
branchId: to.params.branch,
|
||||
|
|
|
@ -14,15 +14,16 @@ function initIde(el) {
|
|||
components: {
|
||||
ide,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('ide', {
|
||||
props: {
|
||||
emptyStateSvgPath: el.dataset.emptyStateSvgPath,
|
||||
noChangesStateSvgPath: el.dataset.noChangesStateSvgPath,
|
||||
committedStateSvgPath: el.dataset.committedStateSvgPath,
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch('setEmptyStateSvgs', {
|
||||
emptyStateSvgPath: el.dataset.emptyStateSvgPath,
|
||||
noChangesStateSvgPath: el.dataset.noChangesStateSvgPath,
|
||||
committedStateSvgPath: el.dataset.committedStateSvgPath,
|
||||
});
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement('ide');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -33,10 +33,7 @@ export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const toggleRightPanelCollapsed = (
|
||||
{ dispatch, state },
|
||||
e = undefined,
|
||||
) => {
|
||||
export const toggleRightPanelCollapsed = ({ dispatch, state }, e = undefined) => {
|
||||
if (e) {
|
||||
$(e.currentTarget)
|
||||
.tooltip('hide')
|
||||
|
@ -137,6 +134,18 @@ export const updateDelayViewerUpdated = ({ commit }, delay) => {
|
|||
commit(types.UPDATE_DELAY_VIEWER_CHANGE, delay);
|
||||
};
|
||||
|
||||
export const updateActivityBarView = ({ commit }, view) => {
|
||||
commit(types.UPDATE_ACTIVITY_BAR_VIEW, view);
|
||||
};
|
||||
|
||||
export const setEmptyStateSvgs = ({ commit }, svgs) => {
|
||||
commit(types.SET_EMPTY_STATE_SVGS, svgs);
|
||||
};
|
||||
|
||||
export const setCurrentBranchId = ({ commit }, currentBranchId) => {
|
||||
commit(types.SET_CURRENT_BRANCH, currentBranchId);
|
||||
};
|
||||
|
||||
export const toggleFileFinder = ({ commit }, fileFindVisible) =>
|
||||
commit(types.TOGGLE_FILE_FINDER, fileFindVisible);
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ export const unstageChange = ({ commit }, path) => {
|
|||
};
|
||||
|
||||
export const openPendingTab = ({ commit, getters, dispatch, state }, { file, keyPrefix }) => {
|
||||
if (getters.activeFile && getters.activeFile === file && state.viewer === 'diff') {
|
||||
if (getters.activeFile && getters.activeFile.path === file.path && state.viewer === 'diff') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ export const getBranchData = (
|
|||
branch: data,
|
||||
});
|
||||
commit(types.SET_BRANCH_WORKING_REFERENCE, { projectId, branchId, reference: id });
|
||||
commit(types.SET_CURRENT_BRANCH, branchId);
|
||||
resolve(data);
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { __ } from '~/locale';
|
||||
|
||||
export const activeFile = state => state.openFiles.find(file => file.active) || null;
|
||||
|
||||
export const addedFiles = state => state.changedFiles.filter(f => f.tempFile);
|
||||
|
@ -30,16 +28,13 @@ export const currentMergeRequest = state => {
|
|||
return null;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-confusing-arrow
|
||||
export const collapseButtonIcon = state =>
|
||||
state.rightPanelCollapsed ? 'angle-double-left' : 'angle-double-right';
|
||||
export const currentProject = state => state.projects[state.currentProjectId];
|
||||
|
||||
export const currentTree = state =>
|
||||
state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
|
||||
|
||||
export const hasChanges = state => !!state.changedFiles.length || !!state.stagedFiles.length;
|
||||
|
||||
// eslint-disable-next-line no-confusing-arrow
|
||||
export const collapseButtonTooltip = state =>
|
||||
state.rightPanelCollapsed ? __('Expand sidebar') : __('Collapse sidebar');
|
||||
|
||||
export const hasMergeRequest = state => !!state.currentMergeRequestId;
|
||||
|
||||
export const allBlobs = state =>
|
||||
|
|
|
@ -5,6 +5,7 @@ export const SET_LAST_COMMIT_MSG = 'SET_LAST_COMMIT_MSG';
|
|||
export const SET_LEFT_PANEL_COLLAPSED = 'SET_LEFT_PANEL_COLLAPSED';
|
||||
export const SET_RIGHT_PANEL_COLLAPSED = 'SET_RIGHT_PANEL_COLLAPSED';
|
||||
export const SET_RESIZING_STATUS = 'SET_RESIZING_STATUS';
|
||||
export const SET_EMPTY_STATE_SVGS = 'SET_EMPTY_STATE_SVGS';
|
||||
|
||||
// Project Mutation Types
|
||||
export const SET_PROJECT = 'SET_PROJECT';
|
||||
|
@ -59,4 +60,5 @@ export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
|
|||
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
|
||||
export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
|
||||
|
||||
export const UPDATE_ACTIVITY_BAR_VIEW = 'UPDATE_ACTIVITY_BAR_VIEW';
|
||||
export const TOGGLE_FILE_FINDER = 'TOGGLE_FILE_FINDER';
|
||||
|
|
|
@ -100,6 +100,21 @@ export default {
|
|||
delayViewerUpdated,
|
||||
});
|
||||
},
|
||||
[types.UPDATE_ACTIVITY_BAR_VIEW](state, currentActivityView) {
|
||||
Object.assign(state, {
|
||||
currentActivityView,
|
||||
});
|
||||
},
|
||||
[types.SET_EMPTY_STATE_SVGS](
|
||||
state,
|
||||
{ emptyStateSvgPath, noChangesStateSvgPath, committedStateSvgPath },
|
||||
) {
|
||||
Object.assign(state, {
|
||||
emptyStateSvgPath,
|
||||
noChangesStateSvgPath,
|
||||
committedStateSvgPath,
|
||||
});
|
||||
},
|
||||
[types.TOGGLE_FILE_FINDER](state, fileFindVisible) {
|
||||
Object.assign(state, {
|
||||
fileFindVisible,
|
||||
|
|
|
@ -192,6 +192,10 @@ export default {
|
|||
|
||||
return acc.concat(f);
|
||||
}, []);
|
||||
} else {
|
||||
openFiles = state.openFiles.map(f =>
|
||||
Object.assign(f, { active: f.key === key, opened: f.key === key }),
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(state, { openFiles });
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { activityBarViews } from '../constants';
|
||||
|
||||
export default () => ({
|
||||
currentProjectId: '',
|
||||
currentBranchId: '',
|
||||
|
@ -18,5 +20,6 @@ export default () => ({
|
|||
entries: {},
|
||||
viewer: 'editor',
|
||||
delayViewerUpdated: false,
|
||||
currentActivityView: activityBarViews.edit,
|
||||
fileFindVisible: false,
|
||||
});
|
||||
|
|
|
@ -177,25 +177,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Web IDE
|
||||
.ide-sidebar-link {
|
||||
color: $color-200;
|
||||
background-color: $color-700;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $color-500;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: $color-800;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-container {
|
||||
border-left-color: $color-700;
|
||||
}
|
||||
|
||||
.branch-header-title {
|
||||
color: $color-700;
|
||||
}
|
||||
|
@ -203,6 +184,18 @@
|
|||
.ide-file-list .file.file-active {
|
||||
color: $color-700;
|
||||
}
|
||||
|
||||
.ide-sidebar-link {
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.active {
|
||||
color: $color-700;
|
||||
}
|
||||
|
||||
&.active {
|
||||
box-shadow: inset 3px 0 $color-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -343,9 +336,5 @@ body {
|
|||
.sidebar-top-level-items > li.active .badge {
|
||||
color: $theme-gray-900;
|
||||
}
|
||||
|
||||
.ide-sidebar-link {
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: inherit;
|
||||
line-height: 22px;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
|
@ -78,7 +79,6 @@
|
|||
.ide-new-btn {
|
||||
display: none;
|
||||
margin-bottom: -4px;
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
|
@ -111,20 +111,12 @@
|
|||
.file-col-commit-message {
|
||||
display: flex;
|
||||
overflow: visible;
|
||||
padding: 6px 12px;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.multi-file-loading-container {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.animation-container {
|
||||
background: $gray-light;
|
||||
|
||||
div {
|
||||
background: $gray-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-table-col-commit-message {
|
||||
|
@ -151,7 +143,17 @@
|
|||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $grid-size $gl-padding;
|
||||
background-color: $gray-normal;
|
||||
border-right: 1px solid $white-dark;
|
||||
border-bottom: 1px solid $white-dark;
|
||||
|
||||
&.active {
|
||||
background-color: $white-light;
|
||||
border-bottom-color: $white-light;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
|
@ -174,41 +176,36 @@
|
|||
}
|
||||
|
||||
.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-bottom: 1px solid $white-dark;
|
||||
@include str-truncated(141px);
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $white-light;
|
||||
border-bottom-color: $white-light;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-tab-close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
margin-left: $grid-size;
|
||||
background: none;
|
||||
border: 0;
|
||||
border-radius: $border-radius-default;
|
||||
color: $theme-gray-900;
|
||||
transform: translateY(-50%);
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.ide-file-changed-icon {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
right: 3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $theme-gray-200;
|
||||
}
|
||||
|
@ -311,6 +308,7 @@
|
|||
|
||||
.multi-file-editor-holder {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
|
@ -429,27 +427,27 @@
|
|||
.multi-file-commit-panel {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
width: 340px;
|
||||
padding: 0;
|
||||
background-color: $gray-light;
|
||||
padding-right: 3px;
|
||||
padding-right: 1px;
|
||||
|
||||
.projects-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
.context-header {
|
||||
width: auto;
|
||||
margin-right: 0;
|
||||
|
||||
.context-header {
|
||||
width: auto;
|
||||
margin-right: 0;
|
||||
a:hover,
|
||||
a:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-commit-panel-inner {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.multi-file-commit-panel-inner-scroll {
|
||||
|
@ -457,68 +455,10 @@
|
|||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
width: 60px;
|
||||
|
||||
.multi-file-commit-list {
|
||||
padding-top: $gl-padding;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.multi-file-context-bar-icon {
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
float: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.branch-container {
|
||||
border-left: 4px solid;
|
||||
margin-bottom: $gl-bar-padding;
|
||||
}
|
||||
|
||||
.branch-header {
|
||||
background: $white-dark;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.branch-header-title {
|
||||
flex: 1;
|
||||
padding: $grid-size $gl-padding;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-header-btns {
|
||||
padding: $gl-vert-padding $gl-padding;
|
||||
}
|
||||
|
||||
.left-collapse-btn {
|
||||
display: none;
|
||||
background: $gray-light;
|
||||
text-align: left;
|
||||
background-color: $white-light;
|
||||
border-left: 1px solid $white-dark;
|
||||
border-top: 1px solid $white-dark;
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-context-bar-icon {
|
||||
padding: 10px;
|
||||
|
||||
svg {
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
border-top-left-radius: $border-radius-small;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -802,7 +742,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
width: 1px;
|
||||
background-color: $white-dark;
|
||||
|
||||
&.dragright {
|
||||
|
@ -816,32 +756,11 @@
|
|||
|
||||
.ide-commit-list-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
min-height: 140px;
|
||||
padding: 0 16px;
|
||||
|
||||
&:not(.is-collapsed) {
|
||||
flex: 1;
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
&.is-collapsed {
|
||||
.multi-file-commit-panel-header {
|
||||
margin-left: -$gl-padding;
|
||||
margin-right: -$gl-padding;
|
||||
|
||||
svg {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.multi-file-commit-panel-collapse-btn {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ide-staged-action-btn {
|
||||
|
@ -864,17 +783,55 @@
|
|||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.ide-external-links {
|
||||
p {
|
||||
margin: 0;
|
||||
.ide-sidebar-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
padding: 0 $gl-padding;
|
||||
color: $gl-text-color-secondary;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
outline: 0;
|
||||
|
||||
svg {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $theme-gray-100;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: $theme-gray-200;
|
||||
}
|
||||
|
||||
&.active {
|
||||
// extend width over border of sidebar section
|
||||
width: calc(100% + 1px);
|
||||
padding-right: $gl-padding + 1px;
|
||||
background-color: $white-light;
|
||||
border-top-color: $white-dark;
|
||||
border-bottom-color: $white-dark;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ide-sidebar-link {
|
||||
padding: $gl-padding-8 $gl-padding;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
.ide-activity-bar {
|
||||
position: relative;
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
.ide-file-finder-overlay {
|
||||
|
@ -967,3 +924,26 @@
|
|||
background: transparent;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.ide-tree-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
border-bottom: 1px solid $white-dark;
|
||||
|
||||
.ide-new-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ide-sidebar-branch-title {
|
||||
font-weight: $gl-font-weight-normal;
|
||||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,12 +44,16 @@ feature 'Multi-file editor new directory', :js do
|
|||
|
||||
wait_for_requests
|
||||
|
||||
find('.js-ide-commit-mode').click
|
||||
|
||||
click_button 'Stage all'
|
||||
|
||||
fill_in('commit-message', with: 'commit message ide')
|
||||
|
||||
click_button('Commit')
|
||||
|
||||
find('.js-ide-edit-mode').click
|
||||
|
||||
expect(page).to have_content('folder name')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,6 +34,8 @@ feature 'Multi-file editor new file', :js do
|
|||
|
||||
wait_for_requests
|
||||
|
||||
find('.js-ide-commit-mode').click
|
||||
|
||||
click_button 'Stage all'
|
||||
|
||||
fill_in('commit-message', with: 'commit message ide')
|
||||
|
|
86
spec/javascripts/ide/components/activity_bar_spec.js
Normal file
86
spec/javascripts/ide/components/activity_bar_spec.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
import Vue from 'vue';
|
||||
import store from '~/ide/stores';
|
||||
import { activityBarViews } from '~/ide/constants';
|
||||
import ActivityBar from '~/ide/components/activity_bar.vue';
|
||||
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
|
||||
import { resetStore } from '../helpers';
|
||||
|
||||
describe('IDE activity bar', () => {
|
||||
const Component = Vue.extend(ActivityBar);
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
Vue.set(store.state.projects, 'abcproject', {
|
||||
web_url: 'testing',
|
||||
});
|
||||
Vue.set(store.state, 'currentProjectId', 'abcproject');
|
||||
|
||||
vm = createComponentWithStore(Component, store);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
|
||||
resetStore(vm.$store);
|
||||
});
|
||||
|
||||
describe('goBackUrl', () => {
|
||||
it('renders the Go Back link with the referrer when present', () => {
|
||||
const fakeReferrer = '/example/README.md';
|
||||
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
|
||||
|
||||
vm.$mount();
|
||||
|
||||
expect(vm.goBackUrl).toEqual(fakeReferrer);
|
||||
});
|
||||
|
||||
it('renders the Go Back link with the project url when referrer is not present', () => {
|
||||
const fakeReferrer = '';
|
||||
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
|
||||
|
||||
vm.$mount();
|
||||
|
||||
expect(vm.goBackUrl).toEqual('testing');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateActivityBarView', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(vm, 'updateActivityBarView');
|
||||
|
||||
vm.$mount();
|
||||
});
|
||||
|
||||
it('calls updateActivityBarView with edit value on click', () => {
|
||||
vm.$el.querySelector('.js-ide-edit-mode').click();
|
||||
|
||||
expect(vm.updateActivityBarView).toHaveBeenCalledWith(activityBarViews.edit);
|
||||
});
|
||||
|
||||
it('calls updateActivityBarView with commit value on click', () => {
|
||||
vm.$el.querySelector('.js-ide-commit-mode').click();
|
||||
|
||||
expect(vm.updateActivityBarView).toHaveBeenCalledWith(activityBarViews.commit);
|
||||
});
|
||||
});
|
||||
|
||||
describe('active item', () => {
|
||||
beforeEach(() => {
|
||||
vm.$mount();
|
||||
});
|
||||
|
||||
it('sets edit item active', () => {
|
||||
expect(vm.$el.querySelector('.js-ide-edit-mode').classList).toContain('active');
|
||||
});
|
||||
|
||||
it('sets commit item active', done => {
|
||||
vm.$store.state.currentActivityView = activityBarViews.commit;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.js-ide-commit-mode').classList).toContain('active');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -10,10 +10,10 @@ describe('IDE commit panel empty state', () => {
|
|||
beforeEach(() => {
|
||||
const Component = Vue.extend(emptyState);
|
||||
|
||||
vm = createComponentWithStore(Component, store, {
|
||||
noChangesStateSvgPath: 'no-changes',
|
||||
committedStateSvgPath: 'committed-state',
|
||||
});
|
||||
Vue.set(store.state, 'noChangesStateSvgPath', 'no-changes');
|
||||
Vue.set(store.state, 'committedStateSvgPath', 'committed-state');
|
||||
|
||||
vm = createComponentWithStore(Component, store);
|
||||
|
||||
vm.$mount();
|
||||
});
|
||||
|
@ -27,9 +27,7 @@ describe('IDE commit panel empty state', () => {
|
|||
describe('statusSvg', () => {
|
||||
it('uses noChangesStateSvgPath when commit message is empty', () => {
|
||||
expect(vm.statusSvg).toBe('no-changes');
|
||||
expect(vm.$el.querySelector('img').getAttribute('src')).toBe(
|
||||
'no-changes',
|
||||
);
|
||||
expect(vm.$el.querySelector('img').getAttribute('src')).toBe('no-changes');
|
||||
});
|
||||
|
||||
it('uses committedStateSvgPath when commit message exists', done => {
|
||||
|
@ -37,9 +35,7 @@ describe('IDE commit panel empty state', () => {
|
|||
|
||||
Vue.nextTick(() => {
|
||||
expect(vm.statusSvg).toBe('committed-state');
|
||||
expect(vm.$el.querySelector('img').getAttribute('src')).toBe(
|
||||
'committed-state',
|
||||
);
|
||||
expect(vm.$el.querySelector('img').getAttribute('src')).toBe('committed-state');
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -59,37 +55,4 @@ describe('IDE commit panel empty state', () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggle button', () => {
|
||||
it('calls store action', () => {
|
||||
spyOn(vm, 'toggleRightPanelCollapsed');
|
||||
|
||||
vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
|
||||
|
||||
expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders collapsed class', done => {
|
||||
vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('collapsed state', () => {
|
||||
beforeEach(done => {
|
||||
vm.$store.state.rightPanelCollapsed = true;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('does not render text & svg', () => {
|
||||
expect(vm.$el.querySelector('img')).toBeNull();
|
||||
expect(vm.$el.textContent).not.toContain('No changes');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -50,35 +50,6 @@ describe('Multi-file editor commit sidebar list', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('collapsed', () => {
|
||||
beforeEach(done => {
|
||||
vm.$store.state.rightPanelCollapsed = true;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('hides list', () => {
|
||||
expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
|
||||
expect(vm.$el.querySelector('.help-block')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with toggle', () => {
|
||||
beforeEach(done => {
|
||||
spyOn(vm, 'toggleRightPanelCollapsed');
|
||||
|
||||
vm.showToggle = true;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('calls setPanelCollapsedStatus when clickin toggle', () => {
|
||||
vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
|
||||
|
||||
expect(vm.toggleRightPanelCollapsed).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('action button', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(vm, 'stageAllChanges');
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import store from '~/ide/stores';
|
||||
import ideContextBar from '~/ide/components/ide_context_bar.vue';
|
||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
describe('Multi-file editor right context bar', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
const Component = Vue.extend(ideContextBar);
|
||||
|
||||
vm = createComponentWithStore(Component, store, {
|
||||
noChangesStateSvgPath: 'svg',
|
||||
committedStateSvgPath: 'svg',
|
||||
});
|
||||
|
||||
vm.$store.state.rightPanelCollapsed = false;
|
||||
|
||||
vm.$mount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
describe('collapsed', () => {
|
||||
beforeEach(done => {
|
||||
vm.$store.state.rightPanelCollapsed = true;
|
||||
|
||||
Vue.nextTick(done);
|
||||
});
|
||||
|
||||
it('adds collapsed class', () => {
|
||||
expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import ideExternalLinks from '~/ide/components/ide_external_links.vue';
|
||||
import createComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
describe('ide external links component', () => {
|
||||
let vm;
|
||||
let fakeReferrer;
|
||||
let Component;
|
||||
|
||||
const fakeProjectUrl = '/project/';
|
||||
|
||||
beforeEach(() => {
|
||||
Component = Vue.extend(ideExternalLinks);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
describe('goBackUrl', () => {
|
||||
it('renders the Go Back link with the referrer when present', () => {
|
||||
fakeReferrer = '/example/README.md';
|
||||
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
|
||||
|
||||
vm = createComponent(Component, {
|
||||
projectUrl: fakeProjectUrl,
|
||||
}).$mount();
|
||||
|
||||
expect(vm.goBackUrl).toEqual(fakeReferrer);
|
||||
});
|
||||
|
||||
it('renders the Go Back link with the project url when referrer is not present', () => {
|
||||
fakeReferrer = '';
|
||||
spyOnProperty(document, 'referrer').and.returnValue(fakeReferrer);
|
||||
|
||||
vm = createComponent(Component, {
|
||||
projectUrl: fakeProjectUrl,
|
||||
}).$mount();
|
||||
|
||||
expect(vm.goBackUrl).toEqual(fakeProjectUrl);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import ProjectTree from '~/ide/components/ide_project_tree.vue';
|
||||
import createComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
describe('IDE project tree', () => {
|
||||
const Component = Vue.extend(ProjectTree);
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = createComponent(Component, {
|
||||
project: {
|
||||
id: 1,
|
||||
name: 'test',
|
||||
web_url: gl.TEST_HOST,
|
||||
avatar_url: '',
|
||||
branches: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders identicon when projct has no avatar', () => {
|
||||
expect(vm.$el.querySelector('.identicon')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders avatar image if project has avatar', done => {
|
||||
vm.project.avatar_url = gl.TEST_HOST;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.identicon')).toBeNull();
|
||||
expect(vm.$el.querySelector('img.avatar')).not.toBeNull();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import ideRepoTree from '~/ide/components/ide_repo_tree.vue';
|
||||
import createComponent from '../../helpers/vue_mount_component_helper';
|
||||
import { file } from '../helpers';
|
||||
|
||||
describe('IdeRepoTree', () => {
|
||||
let vm;
|
||||
let tree;
|
||||
|
||||
beforeEach(() => {
|
||||
const IdeRepoTree = Vue.extend(ideRepoTree);
|
||||
|
||||
tree = {
|
||||
tree: [file()],
|
||||
loading: false,
|
||||
};
|
||||
|
||||
vm = createComponent(IdeRepoTree, {
|
||||
tree,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders a sidebar', () => {
|
||||
expect(vm.$el.querySelector('.loading-file')).toBeNull();
|
||||
expect(vm.$el.querySelector('.file')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders 3 loading files if tree is loading', done => {
|
||||
tree.loading = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(
|
||||
vm.$el.querySelectorAll('.multi-file-loading-container').length,
|
||||
).toEqual(3);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,10 @@
|
|||
import Vue from 'vue';
|
||||
import store from '~/ide/stores';
|
||||
import ideSidebar from '~/ide/components/ide_side_bar.vue';
|
||||
import { activityBarViews } from '~/ide/constants';
|
||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import { resetStore } from '../helpers';
|
||||
import { projectData } from '../mock_data';
|
||||
|
||||
describe('IdeSidebar', () => {
|
||||
let vm;
|
||||
|
@ -10,6 +12,9 @@ describe('IdeSidebar', () => {
|
|||
beforeEach(() => {
|
||||
const Component = Vue.extend(ideSidebar);
|
||||
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.projects.abcproject = projectData;
|
||||
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
});
|
||||
|
||||
|
@ -20,23 +25,33 @@ describe('IdeSidebar', () => {
|
|||
});
|
||||
|
||||
it('renders a sidebar', () => {
|
||||
expect(
|
||||
vm.$el.querySelector('.multi-file-commit-panel-inner'),
|
||||
).not.toBeNull();
|
||||
expect(vm.$el.querySelector('.multi-file-commit-panel-inner')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders loading icon component', done => {
|
||||
vm.$store.state.loading = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(
|
||||
vm.$el.querySelector('.multi-file-loading-container'),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
vm.$el.querySelectorAll('.multi-file-loading-container').length,
|
||||
).toBe(3);
|
||||
expect(vm.$el.querySelector('.multi-file-loading-container')).not.toBeNull();
|
||||
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('activityBarComponent', () => {
|
||||
it('renders tree component', () => {
|
||||
expect(vm.$el.querySelector('.ide-file-list')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('renders commit component', done => {
|
||||
vm.$store.state.currentActivityView = activityBarViews.commit;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.multi-file-commit-panel-section')).not.toBeNull();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import store from '~/ide/stores';
|
|||
import ide from '~/ide/components/ide.vue';
|
||||
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
|
||||
import { file, resetStore } from '../helpers';
|
||||
import { projectData } from '../mock_data';
|
||||
|
||||
describe('ide component', () => {
|
||||
let vm;
|
||||
|
@ -11,6 +12,10 @@ describe('ide component', () => {
|
|||
beforeEach(() => {
|
||||
const Component = Vue.extend(ide);
|
||||
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.currentBranchId = 'master';
|
||||
store.state.projects.abcproject = Object.assign({}, projectData);
|
||||
|
||||
vm = createComponentWithStore(Component, store, {
|
||||
emptyStateSvgPath: 'svg',
|
||||
noChangesStateSvgPath: 'svg',
|
||||
|
@ -24,11 +29,11 @@ describe('ide component', () => {
|
|||
resetStore(vm.$store);
|
||||
});
|
||||
|
||||
it('does not render panel right when no files open', () => {
|
||||
it('does not render right right when no files open', () => {
|
||||
expect(vm.$el.querySelector('.panel-right')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders panel right when files are open', done => {
|
||||
it('renders right panel when files are open', done => {
|
||||
vm.$store.state.trees['abcproject/mybranch'] = {
|
||||
tree: [file()],
|
||||
};
|
||||
|
|
43
spec/javascripts/ide/components/ide_tree_spec.js
Normal file
43
spec/javascripts/ide/components/ide_tree_spec.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import Vue from 'vue';
|
||||
import IdeTree from '~/ide/components/ide_tree.vue';
|
||||
import store from '~/ide/stores';
|
||||
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
|
||||
import { resetStore, file } from '../helpers';
|
||||
import { projectData } from '../mock_data';
|
||||
|
||||
describe('IdeRepoTree', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
const IdeRepoTree = Vue.extend(IdeTree);
|
||||
|
||||
store.state.currentProjectId = 'abcproject';
|
||||
store.state.currentBranchId = 'master';
|
||||
store.state.projects.abcproject = Object.assign({}, projectData);
|
||||
Vue.set(store.state.trees, 'abcproject/master', {
|
||||
tree: [file('fileName')],
|
||||
loading: false,
|
||||
});
|
||||
|
||||
vm = createComponentWithStore(IdeRepoTree, store).$mount();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
|
||||
resetStore(vm.$store);
|
||||
});
|
||||
|
||||
it('renders loading', done => {
|
||||
vm.currentTree.loading = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelectorAll('.multi-file-loading-container').length).toBe(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders list of files', () => {
|
||||
expect(vm.$el.textContent).toContain('fileName');
|
||||
});
|
||||
});
|
|
@ -12,10 +12,10 @@ describe('RepoCommitSection', () => {
|
|||
function createComponent() {
|
||||
const Component = Vue.extend(repoCommitSection);
|
||||
|
||||
vm = createComponentWithStore(Component, store, {
|
||||
noChangesStateSvgPath: 'svg',
|
||||
committedStateSvgPath: 'commitsvg',
|
||||
});
|
||||
store.state.noChangesStateSvgPath = 'svg';
|
||||
store.state.committedStateSvgPath = 'commitsvg';
|
||||
|
||||
vm = createComponentWithStore(Component, store);
|
||||
|
||||
vm.$store.state.currentProjectId = 'abcproject';
|
||||
vm.$store.state.currentBranchId = 'master';
|
||||
|
@ -93,28 +93,20 @@ describe('RepoCommitSection', () => {
|
|||
resetStore(vm.$store);
|
||||
const Component = Vue.extend(repoCommitSection);
|
||||
|
||||
vm = createComponentWithStore(Component, store, {
|
||||
noChangesStateSvgPath: 'nochangessvg',
|
||||
committedStateSvgPath: 'svg',
|
||||
}).$mount();
|
||||
store.state.noChangesStateSvgPath = 'nochangessvg';
|
||||
store.state.committedStateSvgPath = 'svg';
|
||||
|
||||
expect(
|
||||
vm.$el.querySelector('.js-empty-state').textContent.trim(),
|
||||
).toContain('No changes');
|
||||
expect(
|
||||
vm.$el.querySelector('.js-empty-state img').getAttribute('src'),
|
||||
).toBe('nochangessvg');
|
||||
vm = createComponentWithStore(Component, store).$mount();
|
||||
|
||||
expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes');
|
||||
expect(vm.$el.querySelector('.js-empty-state img').getAttribute('src')).toBe('nochangessvg');
|
||||
});
|
||||
});
|
||||
|
||||
it('renders a commit section', () => {
|
||||
const changedFileElements = [
|
||||
...vm.$el.querySelectorAll('.multi-file-commit-list li'),
|
||||
];
|
||||
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
|
||||
const submitCommit = vm.$el.querySelector('form .btn');
|
||||
const allFiles = vm.$store.state.changedFiles.concat(
|
||||
vm.$store.state.stagedFiles,
|
||||
);
|
||||
const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
|
||||
|
||||
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
|
||||
expect(changedFileElements.length).toEqual(4);
|
||||
|
@ -131,9 +123,9 @@ describe('RepoCommitSection', () => {
|
|||
vm.$el.querySelector('.ide-staged-action-btn').click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el.querySelector('.ide-commit-list-container').textContent,
|
||||
).toContain('No changes');
|
||||
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).toContain(
|
||||
'No changes',
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -143,11 +135,9 @@ describe('RepoCommitSection', () => {
|
|||
vm.$el.querySelector('.multi-file-discard-btn .btn').click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el
|
||||
.querySelector('.ide-commit-list-container')
|
||||
.querySelectorAll('li').length,
|
||||
).toBe(1);
|
||||
expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -157,14 +147,10 @@ describe('RepoCommitSection', () => {
|
|||
vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el.querySelector('.ide-commit-list-container').textContent,
|
||||
).not.toContain('file1');
|
||||
expect(
|
||||
vm.$el
|
||||
.querySelector('.ide-commit-list-container')
|
||||
.querySelectorAll('li').length,
|
||||
).toBe(1);
|
||||
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
|
||||
expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
|
||||
1,
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -174,9 +160,9 @@ describe('RepoCommitSection', () => {
|
|||
vm.$el.querySelectorAll('.ide-staged-action-btn')[1].click();
|
||||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el.querySelectorAll('.ide-commit-list-container')[1].textContent,
|
||||
).toContain('No changes');
|
||||
expect(vm.$el.querySelectorAll('.ide-commit-list-container')[1].textContent).toContain(
|
||||
'No changes',
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
|
@ -190,9 +176,7 @@ describe('RepoCommitSection', () => {
|
|||
|
||||
Vue.nextTick(() => {
|
||||
expect(
|
||||
vm.$el
|
||||
.querySelectorAll('.ide-commit-list-container')[1]
|
||||
.querySelectorAll('li').length,
|
||||
vm.$el.querySelectorAll('.ide-commit-list-container')[1].querySelectorAll('li').length,
|
||||
).toBe(1);
|
||||
|
||||
done();
|
||||
|
@ -208,9 +192,7 @@ describe('RepoCommitSection', () => {
|
|||
|
||||
getSetTimeoutPromise()
|
||||
.then(() => {
|
||||
expect(vm.$store.state.commit.commitMessage).toBe(
|
||||
'testing commit message',
|
||||
);
|
||||
expect(vm.$store.state.commit.commitMessage).toBe('testing commit message');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
@ -218,9 +200,7 @@ describe('RepoCommitSection', () => {
|
|||
|
||||
describe('discard draft button', () => {
|
||||
it('hidden when commitMessage is empty', () => {
|
||||
expect(
|
||||
vm.$el.querySelector('.multi-file-commit-form .btn-default'),
|
||||
).toBeNull();
|
||||
expect(vm.$el.querySelector('.multi-file-commit-form .btn-default')).toBeNull();
|
||||
});
|
||||
|
||||
it('resets commitMessage when clicking discard button', done => {
|
||||
|
@ -232,9 +212,7 @@ describe('RepoCommitSection', () => {
|
|||
})
|
||||
.then(Vue.nextTick)
|
||||
.then(() => {
|
||||
expect(vm.$store.state.commit.commitMessage).not.toBe(
|
||||
'testing commit message',
|
||||
);
|
||||
expect(vm.$store.state.commit.commitMessage).not.toBe('testing commit message');
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
|
|
|
@ -26,8 +26,8 @@ describe('RepoTabs', () => {
|
|||
const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
|
||||
|
||||
expect(tabs.length).toEqual(2);
|
||||
expect(tabs[0].classList.contains('active')).toEqual(true);
|
||||
expect(tabs[1].classList.contains('active')).toEqual(false);
|
||||
expect(tabs[0].parentNode.classList.contains('active')).toEqual(true);
|
||||
expect(tabs[1].parentNode.classList.contains('active')).toEqual(false);
|
||||
|
||||
done();
|
||||
});
|
||||
|
|
14
spec/javascripts/ide/mock_data.js
Normal file
14
spec/javascripts/ide/mock_data.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const projectData = {
|
||||
id: 1,
|
||||
name: 'abcproject',
|
||||
web_url: '',
|
||||
avatar_url: '',
|
||||
path: '',
|
||||
name_with_namespace: 'namespace/abcproject',
|
||||
branches: {
|
||||
master: {
|
||||
treeId: 'abcproject/master',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,4 +1,11 @@
|
|||
import actions, { stageAllChanges, unstageAllChanges, toggleFileFinder } from '~/ide/stores/actions';
|
||||
import actions, {
|
||||
stageAllChanges,
|
||||
unstageAllChanges,
|
||||
toggleFileFinder,
|
||||
setCurrentBranchId,
|
||||
setEmptyStateSvgs,
|
||||
updateActivityBarView,
|
||||
} from '~/ide/stores/actions';
|
||||
import store from '~/ide/stores';
|
||||
import * as types from '~/ide/stores/mutation_types';
|
||||
import router from '~/ide/ide_router';
|
||||
|
@ -340,6 +347,45 @@ describe('Multi-file store actions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('updateActivityBarView', () => {
|
||||
it('commits UPDATE_ACTIVITY_BAR_VIEW', done => {
|
||||
testAction(
|
||||
updateActivityBarView,
|
||||
'test',
|
||||
{},
|
||||
[{ type: 'UPDATE_ACTIVITY_BAR_VIEW', payload: 'test' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setEmptyStateSvgs', () => {
|
||||
it('commits setEmptyStateSvgs', done => {
|
||||
testAction(
|
||||
setEmptyStateSvgs,
|
||||
'svg',
|
||||
{},
|
||||
[{ type: 'SET_EMPTY_STATE_SVGS', payload: 'svg' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setCurrentBranchId', () => {
|
||||
it('commits setCurrentBranchId', done => {
|
||||
testAction(
|
||||
setCurrentBranchId,
|
||||
'branchId',
|
||||
{},
|
||||
[{ type: 'SET_CURRENT_BRANCH', payload: 'branchId' }],
|
||||
[],
|
||||
done,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleFileFinder', () => {
|
||||
it('commits TOGGLE_FILE_FINDER', done => {
|
||||
testAction(
|
||||
|
|
|
@ -37,12 +37,6 @@ describe('IDE store getters', () => {
|
|||
expect(modifiedFiles.length).toBe(1);
|
||||
expect(modifiedFiles[0].name).toBe('changed');
|
||||
});
|
||||
|
||||
it('returns angle left when collapsed', () => {
|
||||
localState.rightPanelCollapsed = true;
|
||||
|
||||
expect(getters.collapseButtonIcon(localState)).toBe('angle-double-left');
|
||||
});
|
||||
});
|
||||
|
||||
describe('currentMergeRequest', () => {
|
||||
|
|
|
@ -87,6 +87,28 @@ describe('Multi-file store mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('UPDATE_ACTIVITY_BAR_VIEW', () => {
|
||||
it('updates currentActivityBar', () => {
|
||||
mutations.UPDATE_ACTIVITY_BAR_VIEW(localState, 'test');
|
||||
|
||||
expect(localState.currentActivityView).toBe('test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_EMPTY_STATE_SVGS', () => {
|
||||
it('updates empty state SVGs', () => {
|
||||
mutations.SET_EMPTY_STATE_SVGS(localState, {
|
||||
emptyStateSvgPath: 'emptyState',
|
||||
noChangesStateSvgPath: 'noChanges',
|
||||
committedStateSvgPath: 'commited',
|
||||
});
|
||||
|
||||
expect(localState.emptyStateSvgPath).toBe('emptyState');
|
||||
expect(localState.noChangesStateSvgPath).toBe('noChanges');
|
||||
expect(localState.committedStateSvgPath).toBe('commited');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TOGGLE_FILE_FINDER', () => {
|
||||
it('updates fileFindVisible', () => {
|
||||
mutations.TOGGLE_FILE_FINDER(localState, true);
|
||||
|
|
Loading…
Reference in a new issue