Skip to content
Snippets Groups Projects
Unverified Commit 1224aa51 authored by Phil Hughes's avatar Phil Hughes
Browse files

Created repository list breadcrumbs Vue app

parent 4a938724
No related branches found
No related tags found
No related merge requests found
Showing
with 208 additions and 69 deletions
<script>
import getRefMixin from '../mixins/get_ref';
import getProjectShortPath from '../queries/getProjectShortPath.graphql';
export default {
apollo: {
projectShortPath: {
query: getProjectShortPath,
},
},
mixins: [getRefMixin],
props: {
currentPath: {
type: String,
required: false,
default: '/',
},
},
data() {
return {
projectShortPath: '',
};
},
computed: {
pathLinks() {
return this.currentPath
.split('/')
.filter(p => p !== '')
.reduce(
(acc, name, i) => {
const path = `${i > 0 ? acc[i].path : ''}/${name}`;
return acc.concat({
name,
path,
to: `/tree/${this.ref}${path}`,
});
},
[{ name: this.projectShortPath, path: '/', to: `/tree/${this.ref}` }],
);
},
},
methods: {
isLast(i) {
return i === this.pathLinks.length - 1;
},
},
};
</script>
<template>
<nav :aria-label="__('Files breadcrumb')">
<ol class="breadcrumb repo-breadcrumb">
<li v-for="(link, i) in pathLinks" :key="i" class="breadcrumb-item">
<router-link :to="link.to" :aria-current="isLast(i) ? 'page' : null">
{{ link.name }}
</router-link>
</li>
</ol>
</nav>
</template>
import Vue from 'vue';
import createRouter from './router';
import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
 
export default function setupVueRepositoryList() {
const el = document.getElementById('js-tree-list');
const { projectPath, ref, fullName } = el.dataset;
const { projectPath, projectShortPath, ref, fullName } = el.dataset;
const router = createRouter(projectPath, ref);
 
apolloProvider.clients.defaultClient.cache.writeData({
data: {
projectPath,
projectShortPath,
ref,
},
});
 
router.afterEach(({ params: { pathMatch } }) => setTitle(pathMatch, ref, fullName));
router.afterEach(to => {
const isRoot = to.params.pathMatch === undefined || to.params.pathMatch === '/';
router.afterEach(({ params: { pathMatch } }) => {
const isRoot = pathMatch === undefined || pathMatch === '/';
setTitle(pathMatch, ref, fullName);
 
if (!isRoot) {
document
Loading
Loading
@@ -31,6 +34,20 @@ 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,
},
});
},
});
return new Vue({
el,
router,
Loading
Loading
query getProjectShortPath {
projectShortPath @client
}
// eslint-disable-next-line import/prefer-default-export
export const setTitle = (pathMatch, ref, project) => {
if (!pathMatch) return;
const path = pathMatch.replace(/^\//, '');
const isEmpty = path === '';
 
Loading
Loading
Loading
Loading
@@ -655,4 +655,8 @@ module ProjectsHelper
project.builds_enabled? &&
!project.repository.gitlab_ci_yml
end
def vue_file_list_enabled?
Gitlab::Graphql.enabled? && Feature.enabled?(:vue_file_list, @project)
end
end
Loading
Loading
@@ -4,7 +4,6 @@
- project = local_assigns.fetch(:project) { @project }
- content_url = local_assigns.fetch(:content_url) { @tree.readme ? project_blob_path(@project, tree_join(@ref, @tree.readme.path)) : project_tree_path(@project, @ref) }
- show_auto_devops_callout = show_auto_devops_callout?(@project)
- vue_file_list = Feature.enabled?(:vue_file_list, @project)
 
#tree-holder.tree-holder.clearfix
.nav-block
Loading
Loading
@@ -14,11 +13,11 @@
= render 'shared/commit_well', commit: commit, ref: ref, project: project
 
- if is_project_overview
.project-buttons.append-bottom-default{ class: ("js-keep-hidden-on-navigation" if vue_file_list) }
.project-buttons.append-bottom-default{ class: ("js-keep-hidden-on-navigation" if vue_file_list_enabled?) }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
 
- if vue_file_list
#js-tree-list{ data: { project_path: @project.full_path, full_name: @project.name_with_namespace, ref: ref } }
- 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 @tree.readme
= render "projects/tree/readme", readme: @tree.readme
- else
Loading
Loading
- empty_repo = @project.empty_repo?
- show_auto_devops_callout = show_auto_devops_callout?(@project)
- max_project_topic_length = 15
.project-home-panel{ class: [("empty-project" if empty_repo), ("js-keep-hidden-on-navigation" if Feature.enabled?(:vue_file_list, @project))] }
.project-home-panel{ class: [("empty-project" if empty_repo), ("js-keep-hidden-on-navigation" if vue_file_list_enabled?)] }
.row.append-bottom-8
.home-panel-title-row.col-md-12.col-lg-6.d-flex
.avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
Loading
Loading
- if readme.rich_viewer
%article.file-holder.readme-holder{ id: 'readme', class: [("limited-width-container" unless fluid_layout), ("js-hide-on-navigation" if Feature.enabled?(:vue_file_list, @project))] }
%article.file-holder.readme-holder{ id: 'readme', class: [("limited-width-container" unless fluid_layout), ("js-hide-on-navigation" if vue_file_list_enabled?)] }
.js-file-title.file-title
= blob_icon readme.mode, readme.name
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
Loading
Loading
Loading
Loading
@@ -6,71 +6,74 @@
= render 'shared/ref_switcher', destination: 'tree', path: @path, show_create: true
 
- if on_top_of_branch?
- addtotree_toggle_attributes = { href: '#', 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown', 'data-boundary': 'window' }
- addtotree_toggle_attributes = { 'data-toggle': 'dropdown', 'data-target': '.add-to-tree-dropdown', 'data-boundary': 'window' }
- else
- addtotree_toggle_attributes = { title: _("You can only add files when you are on a branch"), data: { container: 'body' }, class: 'disabled has-tooltip' }
 
%ul.breadcrumb.repo-breadcrumb
%li.breadcrumb-item
= link_to project_tree_path(@project, @ref) do
= @project.path
- path_breadcrumbs do |title, path|
- if vue_file_list_enabled?
#js-repo-breadcrumb
- else
%ul.breadcrumb.repo-breadcrumb
%li.breadcrumb-item
= link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path))
= link_to project_tree_path(@project, @ref) do
= @project.path
- path_breadcrumbs do |title, path|
%li.breadcrumb-item
= link_to truncate(title, length: 40), project_tree_path(@project, tree_join(@ref, path))
 
- if can_collaborate || can_create_mr_from_fork
%li.breadcrumb-item
%a.btn.add-to-tree.qa-add-to-tree{ addtotree_toggle_attributes }
= sprite_icon('plus', size: 16, css_class: 'float-left')
= sprite_icon('arrow-down', size: 16, css_class: 'float-left')
- if on_top_of_branch?
.add-to-tree-dropdown
%ul.dropdown-menu
- if can_edit_tree?
%li.dropdown-header
#{ _('This directory') }
%li
= link_to project_new_blob_path(@project, @id), class: 'qa-new-file-option' do
#{ _('New file') }
%li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
#{ _('Upload file') }
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
#{ _('New directory') }
- elsif can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
%li
- continue_params = { to: project_new_blob_path(@project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('New file') }
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('Upload file') }
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('New directory') }
- if can_collaborate || can_create_mr_from_fork
%li.breadcrumb-item
%button.btn.add-to-tree.qa-add-to-tree{ addtotree_toggle_attributes, type: 'button' }
= sprite_icon('plus', size: 16, css_class: 'float-left')
= sprite_icon('arrow-down', size: 16, css_class: 'float-left')
- if on_top_of_branch?
.add-to-tree-dropdown
%ul.dropdown-menu
- if can_edit_tree?
%li.dropdown-header
#{ _('This directory') }
%li
= link_to project_new_blob_path(@project, @id), class: 'qa-new-file-option' do
#{ _('New file') }
%li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
#{ _('Upload file') }
%li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
#{ _('New directory') }
- elsif can?(current_user, :fork_project, @project) && can?(current_user, :create_merge_request_in, @project)
%li
- continue_params = { to: project_new_blob_path(@project, @id),
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('New file') }
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('Upload file') }
%li
- continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.",
notice_now: edit_in_new_fork_notice_now }
- fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params)
= link_to fork_path, method: :post do
#{ _('New directory') }
 
