From f337799e39f4f950d59c78a3ba52f5d1244383a2 Mon Sep 17 00:00:00 2001 From: Joan Sanchez Date: Wed, 3 Apr 2019 11:34:58 +0200 Subject: [PATCH] tests & refactor calendar component #811 --- front/core/components/calendar/index.html | 10 +- front/core/components/calendar/index.js | 161 +++++++++++++------ front/core/components/calendar/index.spec.js | 125 ++++++++++++++ front/core/components/calendar/style.scss | 1 + modules/agency/front/calendar/index.html | 14 +- modules/agency/front/calendar/index.js | 62 ++++--- modules/worker/front/calendar/index.html | 4 +- modules/worker/front/calendar/index.js | 6 +- 8 files changed, 277 insertions(+), 106 deletions(-) create mode 100644 front/core/components/calendar/index.spec.js diff --git a/front/core/components/calendar/index.html b/front/core/components/calendar/index.html index 5e3d34f49..110be3d78 100644 --- a/front/core/components/calendar/index.html +++ b/front/core/components/calendar/index.html @@ -75,14 +75,14 @@ -
+ ng-style="{'color': day.event.style.color}"> - {{::day.date | date: 'd'}} + ng-style="{'background-color': day.event.style.background}"> + {{::day.dated | date: 'd'}} - {{::day.date | date: 'd'}} + {{::day.dated | date: 'd'}}
diff --git a/front/core/components/calendar/index.js b/front/core/components/calendar/index.js index 9ccb856ee..8948b5d65 100644 --- a/front/core/components/calendar/index.js +++ b/front/core/components/calendar/index.js @@ -15,37 +15,57 @@ export default class Calendar extends Component { this.skip = 1; } + /** + * Returns the initial date + * + * @return {Date} - Default date + */ get defaultDate() { return this._defaultDate; } + /** + * Sets a new initial date + * + * @param {Date} value - New default date + */ set defaultDate(value) { this._defaultDate = value; this.repaint(); } - get currentMonth() { - return this.defaultDate; - } - - get events() { - return this._events; - } - - set events(value) { + /** + * Sets initial events + * + * @param {Array} value - Array of events + */ + set data(value) { if (!value) return; - value.map(event => { - event.date = new Date(event.date); - }); + this.events = []; - this._events = value; + value.forEach(event => { + this.addEvent(event); + }); if (value.length && this.defaultDate) this.repaint(); } + /** + * Gets current month date + */ + + get currentMonth() { + return this.defaultDate; + } + + /** + * Gets next month date + * + * @return {Date} + */ get nextMonth() { const newDate = new Date(this.currentMonth); newDate.setMonth(this.currentMonth.getMonth() + 1); @@ -53,6 +73,11 @@ export default class Calendar extends Component { return newDate; } + /** + * Gets previous month date + * + * @return {Date} + */ get previousMonth() { const newDate = new Date(this.currentMonth); newDate.setMonth(this.currentMonth.getMonth() - 1); @@ -101,73 +126,100 @@ export default class Calendar extends Component { this.days = []; - for (let fieldIndex = 1; fieldIndex <= maxFields; fieldIndex++) { + for (let fieldIndex = 1; fieldIndex < maxFields; fieldIndex++) { + // Insert previous month days if (fieldIndex < weekdayOffset) { - this.addDay(this.previousMonth, dayPrevious, 'gray'); + const dated = new Date( + this.previousMonth.getFullYear(), + this.previousMonth.getMonth(), dayPrevious); + + this.insertDay(dated, 'gray'); dayPrevious++; - } else if (fieldIndex >= weekdayOffset && dayCurrent <= currentLastDay) { - this.addDay(this.currentMonth, dayCurrent); + } + + // Insert current month days + if (fieldIndex >= weekdayOffset && dayCurrent <= currentLastDay) { + const dated = new Date( + this.currentMonth.getFullYear(), + this.currentMonth.getMonth(), dayCurrent); + + this.insertDay(dated); dayCurrent++; - } else if (fieldIndex >= weekdayOffset && dayCurrent > currentLastDay) { - this.addDay(this.nextMonth, dayNext, 'gray'); + } + + // Insert next month days + if (fieldIndex >= weekdayOffset && dayCurrent > currentLastDay) { + const dated = new Date( + this.nextMonth.getFullYear(), + this.nextMonth.getMonth(), dayNext); + + this.insertDay(dated, 'gray'); dayNext++; } } } - addDay(date, day, className = '', style) { - const newDate = new Date( - date.getFullYear(), - date.getMonth(), day); - + /** + * Inserts a date on an array of month days + * + * @param {Date} dated - Date of month + * @param {String} className - Default class style + */ + insertDay(dated, className = '') { let event = this.events.find(event => { - return event.date >= newDate && event.date <= newDate; + return event.dated >= dated && event.dated <= dated; }); - if (newDate.getMonth() === this.currentMonth.getMonth() && newDate.getDay() == 0) + // Weeekends + if (dated.getMonth() === this.currentMonth.getMonth() && dated.getDay() == 0) className = 'red'; - if (event) { - className = event.className; - style = event.style; - } - - - this.days.push({date: newDate, className, style, event}); + this.days.push({dated, className, event}); } /** * Adds a new calendar event * - * @param {Date} date - Day to add event - * @param {String} className - [green, blue, orange, red] - * @param {String} title - Tooltip description - * @param {Boolean} isRemovable - True if is removable by users + * @param {Object} options - Event params + * @param {Date} options.dated - Day to add event + * @param {String} options.title - Tooltip description + * @param {String} options.className - ClassName style + * @param {Object} options.style - Style properties + * @param {Boolean} options.isRemovable - True if is removable by users */ - addEvent(date, className, title = '', isRemovable = true) { + addEvent(options) { + if (!Object.hasOwnProperty.call(options, 'isRemovable')) + options.isRemovable = true; + + options.dated = new Date(options.dated); + options.dated.setHours(0, 0, 0, 0); + const event = this.events.findIndex(event => { - return event.date >= date && event.date <= date; + return event.dated >= options.dated && event.dated <= options.dated; }); - if (event == -1) - this.events.push({date, className, title, isRemovable}); - - this.repaint(); + if (event < 0) + this.events.push(options); } - removeEvent(date) { + /** + * Removes an event from an array of events + * @param {Object} dated - Dated event + */ + removeEvent(dated) { + dated = new Date(dated); + dated.setHours(0, 0, 0, 0); + const event = this.events.findIndex(event => { - return event.date >= date && event.date <= date; + return event.dated >= dated && event.dated <= dated; }); if (event > -1) this.events.splice(event, 1); - - this.repaint(); } /** - * Moves to next month + * Moves to next month(s) * * @param {Integer} skip - Months to skip at once */ @@ -180,7 +232,7 @@ export default class Calendar extends Component { } /** - * Moves to previous month + * Moves to previous month(s) * * @param {Integer} skip - Months to skip at once */ @@ -204,11 +256,16 @@ export default class Calendar extends Component { this.emit('selection', {values: [day]}); } + /** + * WeekDay selection event + * + * @param {Integer} weekday - weekday index + */ selectAll(weekday) { let selected = []; for (let i in this.days) { const day = this.days[i]; - const date = day.date; + const date = day.dated; day.index = i; if (date.getDay() === weekday && date.getMonth() == this.defaultDate.getMonth()) selected.push(day); @@ -217,14 +274,14 @@ export default class Calendar extends Component { } } -Calendar.$inject = ['$element', '$scope', '$attrs']; +Calendar.$inject = ['$element', '$scope']; ngModule.component('vnCalendar', { template: require('./index.html'), controller: Calendar, bindings: { model: '<', - events: ' { + let controller; + let $element; + + beforeEach(ngModule('vnCore')); + + beforeEach(inject(($compile, $rootScope) => { + $element = $compile(` { + $element.remove(); + }); + + describe('events() setter', () => { + it(`should set an array of events and convert string dates to string object, then call repaint() method`, () => { + spyOn(controller, 'repaint'); + + let currentDate = new Date().toString(); + controller.events = [ + {dated: currentDate, title: 'Event 1'}, + {dated: currentDate, title: 'Event 2'}, + ]; + + expect(controller.events[0].dated instanceof Object).toBeTruthy(); + expect(controller.repaint).toHaveBeenCalledWith(); + }); + }); + + describe('addEvent()', () => { + it(`should add an event to an array of events, then call repaint() method`, () => { + spyOn(controller, 'repaint'); + controller.events = []; + controller.addEvent({ + dated: new Date(), + title: 'My event', + className: 'color' + }); + const firstEvent = controller.events[0]; + + expect(firstEvent.title).toEqual('My event'); + expect(firstEvent.isRemovable).toBeDefined(); + expect(firstEvent.isRemovable).toBeTruthy(); + expect(controller.repaint).toHaveBeenCalledWith(); + }); + + it(`should not repeat an event for the same date, should not call repaint() method`, () => { + spyOn(controller, 'repaint'); + const curDate = new Date(); + controller._events = [{ + dated: curDate, + title: 'My event 1', + className: 'color' + }]; + controller.addEvent({ + dated: curDate, + title: 'My event 2', + className: 'color' + }); + + const firstEvent = controller.events[0]; + + expect(controller.events.length).toEqual(1); + expect(firstEvent.title).toEqual('My event 1'); + expect(controller.repaint).not.toHaveBeenCalledWith(); + }); + }); + + describe('removeEvent()', () => { + it(`should remove an event from an array of events, then call repaint() method`, () => { + spyOn(controller, 'repaint'); + const curDate = new Date(); + controller._events = [{ + dated: curDate, + title: 'My event 1', + className: 'color' + }]; + controller.removeEvent(curDate); + + expect(controller.events.length).toEqual(0); + expect(controller.repaint).toHaveBeenCalledWith(); + }); + }); + + describe('moveNext()', () => { + it(`should shift to the next n months, then emit a 'moveNext' event`, () => { + spyOn(controller, 'emit'); + const currentMonth = controller.defaultDate.getMonth(); + let nextMonth = currentMonth + 1; + + controller.moveNext(1); + + expect(controller.defaultDate.getMonth()).toEqual(nextMonth); + expect(controller.emit).toHaveBeenCalledWith('moveNext'); + }); + }); + + describe('movePrevious()', () => { + it(`should shift to the previous n months, then emit a 'movePrevious' event`, () => { + spyOn(controller, 'emit'); + const currentMonth = controller.defaultDate.getMonth(); + let previousMonth = currentMonth - 1; + + controller.movePrevious(1); + + expect(controller.defaultDate.getMonth()).toEqual(previousMonth); + expect(controller.emit).toHaveBeenCalledWith('movePrevious'); + }); + }); + + describe('select()', () => { + it(`should return the selected element, then emit a 'selection' event`, () => { + spyOn(controller, 'emit'); + const days = [{dated: new Date()}]; + controller.days = days; + + controller.select(0); + + expect(controller.emit).toHaveBeenCalledWith('selection', {values: days}); + }); + }); +}); + diff --git a/front/core/components/calendar/style.scss b/front/core/components/calendar/style.scss index 69749023b..6baaa0f65 100644 --- a/front/core/components/calendar/style.scss +++ b/front/core/components/calendar/style.scss @@ -19,6 +19,7 @@ vn-calendar { .weekdays { border-bottom: 1px solid $color-hover-cd; border-top: 1px solid $color-hover-cd; + color: $color-font-secondary; font-weight: bold } diff --git a/modules/agency/front/calendar/index.html b/modules/agency/front/calendar/index.html index 53eb9bf50..d02be3059 100644 --- a/modules/agency/front/calendar/index.html +++ b/modules/agency/front/calendar/index.html @@ -7,20 +7,14 @@ primary-key="zoneFk" auto-load="true"> - - diff --git a/modules/agency/front/calendar/index.js b/modules/agency/front/calendar/index.js index 20d91f994..bf1625585 100644 --- a/modules/agency/front/calendar/index.js +++ b/modules/agency/front/calendar/index.js @@ -9,7 +9,6 @@ class Controller { this.stMonthDate = new Date(); this.ndMonthDate = new Date(); this.ndMonthDate.setMonth(this.ndMonthDate.getMonth() + 1); - this.events = []; } $postLink() { @@ -53,28 +52,23 @@ class Controller { this._data = value; if (!value) return; - const events = []; value.forEach(event => { - let date = new Date(event.delivered); - date.setHours(0, 0, 0, 0); - events.push({ - date: date, + dated: event.delivered, className: 'green-circle', - title: 'Has delivery', - isRemovable: true + title: 'Has delivery' }); }); - this.events = this.events.concat(events); + this.events = events; } onSelection(calendar, values) { let totalEvents = 0; values.forEach(day => { - const exists = this.events.findIndex(event => { - return event.date >= day.date && event.date <= day.date + const exists = calendar.events.findIndex(event => { + return event.dated >= day.dated && event.dated <= day.dated && event.isRemovable; }); @@ -82,54 +76,56 @@ class Controller { }); if (totalEvents > (values.length / 2)) - this.removeEvents(values); + this.removeEvents(calendar, values); else - this.addEvents(values); + this.insertEvents(calendar, values); } - addEvents(days) { + insertEvents(calendar, days) { days.forEach(day => { - const event = this.events.find(event => { - return event.date >= day.date && event.date <= day.date; + const event = calendar.events.find(event => { + return event.dated >= day.dated && event.dated <= day.dated; }); - if (event) - return false; + if (event) return false; this.$scope.model.insert({ zoneFk: this.zone.id, - delivered: day.date + delivered: day.dated }); - this.stMonth.addEvent(day.date, 'green-circle', 'Has delivery', true); - this.stMonth.repaint(); - this.ndMonth.addEvent(day.date, 'green-circle', 'Has delivery', true); - this.ndMonth.repaint(); + calendar.addEvent({ + dated: day.dated, + className: 'green-circle', + title: 'Has delivery' + }); + }); + + this.$scope.model.save().then(() => { + this.events = calendar.events; }); - this.$scope.model.save(); } - removeEvents(days) { + removeEvents(calendar, days) { let dates = []; days.forEach(day => { - const event = this.events.find(event => { - return event.date >= day.date && event.date <= day.date; + const event = calendar.events.find(event => { + return event.dated >= day.dated && event.dated <= day.dated; }); if (event && !event.isRemovable) return false; - dates.push(day.date); + dates.push(day.dated); - this.stMonth.removeEvent(day.date); - this.stMonth.repaint(); - this.ndMonth.removeEvent(day.date); - this.ndMonth.repaint(); + calendar.removeEvent(day.dated); }); if (dates.length == 0) return; const params = {zoneFk: this.zone.id, dates}; - this.$http.post('/agency/api/zoneCalendars/removeByDate', params); + this.$http.post('/agency/api/zoneCalendars/removeByDate', params).then(() => { + this.events = calendar.events; + }); } onMoveNext(calendar) { diff --git a/modules/worker/front/calendar/index.html b/modules/worker/front/calendar/index.html index bef551523..472ceab33 100644 --- a/modules/worker/front/calendar/index.html +++ b/modules/worker/front/calendar/index.html @@ -11,9 +11,9 @@
-
diff --git a/modules/worker/front/calendar/index.js b/modules/worker/front/calendar/index.js index c97e1b138..dafa1d24d 100644 --- a/modules/worker/front/calendar/index.js +++ b/modules/worker/front/calendar/index.js @@ -7,7 +7,6 @@ class Controller { this.$http = $http; this.months = this.monthsOfYear(); this.events = []; - this.eventMap = {}; } get worker() { @@ -31,7 +30,6 @@ class Controller { this.setHolidays(res.data); this.setWorkerCalendar(res.data); - this.calendar = res.data.calendar; }); } @@ -43,7 +41,7 @@ class Controller { const events = []; holidays.forEach(holiday => { events.push({ - date: holiday.dated, + dated: holiday.dated, className: 'red', title: holiday.detail.description || holiday.type.name, isRemovable: false @@ -60,7 +58,7 @@ class Controller { absences.forEach(absence => { const absenceType = absence.absenceType; events.push({ - date: absence.dated, + dated: absence.dated, title: absenceType.name, style: { background: absenceType.rgb