Skip to content
This repository was archived by the owner on Dec 14, 2023. It is now read-only.

Commit 76b5229

Browse files
authored
Merge pull request #280 from CoderDojo/error-notify
Global error display query param
2 parents 22c0700 + f2e3c73 commit 76b5229

File tree

7 files changed

+107
-9
lines changed

7 files changed

+107
-9
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"vue-router": "^2.5.3",
4747
"vue-router-multiguard": "^1.0.3",
4848
"vue-static-map": "^2.0.0",
49+
"vue-toasted": "^1.1.27",
4950
"vue-trix": "^1.1.0",
5051
"vue2-google-maps": "^0.7.9",
5152
"vuex": "^2.3.1"

src/common/base.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ body {
1414
.flow {
1515
margin-bottom: @margin;
1616
}
17+
18+
.toasted-container .toasted .action {
19+
font-size: unset !important;
20+
}

src/main.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import VueResource from 'vue-resource';
55
import VeeValidate from 'vee-validate';
66
import * as VueGoogleMaps from 'vue2-google-maps';
77
import VueAnalytics from 'vue-analytics';
8+
import VueToasted from 'vue-toasted';
89
import PasswordValidator from '@/common/directives/cd-password-validator';
910
import titleDirective from '@/common/directives/title';
1011
import gaTrackClickDirective from '@/common/directives/cd-ga-track-click';
@@ -26,6 +27,7 @@ Vue.config.projectsUrlBase = process.env.PROJECTS_URL_BASE;
2627

2728
Vue.use(VueResource);
2829
Vue.use(VeeValidate);
30+
Vue.use(VueToasted);
2931
Vue.use(VueGoogleMaps, {
3032
load: {
3133
key: Vue.config.googleMapsApiKey,

src/router/errorDisplayGuard.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Vue from 'vue';
2+
3+
function notifyError(error) {
4+
Vue.toasted.show(error, {
5+
duration: 10000,
6+
type: 'error',
7+
keepOnHover: true,
8+
action: {
9+
text: 'X',
10+
onClick: (e, toastObject) => {
11+
toastObject.goAway(0);
12+
},
13+
},
14+
});
15+
}
16+
17+
export default function errorDisplayGuard(to, from, next) {
18+
const { error, ...queryWithoutError } = to.query;
19+
if (error) {
20+
notifyError(error);
21+
next({ ...to, query: queryWithoutError || {} });
22+
} else {
23+
next();
24+
}
25+
}

src/router/index.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Vue from 'vue';
22
import Router from 'vue-router';
3-
import MultiGuard from 'vue-router-multiguard';
3+
import multiguard from 'vue-router-multiguard';
44
import DojoDetails from '@/dojos/cd-dojo-details';
55
import FindDojo from '@/dojos/cd-find-dojo';
66
import UserTickets from '@/users/cd-tickets';
@@ -20,9 +20,9 @@ import loggedInNavGuard from './loggedInNavGuard';
2020
import loggedInCDFNavGuard from './loggedInCDFNavGuard';
2121
import profileAuthRedirect from './profileAuthRedirect';
2222
import orderExistsNavGuard from './orderExistsNavGuard';
23+
import errorDisplayGuard from './errorDisplayGuard';
2324
import ticketingAdminNavGuard from './ticketingAdminNavGuard';
2425

25-
2626
Vue.use(Router);
2727

2828
const router = new Router({
@@ -43,10 +43,12 @@ const router = new Router({
4343
component: {
4444
template: '<router-view :key="$route.fullPath"></router-view>',
4545
},
46-
async beforeEnter(to, from, next) {
47-
await store.dispatch('getLoggedInUser');
48-
next();
49-
},
46+
beforeEnter: multiguard([
47+
errorDisplayGuard,
48+
(to, from, next) => {
49+
store.dispatch('getLoggedInUser').then(() => next());
50+
},
51+
]),
5052
children: [
5153
{
5254
path: '',
@@ -91,13 +93,13 @@ const router = new Router({
9193
path: '/dashboard/dojos/:dojoId/events/new',
9294
name: 'NewEventForm',
9395
component: EventForm,
94-
beforeEnter: MultiGuard([loggedInNavGuard, ticketingAdminNavGuard]),
96+
beforeEnter: multiguard([loggedInNavGuard, ticketingAdminNavGuard]),
9597
},
9698
{
9799
path: '/dashboard/dojos/:dojoId/events/:eventId/edit',
98100
name: 'EditEventForm',
99101
component: EventForm,
100-
beforeEnter: MultiGuard([loggedInNavGuard, ticketingAdminNavGuard]),
102+
beforeEnter: multiguard([loggedInNavGuard, ticketingAdminNavGuard]),
101103
},
102104
{
103105
path: '/dashboard/dojos/:dojoId/join-requests/:requestId/status/:status',
@@ -152,7 +154,7 @@ const router = new Router({
152154
path: 'events/:eventId/confirmation',
153155
name: 'EventBookingConfirmation',
154156
component: BookingConfirmation,
155-
beforeEnter: MultiGuard([loggedInNavGuard, orderExistsNavGuard]),
157+
beforeEnter: multiguard([loggedInNavGuard, orderExistsNavGuard]),
156158
props: true,
157159
},
158160
{
@@ -183,3 +185,4 @@ const router = new Router({
183185
});
184186

185187
export default router;
188+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import errorDisplayGuard from 'inject-loader!@/router/errorDisplayGuard';
2+
3+
describe('errorDisplayGuard', () => {
4+
let MockVue;
5+
let errorDisplayGuardWithMock;
6+
let nextMock;
7+
8+
beforeEach(() => {
9+
MockVue = {
10+
toasted: { show: sinon.stub() },
11+
};
12+
nextMock = sinon.stub();
13+
errorDisplayGuardWithMock = errorDisplayGuard({
14+
vue: MockVue,
15+
}).default;
16+
});
17+
18+
describe('with no error query param', () => {
19+
it('should continue if no error query param', async () => {
20+
// ACT
21+
await errorDisplayGuardWithMock({ query: {} }, {}, nextMock);
22+
23+
// ASSERT
24+
expect(nextMock).to.have.been.calledOnce;
25+
expect(nextMock).to.have.been.calledWith();
26+
expect(MockVue.toasted.show).not.to.have.been.called;
27+
});
28+
});
29+
30+
describe('with error query param', () => {
31+
// ARRANGE
32+
const mockTo = {
33+
path: '/mockPath',
34+
query: {
35+
otherQuery: 'mockValue',
36+
error: 'Mock Error Message',
37+
},
38+
};
39+
it('should call toast show with error query param', async () => {
40+
// ACT
41+
await errorDisplayGuardWithMock(mockTo, {}, nextMock);
42+
43+
// ASSERT
44+
expect(MockVue.toasted.show).to.have.been.calledWith(mockTo.query.error);
45+
});
46+
it('should call next with same to object but with the query excluding error', async () => {
47+
// ACT
48+
await errorDisplayGuardWithMock(mockTo, {}, nextMock);
49+
50+
// ASSERT
51+
expect(nextMock).to.have.been.calledOnce;
52+
const calledTo = nextMock.firstCall.args[0];
53+
expect(calledTo.path).to.equal(mockTo.path);
54+
expect(calledTo.query).to.have.property('otherQuery', mockTo.query.otherQuery);
55+
expect(calledTo.query).not.to.have.property('error');
56+
});
57+
});
58+
});

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8761,6 +8761,11 @@ vue-template-es2015-compiler@^1.2.2:
87618761
version "1.5.3"
87628762
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.5.3.tgz#22787de4e37ebd9339b74223bc467d1adee30545"
87638763

8764+
vue-toasted@^1.1.27:
8765+
version "1.1.27"
8766+
resolved "https://registry.yarnpkg.com/vue-toasted/-/vue-toasted-1.1.27.tgz#ce0a74b875f90c2e4a9e163cce6d5fc37d78a07c"
8767+
integrity sha512-GVbwInwnqkVxQ4GU/XYeQt1e0dAXL8sF5Hr1H/coCBbYUan5xP0G2mEz/HRDf1lt73rFQAN/bJcLTOKkqiM6tg==
8768+
87648769
vue-trix@^1.1.0:
87658770
version "1.1.0"
87668771
resolved "https://registry.yarnpkg.com/vue-trix/-/vue-trix-1.1.0.tgz#62468fcc8dce7269fd5662b95588b8bd6bd06bc2"

0 commit comments

Comments
 (0)