Commit 1e3877a9 authored by Phil Hughes's avatar Phil Hughes

Added add to tree dropdown to Vue files breadcrumb

parent 4d8babc8
<script>
import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
import { __ } from '../../locale';
import Icon from '../../vue_shared/components/icon.vue';
import getRefMixin from '../mixins/get_ref';
import getProjectShortPath from '../queries/getProjectShortPath.query.graphql';
import getProjectPath from '../queries/getProjectPath.query.graphql';
import getPermissions from '../queries/getPermissions.query.graphql';
const ROW_TYPES = {
header: 'header',
divider: 'divider',
};
export default {
components: {
GlDropdown,
GlDropdownDivider,
GlDropdownHeader,
GlDropdownItem,
Icon,
},
apollo: {
projectShortPath: {
query: getProjectShortPath,
},
projectPath: {
query: getProjectPath,
},
userPermissions: {
query: getPermissions,
variables() {
return {
projectPath: this.projectPath,
};
},
update: data => data.project.userPermissions,
},
},
mixins: [getRefMixin],
props: {
......@@ -15,10 +44,52 @@ export default {
required: false,
default: '/',
},
canCollaborate: {
type: Boolean,
required: false,
default: false,
},
canEditTree: {
type: Boolean,
required: false,
default: false,
},
newBranchPath: {
type: String,
required: false,
default: null,
},
newTagPath: {
type: String,
required: false,
default: null,
},
newBlobPath: {
type: String,
required: false,
default: null,
},
forkNewBlobPath: {
type: String,
required: false,
default: null,
},
forkNewDirectoryPath: {
type: String,
required: false,
default: null,
},
forkUploadBlobPath: {
type: String,
required: false,
default: null,
},
},
data() {
return {
projectShortPath: '',
projectPath: '',
userPermissions: {},
};
},
computed: {
......@@ -39,11 +110,112 @@ export default {
[{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}/` }],
);
},
canCreateMrFromFork() {
return this.userPermissions.forkProject && this.userPermissions.createMergeRequestIn;
},
dropdownItems() {
const items = [];
if (this.canEditTree) {
items.push(
{
type: ROW_TYPES.header,
text: __('This directory'),
},
{
attrs: {
href: this.newBlobPath,
class: 'qa-new-file-option',
},
text: __('New file'),
},
{
attrs: {
href: '#modal-upload-blob',
'data-target': '#modal-upload-blob',
'data-toggle': 'modal',
},
text: __('Upload file'),
},
{
attrs: {
href: '#modal-create-new-dir',
'data-target': '#modal-create-new-dir',
'data-toggle': 'modal',
},
text: __('New directory'),
},
);
} else if (this.canCreateMrFromFork) {
items.push(
{
attrs: {
href: this.forkNewBlobPath,
'data-method': 'post',
},
text: __('New file'),
},
{
attrs: {
href: this.forkUploadBlobPath,
'data-method': 'post',
},
text: __('Upload file'),
},
{
attrs: {
href: this.forkNewDirectoryPath,
'data-method': 'post',
},
text: __('New directory'),
},
);
}
if (this.userPermissions.pushCode) {
items.push(
{
type: ROW_TYPES.divider,
},
{
type: ROW_TYPES.header,
text: __('This repository'),
},
{
attrs: {
href: this.newBranchPath,
},
text: __('New branch'),
},
{
attrs: {
href: this.newTagPath,
},
text: __('New tag'),
},
);
}
return items;
},
renderAddToTreeDropdown() {
return this.canCollaborate || this.canCreateMrFromFork;
},
},
methods: {
isLast(i) {
return i === this.pathLinks.length - 1;
},
getComponent(type) {
switch (type) {
case ROW_TYPES.divider:
return 'gl-dropdown-divider';
case ROW_TYPES.header:
return 'gl-dropdown-header';
default:
return 'gl-dropdown-item';
}
},
},
};
</script>
......@@ -56,6 +228,20 @@ export default {
{{ link.name }}
</router-link>
</li>
<li v-if="renderAddToTreeDropdown" class="breadcrumb-item">
<gl-dropdown toggle-class="add-to-tree qa-add-to-tree ml-1">
<template slot="button-content">
<span class="sr-only">{{ __('Add to tree') }}</span>
<icon name="plus" :size="16" class="float-left" />
<icon name="arrow-down" :size="16" class="float-left" />
</template>
<template v-for="(item, i) in dropdownItems">
<component :is="getComponent(item.type)" :key="i" v-bind="item.attrs">
{{ item.text }}
</component>
</template>
</gl-dropdown>
</li>
</ol>
</nav>
</template>
......@@ -5,6 +5,7 @@ import Breadcrumbs from './components/breadcrumbs.vue';
import LastCommit from './components/last_commit.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
import { parseBoolean } from '../lib/utils/common_utils';
export default function setupVueRepositoryList() {
const el = document.getElementById('js-tree-list');
......@@ -36,19 +37,42 @@ export default function setupVueRepositoryList() {
.forEach(elem => elem.classList.toggle('hidden', !isRoot));
});
// eslint-disable-next-line no-new
new Vue({
el: document.getElementById('js-repo-breadcrumb'),
router,
apolloProvider,
render(h) {
return h(Breadcrumbs, {
props: {
currentPath: this.$route.params.pathMatch,
},
});
},
});
const breadcrumbEl = document.getElementById('js-repo-breadcrumb');
if (breadcrumbEl) {
const {
canCollaborate,
canEditTree,
newBranchPath,
newTagPath,
newBlobPath,
forkNewBlobPath,
forkNewDirectoryPath,
forkUploadBlobPath,
} = breadcrumbEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: breadcrumbEl,
router,
apolloProvider,
render(h) {
return h(Breadcrumbs, {
props: {
currentPath: this.$route.params.pathMatch,
canCollaborate: parseBoolean(canCollaborate),
canEditTree: parseBoolean(canEditTree),
newBranchPath,
newTagPath,
newBlobPath,
forkNewBlobPath,
forkNewDirectoryPath,
forkUploadBlobPath,
},
});
},
});
}
// eslint-disable-next-line no-new
new Vue({
......
query getPermissions($projectPath: ID!) {
project(fullPath: $projectPath) {
userPermissions {
pushCode
forkProject
createMergeRequestIn
}
}
}
......@@ -154,4 +154,36 @@ module TreeHelper
"logs-path" => logs_path
}
end
def breadcrumb_data_attributes
attrs = {
can_collaborate: can_collaborate_with_project?(@project).to_s,
new_blob_path: project_new_blob_path(@project, @id),
new_branch_path: new_project_branch_path(@project),
new_tag_path: new_project_tag_path(@project),
can_edit_tree: can_edit_tree?.to_s
}
if can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
continue_param = {
to: project_new_blob_path(@project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
attrs.merge!(
fork_new_blob_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param),
fork_new_directory_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param.merge({
to: request.fullpath,
notice: _("%{edit_in_new_fork_notice} Try to create a new directory again.") % { edit_in_new_fork_notice: edit_in_new_fork_notice }
})),
fork_upload_blob_path: project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_param.merge({
to: request.fullpath,
notice: _("%{edit_in_new_fork_notice} Try to upload a file again.") % { edit_in_new_fork_notice: edit_in_new_fork_notice }
}))
)
end
attrs
end
end
......@@ -20,6 +20,9 @@
- if vue_file_list_enabled?
#js-tree-list{ data: { project_path: @project.full_path, project_short_path: @project.path, ref: ref, full_name: @project.name_with_namespace } }
- if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post
= render 'projects/blob/new_dir'
- if @tree.readme
= render "projects/tree/readme", readme: @tree.readme
- else
......
......@@ -11,7 +11,7 @@
- addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' }
- if vue_file_list_enabled?
#js-repo-breadcrumb
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }
- else
%ul.breadcrumb.repo-breadcrumb
%li.breadcrumb-item
......
......@@ -138,9 +138,15 @@ msgstr[1] ""
msgid "%{edit_in_new_fork_notice} Try to cherry-pick this commit again."
msgstr ""
msgid "%{edit_in_new_fork_notice} Try to create a new directory again."
msgstr ""
msgid "%{edit_in_new_fork_notice} Try to revert this commit again."
msgstr ""
msgid "%{edit_in_new_fork_notice} Try to upload a file again."
msgstr ""
msgid "%{filePath} deleted"
msgstr ""
......@@ -707,6 +713,9 @@ msgstr ""
msgid "Add to review"
msgstr ""
msgid "Add to tree"
msgstr ""
msgid "Add user(s) to the group:"
msgstr ""
......
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlDropdown } from '@gitlab/ui';
import Breadcrumbs from '~/repository/components/breadcrumbs.vue';
let vm;
function factory(currentPath) {
function factory(currentPath, extraProps = {}) {
vm = shallowMount(Breadcrumbs, {
propsData: {
currentPath,
...extraProps,
},
stubs: {
RouterLink: RouterLinkStub,
......@@ -41,4 +43,20 @@ describe('Repository breadcrumbs component', () => {
.attributes('aria-current'),
).toEqual('page');
});
it('does not render add to tree dropdown when permissions are false', () => {
factory('/', { canCollaborate: false });
vm.setData({ userPermissions: { forkProject: false, createMergeRequestIn: false } });
expect(vm.find(GlDropdown).exists()).toBe(false);
});
it('renders add to tree dropdown when permissions are true', () => {
factory('/', { canCollaborate: true });
vm.setData({ userPermissions: { forkProject: true, createMergeRequestIn: true } });
expect(vm.find(GlDropdown).exists()).toBe(true);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment