Added add to tree dropdown to Vue files breadcrumb
This commit is contained in:
parent
4d8babc809
commit
1e3877a9c8
|
@ -1,12 +1,41 @@
|
||||||
<script>
|
<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 getRefMixin from '../mixins/get_ref';
|
||||||
import getProjectShortPath from '../queries/getProjectShortPath.query.graphql';
|
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 {
|
export default {
|
||||||
|
components: {
|
||||||
|
GlDropdown,
|
||||||
|
GlDropdownDivider,
|
||||||
|
GlDropdownHeader,
|
||||||
|
GlDropdownItem,
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
projectShortPath: {
|
projectShortPath: {
|
||||||
query: getProjectShortPath,
|
query: getProjectShortPath,
|
||||||
},
|
},
|
||||||
|
projectPath: {
|
||||||
|
query: getProjectPath,
|
||||||
|
},
|
||||||
|
userPermissions: {
|
||||||
|
query: getPermissions,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
projectPath: this.projectPath,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
update: data => data.project.userPermissions,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mixins: [getRefMixin],
|
mixins: [getRefMixin],
|
||||||
props: {
|
props: {
|
||||||
|
@ -15,10 +44,52 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: '/',
|
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() {
|
data() {
|
||||||
return {
|
return {
|
||||||
projectShortPath: '',
|
projectShortPath: '',
|
||||||
|
projectPath: '',
|
||||||
|
userPermissions: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -39,11 +110,112 @@ export default {
|
||||||
[{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}/` }],
|
[{ 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: {
|
methods: {
|
||||||
isLast(i) {
|
isLast(i) {
|
||||||
return i === this.pathLinks.length - 1;
|
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>
|
</script>
|
||||||
|
@ -56,6 +228,20 @@ export default {
|
||||||
{{ link.name }}
|
{{ link.name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</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>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Breadcrumbs from './components/breadcrumbs.vue';
|
||||||
import LastCommit from './components/last_commit.vue';
|
import LastCommit from './components/last_commit.vue';
|
||||||
import apolloProvider from './graphql';
|
import apolloProvider from './graphql';
|
||||||
import { setTitle } from './utils/title';
|
import { setTitle } from './utils/title';
|
||||||
|
import { parseBoolean } from '../lib/utils/common_utils';
|
||||||
|
|
||||||
export default function setupVueRepositoryList() {
|
export default function setupVueRepositoryList() {
|
||||||
const el = document.getElementById('js-tree-list');
|
const el = document.getElementById('js-tree-list');
|
||||||
|
@ -36,19 +37,42 @@ export default function setupVueRepositoryList() {
|
||||||
.forEach(elem => elem.classList.toggle('hidden', !isRoot));
|
.forEach(elem => elem.classList.toggle('hidden', !isRoot));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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
|
// eslint-disable-next-line no-new
|
||||||
new Vue({
|
new Vue({
|
||||||
el: document.getElementById('js-repo-breadcrumb'),
|
el: breadcrumbEl,
|
||||||
router,
|
router,
|
||||||
apolloProvider,
|
apolloProvider,
|
||||||
render(h) {
|
render(h) {
|
||||||
return h(Breadcrumbs, {
|
return h(Breadcrumbs, {
|
||||||
props: {
|
props: {
|
||||||
currentPath: this.$route.params.pathMatch,
|
currentPath: this.$route.params.pathMatch,
|
||||||
|
canCollaborate: parseBoolean(canCollaborate),
|
||||||
|
canEditTree: parseBoolean(canEditTree),
|
||||||
|
newBranchPath,
|
||||||
|
newTagPath,
|
||||||
|
newBlobPath,
|
||||||
|
forkNewBlobPath,
|
||||||
|
forkNewDirectoryPath,
|
||||||
|
forkUploadBlobPath,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
query getPermissions($projectPath: ID!) {
|
||||||
|
project(fullPath: $projectPath) {
|
||||||
|
userPermissions {
|
||||||
|
pushCode
|
||||||
|
forkProject
|
||||||
|
createMergeRequestIn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -154,4 +154,36 @@ module TreeHelper
|
||||||
"logs-path" => logs_path
|
"logs-path" => logs_path
|
||||||
}
|
}
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
- if vue_file_list_enabled?
|
- 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 } }
|
#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
|
- if @tree.readme
|
||||||
= render "projects/tree/readme", readme: @tree.readme
|
= render "projects/tree/readme", readme: @tree.readme
|
||||||
- else
|
- 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' }
|
- 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?
|
- if vue_file_list_enabled?
|
||||||
#js-repo-breadcrumb
|
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }
|
||||||
- else
|
- else
|
||||||
%ul.breadcrumb.repo-breadcrumb
|
%ul.breadcrumb.repo-breadcrumb
|
||||||
%li.breadcrumb-item
|
%li.breadcrumb-item
|
||||||
|
|
|
@ -138,9 +138,15 @@ msgstr[1] ""
|
||||||
msgid "%{edit_in_new_fork_notice} Try to cherry-pick this commit again."
|
msgid "%{edit_in_new_fork_notice} Try to cherry-pick this commit again."
|
||||||
msgstr ""
|
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."
|
msgid "%{edit_in_new_fork_notice} Try to revert this commit again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "%{edit_in_new_fork_notice} Try to upload a file again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "%{filePath} deleted"
|
msgid "%{filePath} deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -707,6 +713,9 @@ msgstr ""
|
||||||
msgid "Add to review"
|
msgid "Add to review"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Add to tree"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Add user(s) to the group:"
|
msgid "Add user(s) to the group:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
|
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
|
||||||
|
import { GlDropdown } from '@gitlab/ui';
|
||||||
import Breadcrumbs from '~/repository/components/breadcrumbs.vue';
|
import Breadcrumbs from '~/repository/components/breadcrumbs.vue';
|
||||||
|
|
||||||
let vm;
|
let vm;
|
||||||
|
|
||||||
function factory(currentPath) {
|
function factory(currentPath, extraProps = {}) {
|
||||||
vm = shallowMount(Breadcrumbs, {
|
vm = shallowMount(Breadcrumbs, {
|
||||||
propsData: {
|
propsData: {
|
||||||
currentPath,
|
currentPath,
|
||||||
|
...extraProps,
|
||||||
},
|
},
|
||||||
stubs: {
|
stubs: {
|
||||||
RouterLink: RouterLinkStub,
|
RouterLink: RouterLinkStub,
|
||||||
|
@ -41,4 +43,20 @@ describe('Repository breadcrumbs component', () => {
|
||||||
.attributes('aria-current'),
|
.attributes('aria-current'),
|
||||||
).toEqual('page');
|
).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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue