Setup Vue app for repository file listing
Part of https://gitlab.com/gitlab-org/gitlab-ce/issues/61578
This commit is contained in:
parent
2f613f28ba
commit
404314c619
24 changed files with 184 additions and 4 deletions
|
@ -46,4 +46,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
GpgBadges.fetch();
|
GpgBadges.fetch();
|
||||||
leaveByUrl('project');
|
leaveByUrl('project');
|
||||||
|
|
||||||
|
if (document.getElementById('js-tree-list')) {
|
||||||
|
import('~/repository')
|
||||||
|
.then(m => m.default())
|
||||||
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,4 +40,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
GpgBadges.fetch();
|
GpgBadges.fetch();
|
||||||
|
|
||||||
|
if (document.getElementById('js-tree-list')) {
|
||||||
|
import('~/repository')
|
||||||
|
.then(m => m.default())
|
||||||
|
.catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
3
app/assets/javascripts/repository/components/app.vue
Normal file
3
app/assets/javascripts/repository/components/app.vue
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
11
app/assets/javascripts/repository/graphql.js
Normal file
11
app/assets/javascripts/repository/graphql.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import VueApollo from 'vue-apollo';
|
||||||
|
import createDefaultClient from '~/lib/graphql';
|
||||||
|
|
||||||
|
Vue.use(VueApollo);
|
||||||
|
|
||||||
|
const defaultClient = createDefaultClient({});
|
||||||
|
|
||||||
|
export default new VueApollo({
|
||||||
|
defaultClient,
|
||||||
|
});
|
25
app/assets/javascripts/repository/index.js
Normal file
25
app/assets/javascripts/repository/index.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import createRouter from './router';
|
||||||
|
import App from './components/app.vue';
|
||||||
|
import apolloProvider from './graphql';
|
||||||
|
|
||||||
|
export default function setupVueRepositoryList() {
|
||||||
|
const el = document.getElementById('js-tree-list');
|
||||||
|
const { projectPath, ref } = el.dataset;
|
||||||
|
|
||||||
|
apolloProvider.clients.defaultClient.cache.writeData({
|
||||||
|
data: {
|
||||||
|
projectPath,
|
||||||
|
ref,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Vue({
|
||||||
|
el,
|
||||||
|
router: createRouter(projectPath, ref),
|
||||||
|
apolloProvider,
|
||||||
|
render(h) {
|
||||||
|
return h(App);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
24
app/assets/javascripts/repository/pages/index.vue
Normal file
24
app/assets/javascripts/repository/pages/index.vue
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script>
|
||||||
|
import getRef from '../queries/getRef.graphql';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
apollo: {
|
||||||
|
ref: {
|
||||||
|
query: getRef,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ref: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<router-link :to="{ path: `/tree/${ref}/app` }">
|
||||||
|
Go to tree
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
15
app/assets/javascripts/repository/pages/tree.vue
Normal file
15
app/assets/javascripts/repository/pages/tree.vue
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>{{ path }}</div>
|
||||||
|
</template>
|
3
app/assets/javascripts/repository/queries/getRef.graphql
Normal file
3
app/assets/javascripts/repository/queries/getRef.graphql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
query getRef {
|
||||||
|
ref @client
|
||||||
|
}
|
36
app/assets/javascripts/repository/router.js
Normal file
36
app/assets/javascripts/repository/router.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import VueRouter from 'vue-router';
|
||||||
|
import { joinPaths } from '../lib/utils/url_utility';
|
||||||
|
import IndexPage from './pages/index.vue';
|
||||||
|
import TreePage from './pages/tree.vue';
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
export default function createRouter(base, baseRef) {
|
||||||
|
return new VueRouter({
|
||||||
|
mode: 'history',
|
||||||
|
base: joinPaths(gon.relative_url_root || '', base),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'projectRoot',
|
||||||
|
component: IndexPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/tree/${baseRef}(/.*)?`,
|
||||||
|
name: 'treePath',
|
||||||
|
component: TreePage,
|
||||||
|
props: route => ({
|
||||||
|
path: route.params.pathMatch,
|
||||||
|
}),
|
||||||
|
beforeEnter(to, from, next) {
|
||||||
|
document
|
||||||
|
.querySelectorAll('.js-hide-on-navigation')
|
||||||
|
.forEach(el => el.classList.add('hidden'));
|
||||||
|
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
- project = local_assigns.fetch(:project) { @project }
|
- 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) }
|
- 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)
|
- show_auto_devops_callout = show_auto_devops_callout?(@project)
|
||||||
|
- vue_file_list = Feature.enabled?(:vue_file_list, @project)
|
||||||
|
|
||||||
#tree-holder.tree-holder.clearfix
|
#tree-holder.tree-holder.clearfix
|
||||||
.nav-block
|
.nav-block
|
||||||
|
@ -13,7 +14,12 @@
|
||||||
= render 'shared/commit_well', commit: commit, ref: ref, project: project
|
= render 'shared/commit_well', commit: commit, ref: ref, project: project
|
||||||
|
|
||||||
- if is_project_overview
|
- if is_project_overview
|
||||||
.project-buttons.append-bottom-default
|
.project-buttons.append-bottom-default{ class: ("js-hide-on-navigation" if vue_file_list) }
|
||||||
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
|
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
|
||||||
|
|
||||||
= render 'projects/tree/tree_content', tree: @tree, content_url: content_url
|
- if vue_file_list
|
||||||
|
#js-tree-list{ data: { project_path: @project.full_path, ref: ref } }
|
||||||
|
- if @tree.readme
|
||||||
|
= render "projects/tree/readme", readme: @tree.readme
|
||||||
|
- else
|
||||||
|
= render 'projects/tree/tree_content', tree: @tree, content_url: content_url
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- empty_repo = @project.empty_repo?
|
- empty_repo = @project.empty_repo?
|
||||||
- show_auto_devops_callout = show_auto_devops_callout?(@project)
|
- show_auto_devops_callout = show_auto_devops_callout?(@project)
|
||||||
- max_project_topic_length = 15
|
- max_project_topic_length = 15
|
||||||
.project-home-panel{ class: ("empty-project" if empty_repo) }
|
.project-home-panel{ class: [("empty-project" if empty_repo), ("js-hide-on-navigation" if Feature.enabled?(:vue_file_list, @project))] }
|
||||||
.row.append-bottom-8
|
.row.append-bottom-8
|
||||||
.home-panel-title-row.col-md-12.col-lg-6.d-flex
|
.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
|
.avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- if readme.rich_viewer
|
- if readme.rich_viewer
|
||||||
%article.file-holder.readme-holder{ id: 'readme', class: ("limited-width-container" unless fluid_layout) }
|
%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))] }
|
||||||
.js-file-title.file-title
|
.js-file-title.file-title
|
||||||
= blob_icon readme.mode, readme.name
|
= blob_icon readme.mode, readme.name
|
||||||
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
|
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
|
||||||
|
|
|
@ -5,6 +5,8 @@ describe 'Projects > Files > User views files page' do
|
||||||
let(:user) { project.owner }
|
let(:user) { project.owner }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
sign_in user
|
sign_in user
|
||||||
visit project_tree_path(project, project.repository.root_ref)
|
visit project_tree_path(project, project.repository.root_ref)
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ describe "User browses files" do
|
||||||
let(:user) { project.owner }
|
let(:user) { project.owner }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
stub_feature_flags(csslab: false)
|
stub_feature_flags(csslab: false)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,8 @@ describe 'Projects > Files > User browses LFS files' do
|
||||||
let(:user) { project.owner }
|
let(:user) { project.owner }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@ describe 'Projects > Files > User creates a directory', :js do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
project.add_developer(user)
|
project.add_developer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
visit project_tree_path(project, 'master')
|
visit project_tree_path(project, 'master')
|
||||||
|
|
|
@ -12,6 +12,8 @@ describe 'Projects > Files > User deletes files', :js do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ describe 'Projects > Files > User edits files', :js do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(web_ide_default: false)
|
stub_feature_flags(web_ide_default: false)
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,8 @@ describe 'Projects > Files > User replaces files', :js do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ describe 'Projects > Files > User uploads files' do
|
||||||
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
|
let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ describe 'Projects tree', :js do
|
||||||
let(:test_sha) { '7975be0116940bf2ad4321f79d02a55c5f7779aa' }
|
let(:test_sha) { '7975be0116940bf2ad4321f79d02a55c5f7779aa' }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ describe 'Project' do
|
||||||
include MobileHelpers
|
include MobileHelpers
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
stub_feature_flags(approval_rules: false)
|
stub_feature_flags(approval_rules: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
23
spec/frontend/repository/router_spec.js
Normal file
23
spec/frontend/repository/router_spec.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import IndexPage from '~/repository/pages/index.vue';
|
||||||
|
import TreePage from '~/repository/pages/tree.vue';
|
||||||
|
import createRouter from '~/repository/router';
|
||||||
|
|
||||||
|
describe('Repository router spec', () => {
|
||||||
|
it.each`
|
||||||
|
path | component | componentName
|
||||||
|
${'/'} | ${IndexPage} | ${'IndexPage'}
|
||||||
|
${'/tree/master'} | ${TreePage} | ${'TreePage'}
|
||||||
|
${'/tree/master/app/assets'} | ${TreePage} | ${'TreePage'}
|
||||||
|
${'/tree/123/app/assets'} | ${null} | ${'null'}
|
||||||
|
`('sets component as $componentName for path "$path"', ({ path, component }) => {
|
||||||
|
const router = createRouter('', 'master');
|
||||||
|
|
||||||
|
const componentsForRoute = router.getMatchedComponents(path);
|
||||||
|
|
||||||
|
expect(componentsForRoute.length).toBe(component ? 1 : 0);
|
||||||
|
|
||||||
|
if (component) {
|
||||||
|
expect(componentsForRoute).toContain(component);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,6 +7,8 @@ describe 'projects/tree/show' do
|
||||||
let(:repository) { project.repository }
|
let(:repository) { project.repository }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(vue_file_list: false)
|
||||||
|
|
||||||
assign(:project, project)
|
assign(:project, project)
|
||||||
assign(:repository, repository)
|
assign(:repository, repository)
|
||||||
assign(:lfs_blob_ids, [])
|
assign(:lfs_blob_ids, [])
|
||||||
|
|
Loading…
Reference in a new issue