- if can?(current_user, :push_code, @project)
%li.divider
%li.dropdown-header
#{ _('This repository') }
%li
= link_to new_project_branch_path(@project) do
#{ _('New branch') }
%li
= link_to new_project_tag_path(@project) do
#{ _('New tag') }
- if can?(current_user, :push_code, @project)
%li.divider
%li.dropdown-header
#{ _('This repository') }
%li
= link_to new_project_branch_path(@project) do
#{ _('New branch') }
%li
= link_to new_project_tag_path(@project) do
#{ _('New tag') }
 
.tree-controls
= link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn'
Loading
Loading
Loading
Loading
@@ -4389,6 +4389,9 @@ msgstr ""
msgid "Files"
msgstr ""
 
msgid "Files breadcrumb"
msgstr ""
msgid "Files, directories, and submodules in the path %{path} for commit reference %{ref}"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ describe 'Projects > Files > Project owner creates a license file', :js do
let(:project_maintainer) { project.owner }
 
before do
stub_feature_flags(vue_file_list: false)
project.repository.delete_file(project_maintainer, 'LICENSE',
message: 'Remove LICENSE', branch_name: 'master')
sign_in(project_maintainer)
Loading
Loading
Loading
Loading
@@ -12,6 +12,7 @@ describe 'Projects > Files > User creates files' do
let(:user) { create(:user) }
 
before do
stub_feature_flags(vue_file_list: false)
stub_feature_flags(web_ide_default: false)
 
project.add_maintainer(user)
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ describe 'Projects > Show > Collaboration links' do
let(:user) { create(:user) }
 
before do
stub_feature_flags(vue_file_list: false)
project.add_developer(user)
sign_in(user)
end
Loading
Loading
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import Breadcrumbs from '~/repository/components/breadcrumbs.vue';
let vm;
function factory(currentPath) {
vm = shallowMount(Breadcrumbs, {
propsData: {
currentPath,
},
stubs: {
RouterLink: RouterLinkStub,
},
});
}
describe('Repository breadcrumbs component', () => {
afterEach(() => {
vm.destroy();
});
it.each`
path | linkCount
${'/'} | ${1}
${'app'} | ${2}
${'app/assets'} | ${3}
${'app/assets/javascripts'} | ${4}
`('renders $linkCount links for path $path', ({ path, linkCount }) => {
factory(path);
expect(vm.findAll(RouterLinkStub).length).toEqual(linkCount);
});
it('renders last link as active', () => {
factory('app/assets');
expect(
vm
.findAll(RouterLinkStub)
.at(2)
.attributes('aria-current'),
).toEqual('page');
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment