Commit 7a262134 authored by Kushal Pandya's avatar Kushal Pandya Committed by Mario de la Ossa

Add Participants and Subscription toggle to Epic sidebar

parent baa05571
......@@ -37,7 +37,7 @@
},
computed: {
showLoadingState() {
return this.subscribed === null;
return this.loading || this.subscribed === null;
},
notificationIcon() {
return this.subscribed ? ICON_ON : ICON_OFF;
......
......@@ -90,6 +90,14 @@
type: Array,
required: true,
},
participants: {
type: Array,
required: true,
},
subscribed: {
type: Boolean,
required: true,
},
namespace: {
type: String,
required: false,
......@@ -99,6 +107,10 @@
type: String,
required: true,
},
toggleSubscriptionPath: {
type: String,
required: true,
},
labelsWebUrl: {
type: String,
required: true,
......@@ -163,9 +175,12 @@
:initial-start-date="startDate"
:initial-end-date="endDate"
:initial-labels="labels"
:initial-participants="participants"
:initial-subscribed="subscribed"
:namespace="namespace"
:update-path="updateEndpoint"
:labels-path="labelsPath"
:toggle-subscription-path="toggleSubscriptionPath"
:labels-web-url="labelsWebUrl"
:epics-web-url="epicsWebUrl"
/>
......
......@@ -3,12 +3,15 @@
import _ from 'underscore';
import Cookies from 'js-cookie';
import Flash from '~/flash';
import { __ } from '~/locale';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import ListLabel from '~/vue_shared/models/label';
import SidebarDatePicker from '~/vue_shared/components/sidebar/date_picker.vue';
import SidebarCollapsedGroupedDatePicker from '~/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue';
import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
import SidebarLabelsSelect from '~/vue_shared/components/sidebar/labels_select/base.vue';
import SidebarParticipants from './sidebar_participants.vue';
import SidebarSubscriptions from './sidebar_subscriptions.vue';
import SidebarService from '../services/sidebar_service';
import Store from '../stores/sidebar_store';
......@@ -19,6 +22,8 @@
SidebarDatePicker,
SidebarCollapsedGroupedDatePicker,
SidebarLabelsSelect,
SidebarParticipants,
SidebarSubscriptions,
},
props: {
endpoint: {
......@@ -42,6 +47,14 @@
type: Array,
required: true,
},
initialParticipants: {
type: Array,
required: true,
},
initialSubscribed: {
type: Boolean,
required: true,
},
namespace: {
type: String,
required: false,
......@@ -55,6 +68,10 @@
type: String,
required: true,
},
toggleSubscriptionPath: {
type: String,
required: true,
},
labelsWebUrl: {
type: String,
required: true,
......@@ -68,6 +85,7 @@
const store = new Store({
startDate: this.initialStartDate,
endDate: this.initialEndDate,
subscribed: this.initialSubscribed,
});
return {
......@@ -77,7 +95,8 @@
autoExpanded: false,
savingStartDate: false,
savingEndDate: false,
service: new SidebarService(this.endpoint),
savingSubscription: false,
service: new SidebarService(this.endpoint, this.toggleSubscriptionPath),
epicContext: {
labels: this.initialLabels,
},
......@@ -158,6 +177,19 @@
this.toggleSidebar();
}
},
handleToggleSubscription() {
this.service.toggleSubscription()
.then(() => {
this.store.setSubscription(!this.store.subscription);
})
.catch(() => {
if (this.store.subscription) {
Flash(__('An error occurred while unsubscribing to notifications.'));
} else {
Flash(__('An error occurred while subscribing to notifications.'));
}
});
},
},
};
</script>
......@@ -222,6 +254,14 @@
>
{{ __('None') }}
</sidebar-labels-select>
<sidebar-participants
:participants="initialParticipants"
/>
<sidebar-subscriptions
:loading="savingSubscription"
:subscribed="store.subscription"
@toggleSubscription="handleToggleSubscription"
/>
</div>
</aside>
</template>
<script>
import Participants from '~/sidebar/components/participants/participants.vue';
export default {
components: {
Participants,
},
props: {
participants: {
type: Array,
required: true,
},
},
};
</script>
<template>
<div class="block participants">
<participants
:participants="participants"
/>
</div>
</template>
<script>
import Subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue';
export default {
components: {
Subscriptions,
},
props: {
loading: {
type: Boolean,
required: true,
},
subscribed: {
type: Boolean,
required: true,
},
},
methods: {
onToggleSubscription() {
this.$emit('toggleSubscription');
},
},
};
</script>
<template>
<div class="block subscriptions">
<subscriptions
:loading="loading"
:subscribed="subscribed"
@toggleSubscription="onToggleSubscription"
/>
</div>
</template>
import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
import axios from '~/lib/utils/axios_utils';
export default class SidebarService {
constructor(endpoint) {
constructor(endpoint, subscriptionEndpoint) {
this.endpoint = endpoint;
this.resource = Vue.resource(`${this.endpoint}.json`, {});
this.subscriptionEndpoint = subscriptionEndpoint;
}
updateStartDate(startDate) {
return this.resource.update({
start_date: startDate,
});
return axios.put(this.endpoint, { start_date: startDate });
}
updateEndDate(endDate) {
return this.resource.update({
end_date: endDate,
});
return axios.put(this.endpoint, { end_date: endDate });
}
toggleSubscription() {
return axios.post(this.subscriptionEndpoint);
}
}
import { parsePikadayDate } from '~/lib/utils/datefix';
export default class SidebarStore {
constructor({ startDate, endDate }) {
constructor({ startDate, endDate, subscribed }) {
this.startDate = startDate;
this.endDate = endDate;
this.subscribed = subscribed;
}
get startDateTime() {
......@@ -13,4 +14,12 @@ export default class SidebarStore {
get endDateTime() {
return this.endDate ? parsePikadayDate(this.endDate) : null;
}
get subscription() {
return this.subscribed;
}
setSubscription(subscribed) {
this.subscribed = subscribed;
}
}
......@@ -40,6 +40,9 @@ describe('EpicShowApp', () => {
labelsWebUrl,
epicsWebUrl,
labels,
participants,
subscribed,
toggleSubscriptionPath,
} = props;
const EpicShowApp = Vue.extend(epicShowApp);
......@@ -68,6 +71,7 @@ describe('EpicShowApp', () => {
projectPath: props.groupPath,
projectNamespace: '',
showInlineEditButton: true,
toggleSubscriptionPath,
});
const EpicSidebar = Vue.extend(epicSidebar);
......@@ -77,7 +81,10 @@ describe('EpicShowApp', () => {
initialStartDate: startDate,
initialEndDate: endDate,
initialLabels: labels,
initialParticipants: participants,
initialSubscribed: subscribed,
updatePath: updateEndpoint,
toggleSubscriptionPath,
labelsPath,
labelsWebUrl,
epicsWebUrl,
......
......@@ -8,8 +8,28 @@ export const mockLabels = [
},
];
export const mockParticipants = [
{
id: 1,
name: 'Administrator',
username: 'root',
state: 'active',
avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
web_url: 'http://127.0.0.1:3001/root',
},
{
id: 12,
name: 'Susy Johnson',
username: 'tana_harvey',
state: 'active',
avatar_url: 'https://www.gravatar.com/avatar/e021a7b0f3e4ae53b5068d487e68c031?s=80&d=identicon',
web_url: 'http://127.0.0.1:3001/tana_harvey',
},
];
export const contentProps = {
endpoint: '',
toggleSubscriptionPath: gl.TEST_HOST,
updateEndpoint: gl.TEST_HOST,
canAdmin: true,
canUpdate: true,
......@@ -27,6 +47,8 @@ export const contentProps = {
startDate: '2017-01-01',
endDate: '2017-10-10',
labels: mockLabels,
participants: mockParticipants,
subscribed: true,
};
export const headerProps = {
......
import Vue from 'vue';
import _ from 'underscore';
import Cookies from 'js-cookie';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import epicSidebar from 'ee/epics/sidebar/components/sidebar_app.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { props } from '../../epic_show/mock_data';
......@@ -15,8 +18,23 @@ describe('epicSidebar', () => {
labelsWebUrl,
epicsWebUrl,
labels,
participants,
subscribed,
toggleSubscriptionPath,
} = props;
const defaultPropsData = {
endpoint: gl.TEST_HOST,
initialLabels: labels,
initialParticipants: participants,
initialSubscribed: subscribed,
updatePath: updateEndpoint,
toggleSubscriptionPath,
labelsPath,
labelsWebUrl,
epicsWebUrl,
};
beforeEach(() => {
setFixtures(`
<div class="page-with-contextual-sidebar right-sidebar-expanded">
......@@ -27,14 +45,7 @@ describe('epicSidebar', () => {
originalCookieState = Cookies.get('collapsed_gutter');
Cookies.set('collapsed_gutter', null);
EpicSidebar = Vue.extend(epicSidebar);
vm = mountComponent(EpicSidebar, {
endpoint: gl.TEST_HOST,
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
}, '#epic-sidebar');
vm = mountComponent(EpicSidebar, defaultPropsData, '#epic-sidebar');
});
afterEach(() => {
......@@ -46,44 +57,22 @@ describe('epicSidebar', () => {
});
it('should render min date sidebar-date-picker', () => {
vm = mountComponent(EpicSidebar, {
endpoint: gl.TEST_HOST,
initialStartDate: '2017-01-01',
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
});
vm = mountComponent(EpicSidebar, Object.assign({}, defaultPropsData, { initialStartDate: '2017-01-01' }));
expect(vm.$el.querySelector('.value-content strong').innerText.trim()).toEqual('Jan 1, 2017');
});
it('should render max date sidebar-date-picker', () => {
vm = mountComponent(EpicSidebar, {
endpoint: gl.TEST_HOST,
initialEndDate: '2018-01-01',
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
});
vm = mountComponent(EpicSidebar, Object.assign({}, defaultPropsData, { initialEndDate: '2018-01-01' }));
expect(vm.$el.querySelector('.value-content strong').innerText.trim()).toEqual('Jan 1, 2018');
});
it('should render both sidebar-date-picker', () => {
vm = mountComponent(EpicSidebar, {
endpoint: gl.TEST_HOST,
vm = mountComponent(EpicSidebar, Object.assign({}, defaultPropsData, {
initialStartDate: '2017-01-01',
initialEndDate: '2018-01-01',
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
});
}));
const startDatePicker = vm.$el.querySelector('.block.start-date');
const endDatePicker = vm.$el.querySelector('.block.end-date');
......@@ -94,15 +83,7 @@ describe('epicSidebar', () => {
describe('when collapsed', () => {
beforeEach(() => {
Cookies.set('collapsed_gutter', 'true');
vm = mountComponent(EpicSidebar, {
endpoint: gl.TEST_HOST,
initialStartDate: '2017-01-01',
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
});
vm = mountComponent(EpicSidebar, Object.assign({}, defaultPropsData, { initialStartDate: '2017-01-01' }));
});
it('should render right-sidebar-collapsed class', () => {
......@@ -138,30 +119,20 @@ describe('epicSidebar', () => {
});
describe('saveDate', () => {
let interceptor;
let component;
let mock;
beforeEach(() => {
interceptor = (request, next) => {
next(request.respondWith(JSON.stringify({}), {
status: 200,
}));
};
Vue.http.interceptors.push(interceptor);
mock = new MockAdapter(axios);
mock.onPut(gl.TEST_HOST).reply(() => [200, JSON.stringify({})]);
component = new EpicSidebar({
propsData: {
endpoint: gl.TEST_HOST,
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
},
propsData: defaultPropsData,
});
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
mock.restore();
});
it('should save startDate', (done) => {
......@@ -253,14 +224,7 @@ describe('epicSidebar', () => {
};
Vue.http.interceptors.push(interceptor);
component = new EpicSidebar({
propsData: {
endpoint: gl.TEST_HOST,
initialLabels: labels,
updatePath: updateEndpoint,
labelsPath,
labelsWebUrl,
epicsWebUrl,
},
propsData: defaultPropsData,
});
});
......
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