Commit 80646fd8 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'design-management-vue-app' into 'master'

Setup initial Vue app for design management

See merge request gitlab-org/gitlab-ee!9652
parents 80360029 aafc8c24
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import defaultClient from '~/lib/graphql';
import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
Vue.use(VueApollo);
......@@ -10,7 +10,7 @@ export default function() {
const issueTitle = document.getElementById('issue_title');
const { projectPath } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient,
defaultClient: createDefaultClient(),
});
return new Vue({
......
import ApolloClient from 'apollo-boost';
import csrf from '~/lib/utils/csrf';
export default new ApolloClient({
uri: `${gon.relative_url_root}/api/graphql`,
headers: {
[csrf.headerKey]: csrf.token,
},
});
export default (clientState = {}) =>
new ApolloClient({
uri: `${gon.relative_url_root}/api/graphql`,
headers: {
[csrf.headerKey]: csrf.token,
},
clientState,
});
......@@ -27,11 +27,11 @@ the Vue application is mounted.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import defaultClient from '~/lib/graphql';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient,
defaultClient: createDefaultClient(),
});
new Vue({
......@@ -43,6 +43,29 @@ new Vue({
Read more about [Vue Apollo][vue-apollo] in the [Vue Apollo documentation][vue-apollo-docs].
### Local state with `apollo-link-state`
It is possible to use our Apollo setup with [apollo-link-state][apollo-link-state] by passing
in the client state object when creating the default client.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient({
defaults: {
testing: true,
},
resolvers: {
...
},
}),
});
```
### Testing
With [Vue test utils][vue-test-utils] it is easy to quickly test components that
......@@ -81,3 +104,4 @@ Read more about the [Apollo] client in the [Apollo documentation][apollo-client-
[default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js
[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html
[vue-test-utils]: https://vue-test-utils.vuejs.org/
[apollo-link-state]: https://www.apollographql.com/docs/link/links/state.html
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export default new VueApollo({
defaultClient: createDefaultClient(),
});
import $ from 'jquery';
import Vue from 'vue';
import router from './router';
import App from './components/app.vue';
import apolloProvider from './graphql';
export default () => {
$('.js-issue-tabs').on('shown.bs.tab', ({ target: { id } }) => {
if (id === 'designs' && router.currentRoute.name === 'root') {
router.push('/designs');
} else if (id === 'discussion') {
router.push('/');
}
});
return new Vue({
el: document.getElementById('js-design-management'),
router,
apolloProvider,
render(createElement) {
return createElement(App);
},
});
};
<script>
export default {
props: {
id: {
type: Number,
required: true,
},
},
};
</script>
<template>
<div>
Design detail for {{ id }}
<router-link to="/designs">All designs</router-link>
</div>
</template>
<template>
<div>
Home
<router-link to="/designs/1">Design</router-link>
</div>
</template>
import $ from 'jquery';
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './pages/index.vue';
import DesignDetail from './pages/design/index.vue';
Vue.use(VueRouter);
const router = new VueRouter({
base: window.location.pathname,
routes: [
{
name: 'root',
path: '/',
component: null,
meta: {
el: 'discussion',
},
},
{
name: 'designs',
path: '/designs',
component: Home,
meta: {
el: 'designs',
},
},
{
name: 'design',
path: '/designs/:id',
component: DesignDetail,
meta: {
el: 'designs',
},
props: ({ params: { id } }) => ({ id: parseInt(id, 10) }),
},
],
});
router.beforeEach(({ meta: { el } }, from, next) => {
$(`#${el}`).tab('show');
next();
});
export default router;
......@@ -8,6 +8,12 @@ document.addEventListener('DOMContentLoaded', () => {
initSidebarBundle();
initRelatedIssues();
if (gon.features.versionedDesigns) {
import(/* webpackChunkName: 'design_management' */ 'ee/design_management')
.then(module => module.default())
.catch(() => {});
}
// eslint-disable-next-line no-new
new UserCallout({ className: 'js-epics-sidebar-callout' });
// eslint-disable-next-line no-new
......
......@@ -16,6 +16,9 @@ module EE
before_action :check_export_issues_available!, only: [:export_csv]
before_action :check_service_desk_available!, only: [:service_desk]
before_action :whitelist_query_limiting_ee, only: [:update]
before_action only: :show do
push_frontend_feature_flag(:versioned_designs)
end
end
override :issue_except_actions
......
- if Feature.enabled?(:versioned_designs)
%ul.nav-tabs.nav.nav-links{ role: 'tablist' }
%li
= link_to '#discussion-tab', class: 'active', id: 'discussion', role: 'tab', 'aria-controls': 'js-discussion', 'aria-selected': 'true', data: { toggle: 'tab', target: '#discussion-tab' } do
= link_to '#discussion-tab', class: 'active js-issue-tabs', id: 'discussion', role: 'tab', 'aria-controls': 'js-discussion', 'aria-selected': 'true', data: { toggle: 'tab', target: '#discussion-tab' } do
= _('Discussion')
%span.badge.badge-pill.js-discussions-count
%li
= link_to '#designs-tab', id: 'designs', role: 'tab', 'aria-controls': 'js-designs', 'aria-selected': 'false', data: { toggle: 'tab', target: '#designs-tab' } do
= link_to '#designs-tab', class: 'js-issue-tabs', id: 'designs', role: 'tab', 'aria-controls': 'js-designs', 'aria-selected': 'false', data: { toggle: 'tab', target: '#designs-tab' } do
= _('Designs')
%span.badge.badge-pill.js-designs-count
.tab-content
#discussion-tab.tab-pane.show.active{ role: 'tabpanel', 'aria-labelledby': 'discussion' }
= render_ce 'projects/issues/discussion'
#designs-tab.tab-pane{ role: 'tabpanel', 'aria-labelledby': 'designs' }
= spinner nil, true
#js-design-management
- else
= render_ce 'projects/issues/discussion'
import { mount, createLocalVue } from '@vue/test-utils';
import VueRouter from 'vue-router';
import App from 'ee/design_management/components/app.vue';
import Designs from 'ee/design_management/pages/index.vue';
import DesignDetail from 'ee/design_management/pages/design/index.vue';
import router from 'ee/design_management/router';
describe('Design management router', () => {
let vm;
function factory() {
const localVue = createLocalVue();
localVue.use(VueRouter);
vm = mount(App, { localVue, router });
}
beforeEach(() => {
factory();
});
afterEach(() => {
vm.destroy();
router.app.$destroy();
window.location.hash = '';
});
describe('root', () => {
it('pushes empty component', () => {
router.push('/');
expect(vm.isEmpty()).toBe(true);
});
});
describe('designs', () => {
it('pushes designs root component', () => {
router.push('/designs');
expect(vm.find(Designs).exists()).toBe(true);
});
});
describe('designs detail', () => {
it('pushes designs detail component', () => {
router.push('/designs/1');
const detail = vm.find(DesignDetail);
expect(detail.exists()).toBe(true);
expect(detail.props('id')).toEqual(1);
});
});
});
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