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