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:
Filipa Lacerda 2018-04-27 08:15:58 +00:00
commit 26c241d955
43 changed files with 724 additions and 919 deletions

View 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>

View file

@ -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" />

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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"

View file

@ -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>

View file

@ -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>

View file

@ -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',
};

View file

@ -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,

View file

@ -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');
},
});
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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(() => {

View file

@ -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 =>

View file

@ -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';

View file

@ -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,

View file

@ -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 });

View file

@ -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,
});

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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

View file

@ -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')

View 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();
});
});
});
});

View file

@ -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');
});
});
});

View file

@ -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');

View file

@ -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();
});
});
});

View file

@ -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);
});
});
});

View file

@ -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();
});
});
});

View file

@ -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();
});
});
});

View file

@ -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();
});
});
});
});

View file

@ -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()],
};

View 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');
});
});

View file

@ -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);

View file

@ -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();
});

View 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',
},
},
};

View file

@ -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(

View file

@ -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', () => {

View file

@ -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);