summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2019-05-29 10:24:35 +0100
committerPhil Hughes <me@iamphill.com>2019-05-29 10:24:35 +0100
commit1224aa516eefe7be1c8ca306625d5d0b6d524d76 (patch)
tree0fe19052d0496ecef64a1f0727a3dc97a85c4d1e
parent4a9387242dbbfee9e9ddc7b46eb69ad6a2f4ba2c (diff)
downloadgitlab-ce-1224aa516eefe7be1c8ca306625d5d0b6d524d76.tar.gz
Created repository list breadcrumbs Vue app
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue61
-rw-r--r--app/assets/javascripts/repository/index.js25
-rw-r--r--app/assets/javascripts/repository/queries/getProjectShortPath.graphql3
-rw-r--r--app/assets/javascripts/repository/utils/title.js2
-rw-r--r--app/helpers/projects_helper.rb4
-rw-r--r--app/views/projects/_files.html.haml7
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/tree/_readme.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml121
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb1
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb1
-rw-r--r--spec/features/projects/show/user_sees_collaboration_links_spec.rb1
-rw-r--r--spec/frontend/repository/components/breadcrumbs_spec.js44
14 files changed, 208 insertions, 69 deletions
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
new file mode 100644
index 00000000000..6eca015036f
--- /dev/null
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -0,0 +1,61 @@
+<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>
diff --git a/app/assets/javascripts/repository/index.js b/app/assets/javascripts/repository/index.js
index f992d4b6d54..52f53be045b 100644
--- a/app/assets/javascripts/repository/index.js
+++ b/app/assets/javascripts/repository/index.js
@@ -1,24 +1,27 @@
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
@@ -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,
diff --git a/app/assets/javascripts/repository/queries/getProjectShortPath.graphql b/app/assets/javascripts/repository/queries/getProjectShortPath.graphql
new file mode 100644
index 00000000000..34eb26598c2
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/getProjectShortPath.graphql
@@ -0,0 +1,3 @@
+query getProjectShortPath {
+ projectShortPath @client
+}
diff --git a/app/assets/javascripts/repository/utils/title.js b/app/assets/javascripts/repository/utils/title.js
index 3c3e918c0a8..4e194640e92 100644
--- a/app/assets/javascripts/repository/utils/title.js
+++ b/app/assets/javascripts/repository/utils/title.js
@@ -1,5 +1,7 @@
// eslint-disable-next-line import/prefer-default-export
export const setTitle = (pathMatch, ref, project) => {
+ if (!pathMatch) return;
+
const path = pathMatch.replace(/^\//, '');
const isEmpty = path === '';
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index f798bfbf703..4cf1d6e93a5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -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
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 7f50a7e4294..2b0c3985755 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -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
@@ -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
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 6de8848d3a1..9f5241344a7 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,7 +1,7 @@
- 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
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index e935af23659..4f6c7e1f9a6 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,5 +1,5 @@
- 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
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index ec8e5234bd4..ea6349f2f57 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -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'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9e7ff8d1847..44dda25c38c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index 6762460971f..44715261b8b 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -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)
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index dd2964c2186..69f8bd4d319 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -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)
diff --git a/spec/features/projects/show/user_sees_collaboration_links_spec.rb b/spec/features/projects/show/user_sees_collaboration_links_spec.rb
index 24777788248..46586b891e7 100644
--- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb
+++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb
@@ -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
diff --git a/spec/frontend/repository/components/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js
new file mode 100644
index 00000000000..068fa317a87
--- /dev/null
+++ b/spec/frontend/repository/components/breadcrumbs_spec.js
@@ -0,0 +1,44 @@
+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');
+ });
+});