Merge branch 'backport-epic-in-issue' into 'master'

Backport epic in issue

See merge request gitlab-org/gitlab-ce!15799
This commit is contained in:
Jacob Schatz 2017-12-11 20:48:52 +00:00
commit 9c6ce2e8c9
11 changed files with 151 additions and 82 deletions

View file

@ -1,22 +1,32 @@
import Flash from '../../../flash';
import AssigneeTitle from './assignee_title';
import Assignees from './assignees';
import Store from '../../stores/sidebar_store';
import Mediator from '../../sidebar_mediator';
import eventHub from '../../event_hub';
export default {
name: 'SidebarAssignees',
data() {
return {
mediator: new Mediator(),
store: new Store(),
loading: false,
field: '',
};
},
props: {
mediator: {
type: Object,
required: true,
},
field: {
type: String,
required: true,
},
signedIn: {
type: Boolean,
required: false,
default: false,
},
},
components: {
'assignee-title': AssigneeTitle,
assignees: Assignees,
@ -61,10 +71,6 @@ export default {
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
beforeMount() {
this.field = this.$el.dataset.field;
this.signedIn = typeof this.$el.dataset.signedIn !== 'undefined';
},
template: `
<div>
<assignee-title

View file

@ -1,15 +1,19 @@
<script>
import Store from '../../stores/sidebar_store';
import Mediator from '../../sidebar_mediator';
import participants from './participants.vue';
export default {
data() {
return {
mediator: new Mediator(),
store: new Store(),
};
},
props: {
mediator: {
type: Object,
required: true,
},
},
components: {
participants,
},
@ -21,6 +25,7 @@ export default {
<participants
:loading="store.isFetching.participants"
:participants="store.participants"
:number-of-less-participants="7" />
:number-of-less-participants="7"
/>
</div>
</template>

View file

@ -1,6 +1,5 @@
<script>
import Store from '../../stores/sidebar_store';
import Mediator from '../../sidebar_mediator';
import eventHub from '../../event_hub';
import Flash from '../../../flash';
import { __ } from '../../../locale';
@ -9,11 +8,15 @@ import subscriptions from './subscriptions.vue';
export default {
data() {
return {
mediator: new Mediator(),
store: new Store(),
};
},
props: {
mediator: {
type: Object,
required: true,
},
},
components: {
subscriptions,
},

View file

@ -10,6 +10,27 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
function mountAssigneesComponent(mediator) {
const el = document.getElementById('js-vue-sidebar-assignees');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
SidebarAssignees,
},
render: createElement => createElement('sidebar-assignees', {
props: {
mediator,
field: el.dataset.field,
signedIn: el.hasAttribute('data-signed-in'),
},
}),
});
}
function mountConfidentialComponent(mediator) {
const el = document.getElementById('js-confidential-entry-point');
@ -49,9 +70,10 @@ function mountLockComponent(mediator) {
}).$mount(el);
}
function mountParticipantsComponent() {
function mountParticipantsComponent(mediator) {
const el = document.querySelector('.js-sidebar-participants-entry-point');
// eslint-disable-next-line no-new
if (!el) return;
// eslint-disable-next-line no-new
@ -60,11 +82,15 @@ function mountParticipantsComponent() {
components: {
sidebarParticipants,
},
render: createElement => createElement('sidebar-participants', {}),
render: createElement => createElement('sidebar-participants', {
props: {
mediator,
},
}),
});
}
function mountSubscriptionsComponent() {
function mountSubscriptionsComponent(mediator) {
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
if (!el) return;
@ -75,22 +101,35 @@ function mountSubscriptionsComponent() {
components: {
sidebarSubscriptions,
},
render: createElement => createElement('sidebar-subscriptions', {}),
render: createElement => createElement('sidebar-subscriptions', {
props: {
mediator,
},
}),
});
}
function mount(mediator) {
const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
// Only create the sidebarAssignees vue app if it is found in the DOM
// We currently do not use sidebarAssignees for the MR page
if (sidebarAssigneesEl) {
new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
}
function mountTimeTrackingComponent() {
const el = document.getElementById('issuable-time-tracker');
if (!el) return;
// eslint-disable-next-line no-new
new Vue({
el,
components: {
SidebarTimeTracking,
},
render: createElement => createElement('sidebar-time-tracking', {}),
});
}
export function mountSidebar(mediator) {
mountAssigneesComponent(mediator);
mountConfidentialComponent(mediator);
mountLockComponent(mediator);
mountParticipantsComponent();
mountSubscriptionsComponent();
mountParticipantsComponent(mediator);
mountSubscriptionsComponent(mediator);
new SidebarMoveIssue(
mediator,
@ -98,7 +137,9 @@ function mount(mediator) {
$('.js-move-issue-confirmation-button'),
).init();
new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
mountTimeTrackingComponent();
}
export default mount;
export function getSidebarOptions() {
return JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
}

View file

@ -1,9 +1,8 @@
import Mediator from './sidebar_mediator';
import mountSidebar from './mount_sidebar';
import { mountSidebar, getSidebarOptions } from './mount_sidebar';
function domContentLoaded() {
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
const mediator = new Mediator(sidebarOptions);
const mediator = new Mediator(getSidebarOptions());
mediator.fetch();
mountSidebar(mediator);

View file

@ -8,7 +8,6 @@ export default class SidebarMediator {
if (!SidebarMediator.singleton) {
this.initSingleton(options);
}
return SidebarMediator.singleton;
}

View file

@ -1,33 +1,37 @@
export default class SidebarStore {
constructor(store) {
constructor(options) {
if (!SidebarStore.singleton) {
const { currentUser, rootPath, editable } = store;
this.currentUser = currentUser;
this.rootPath = rootPath;
this.editable = editable;
this.timeEstimate = 0;
this.totalTimeSpent = 0;
this.humanTimeEstimate = '';
this.humanTimeSpent = '';
this.assignees = [];
this.isFetching = {
assignees: true,
participants: true,
subscriptions: true,
};
this.isLoading = {};
this.autocompleteProjects = [];
this.moveToProjectId = 0;
this.isLockDialogOpen = false;
this.participants = [];
this.subscribed = null;
SidebarStore.singleton = this;
this.initSingleton(options);
}
return SidebarStore.singleton;
}
initSingleton(options) {
const { currentUser, rootPath, editable } = options;
this.currentUser = currentUser;
this.rootPath = rootPath;
this.editable = editable;
this.timeEstimate = 0;
this.totalTimeSpent = 0;
this.humanTimeEstimate = '';
this.humanTimeSpent = '';
this.assignees = [];
this.isFetching = {
assignees: true,
participants: true,
subscriptions: true,
};
this.isLoading = {};
this.autocompleteProjects = [];
this.moveToProjectId = 0;
this.isLockDialogOpen = false;
this.participants = [];
this.subscribed = null;
SidebarStore.singleton = this;
}
setAssigneeData(data) {
this.isFetching.assignees = false;
if (data.assignees) {

View file

@ -50,6 +50,11 @@
&:not(.disabled) {
cursor: pointer;
}
svg {
width: $gl-padding;
height: $gl-padding;
}
}
}

View file

@ -470,7 +470,8 @@
}
}
.milestone-title span {
.milestone-title span,
.collapse-truncated-title {
@include str-truncated(100%);
display: block;
margin: 0 4px;

View file

@ -4,20 +4,29 @@ import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import Mock from './mock_data';
import mountComponent from '../helpers/vue_mount_component_helper';
describe('sidebar assignees', () => {
let component;
let SidebarAssigneeComponent;
let vm;
let mediator;
let sidebarAssigneesEl;
preloadFixtures('issues/open-issue.html.raw');
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
spyOn(SidebarMediator.prototype, 'saveAssignees').and.callThrough();
spyOn(SidebarMediator.prototype, 'assignYourself').and.callThrough();
this.mediator = new SidebarMediator(Mock.mediator);
loadFixtures('issues/open-issue.html.raw');
this.sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
mediator = new SidebarMediator(Mock.mediator);
spyOn(mediator, 'saveAssignees').and.callThrough();
spyOn(mediator, 'assignYourself').and.callThrough();
const SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
vm = mountComponent(SidebarAssigneeComponent, {
mediator,
field: sidebarAssigneesEl.dataset.field,
}, sidebarAssigneesEl);
});
afterEach(() => {
@ -28,30 +37,24 @@ describe('sidebar assignees', () => {
});
it('calls the mediator when saves the assignees', () => {
component = new SidebarAssigneeComponent()
.$mount(this.sidebarAssigneesEl);
component.saveAssignees();
expect(SidebarMediator.prototype.saveAssignees).toHaveBeenCalled();
vm.saveAssignees();
expect(mediator.saveAssignees).toHaveBeenCalled();
});
it('calls the mediator when "assignSelf" method is called', () => {
component = new SidebarAssigneeComponent()
.$mount(this.sidebarAssigneesEl);
component.assignSelf();
vm.assignSelf();
expect(SidebarMediator.prototype.assignYourself).toHaveBeenCalled();
expect(this.mediator.store.assignees.length).toEqual(1);
expect(mediator.assignYourself).toHaveBeenCalled();
expect(mediator.store.assignees.length).toEqual(1);
});
it('hides assignees until fetched', (done) => {
component = new SidebarAssigneeComponent().$mount(this.sidebarAssigneesEl);
const currentAssignee = this.sidebarAssigneesEl.querySelector('.value');
const currentAssignee = sidebarAssigneesEl.querySelector('.value');
expect(currentAssignee).toBe(null);
component.store.isFetching.assignees = false;
vm.store.isFetching.assignees = false;
Vue.nextTick(() => {
expect(component.$el.querySelector('.value')).toBeVisible();
expect(vm.$el.querySelector('.value')).toBeVisible();
done();
});
});

View file

@ -26,11 +26,14 @@ describe('Sidebar Subscriptions', function () {
});
it('calls the mediator toggleSubscription on event', () => {
spyOn(SidebarMediator.prototype, 'toggleSubscription').and.returnValue(Promise.resolve());
vm = mountComponent(SidebarSubscriptions, {});
const mediator = new SidebarMediator();
spyOn(mediator, 'toggleSubscription').and.returnValue(Promise.resolve());
vm = mountComponent(SidebarSubscriptions, {
mediator,
});
eventHub.$emit('toggleSubscription');
expect(SidebarMediator.prototype.toggleSubscription).toHaveBeenCalled();
expect(mediator.toggleSubscription).toHaveBeenCalled();
});
});