Skip to content

Commit 5d7ddbb

Browse files
fix: render single-date tasks and task checkbox visibility
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
1 parent 99964d8 commit 5d7ddbb

5 files changed

Lines changed: 114 additions & 48 deletions

File tree

css/fullcalendar.scss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@
213213
}
214214
}
215215

216+
.fc-task-checkbox {
217+
border-style: solid;
218+
border-width: 2px;
219+
border-radius: 4px;
220+
height: 16px;
221+
width: 16px;
222+
}
223+
224+
.fc-task-checkbox--checked {
225+
border-style: solid;
226+
border-width: 8px;
227+
border-radius: 4px;
228+
height: 16px;
229+
width: 16px;
230+
}
231+
216232
.fc-list-event-checkbox {
217233
margin: 2px 4px 0 -2px;
218234
line-height: 1;

src/components/CalendarGrid.vue

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -417,22 +417,6 @@ export default {
417417
</script>
418418
419419
<style scoped lang="scss">
420-
.calendar-grid-checkbox {
421-
border-style: solid;
422-
border-width: 2px;
423-
border-radius: 4px;
424-
height: 16px;
425-
width: 16px;
426-
}
427-
428-
.calendar-grid-checkbox-checked {
429-
border-style: solid;
430-
border-width: 8px;
431-
border-radius: 4px;
432-
height: 16px;
433-
width: 16px;
434-
}
435-
436420
.fullcalendar-widget{
437421
min-height: 500px;
438422
:deep(.fc-col-header-cell-cushion){

src/fullcalendar/eventSources/eventSourceFunction.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,34 @@ export function eventSourceFunction(calendarObjects, calendar, start, end, timez
9696
// if there is no due date, we store the task in the
9797
// tasksstore, so user can add it to the calendar if
9898
// he wants
99-
if (object.endDate === null) {
100-
jsStart = null
101-
jsEnd = null
102-
} else {
103-
jsStart = object.endDate.getInTimezone(timezone).jsDate
99+
jsStart = null
100+
jsEnd = null
101+
// Pick up the start and end dates if available.
102+
if (object.startDate) {
103+
jsStart = object.startDate.getInTimezone(timezone).jsDate
104+
}
105+
if (object.endDate) {
104106
jsEnd = object.endDate.getInTimezone(timezone).jsDate
105107
}
108+
let hasOnlyOneDate = false
109+
if (jsStart === null && jsEnd !== null) {
110+
// Task has no start date. Display the start
111+
// of the task as its due date.
112+
jsStart = new Date(jsEnd)
113+
hasOnlyOneDate = true
114+
} else if (jsStart !== null && jsEnd === null) {
115+
// Task has no due date. Display the end of
116+
// the task as its start date.
117+
jsEnd = new Date(jsStart)
118+
hasOnlyOneDate = true
119+
}
120+
if (hasOnlyOneDate && !object.isAllDay()) {
121+
// A timed task with only one date would otherwise render as
122+
// a zero-length slot in the time grid, too short for its
123+
// checkbox and title to be legible. Give it a visible
124+
// minimum span instead.
125+
jsEnd = new Date(jsEnd.getTime() + 30 * 60 * 1000)
126+
}
106127
} else {
107128
// We do not want to display anything that's neither
108129
// an event nor a task
@@ -199,7 +220,7 @@ export function eventSourceFunction(calendarObjects, calendar, start, end, timez
199220
}
200221
}
201222

202-
if (object.name === 'VTODO' && object.endDate === null && object.percent !== 100 && object.status !== 'COMPLETED') {
223+
if (object.name === 'VTODO' && object.startDate === null && object.endDate === null && object.percent !== 100 && object.status !== 'COMPLETED') {
203224
fcEvent.create = true
204225
tasksStore.appendTask(calendar.id, fcEvent)
205226
} else {

src/fullcalendar/rendering/eventDidMount.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ export default errorCatch(function({ event, el }) {
3232
dotElement.style.color = 'var(--color-main-text)'
3333

3434
if (event.extendedProps.percent === 100) {
35-
dotElement.classList.add('calendar-grid-checkbox-checked')
35+
dotElement.classList.add('fc-task-checkbox--checked')
3636
} else {
37-
dotElement.classList.add('calendar-grid-checkbox')
37+
dotElement.classList.add('fc-task-checkbox')
3838
}
3939
} else if (el.classList.contains('fc-daygrid-dot-event')) {
4040
// Dot event in day grid view
@@ -44,19 +44,19 @@ export default errorCatch(function({ event, el }) {
4444
dotElement.style.color = 'var(--color-main-text)'
4545

4646
if (event.extendedProps.percent === 100) {
47-
dotElement.classList.add('calendar-grid-checkbox-checked')
47+
dotElement.classList.add('fc-task-checkbox--checked')
4848
} else {
49-
dotElement.classList.add('calendar-grid-checkbox')
49+
dotElement.classList.add('fc-task-checkbox')
5050
}
5151
} else {
5252
// AgendaView and all-day grid view
5353
const titleContainer = el.querySelector('.fc-event-title-container')
5454
const checkboxElement = document.createElement('div')
5555
checkboxElement.classList.add('fc-event-title-checkbox')
5656
if (event.extendedProps.percent === 100) {
57-
checkboxElement.classList.add('calendar-grid-checkbox-checked')
57+
checkboxElement.classList.add('fc-task-checkbox--checked')
5858
} else {
59-
checkboxElement.classList.add('calendar-grid-checkbox')
59+
checkboxElement.classList.add('fc-task-checkbox')
6060
}
6161

6262
titleContainer.prepend(checkboxElement)

tests/javascript/unit/fullcalendar/eventSources/freeBusyResourceEventSourceFunction.test.js

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
4242
const event21End = new Date(2020, 5, 6, 0, 0, 0, 0);
4343
const event31Start = new Date(2020, 6, 10, 10, 0, 0, 0);
4444
const event31End = new Date(2020, 6, 10, 10, 0, 0, 0);
45+
const event6Start = new Date(2020, 7, 1, 9, 0, 0, 0)
4546

4647
const eventComponentSet1 = [{
4748
name: 'VEVENT',
@@ -564,6 +565,23 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
564565
percent: null,
565566
getFirstPropertyFirstValue: vi.fn().mockReturnValue(null),
566567
getPropertyIterator: vi.fn().mockReturnValue([]),
568+
}, {
569+
name: 'VTODO',
570+
id: '7',
571+
isAllDay: vi.fn().mockReturnValue(false),
572+
getReferenceRecurrenceId: vi.fn().mockReturnValue({ unixTime: 123 }),
573+
canModifyAllDay: vi.fn().mockReturnValue(false),
574+
startDate: {
575+
getInTimezone: vi.fn().mockReturnValue({
576+
jsDate: event6Start,
577+
}),
578+
},
579+
endDate: null,
580+
hasComponent: vi.fn().mockReturnValue(false),
581+
title: 'Task without Due but with a start date',
582+
percent: null,
583+
getFirstPropertyFirstValue: vi.fn().mockReturnValue(null),
584+
getPropertyIterator: vi.fn().mockReturnValue([]),
567585
}]
568586

569587
getAllObjectsInTimeRange
@@ -607,7 +625,7 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
607625
attendeeCount: 0,
608626
},
609627
id: '1###1',
610-
start: event1End,
628+
start: event1Start,
611629
title: 'Untitled task',
612630
}, {
613631
allDay: false,
@@ -631,7 +649,7 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
631649
attendeeCount: 0,
632650
},
633651
id: '1###2',
634-
start: event2End,
652+
start: event2Start,
635653
title: 'Untitled task',
636654
}, {
637655
allDay: false,
@@ -655,7 +673,7 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
655673
attendeeCount: 0,
656674
},
657675
id: '1###3',
658-
start: event3End,
676+
start: event3Start,
659677
title: 'Untitled task (99%)',
660678
}, {
661679
allDay: false,
@@ -679,7 +697,7 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
679697
attendeeCount: 0,
680698
},
681699
id: '1###4',
682-
start: event4End,
700+
start: event4Start,
683701
title: 'This task has a title',
684702
}, {
685703
allDay: false,
@@ -703,30 +721,57 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
703721
attendeeCount: 0,
704722
},
705723
id: '1###5',
706-
start: event5End,
724+
start: event5Start,
707725
title: 'This task has a title and percent (99%)',
726+
}, {
727+
allDay: false,
728+
classNames: [
729+
'fc-event-nc-task',
730+
],
731+
end: new Date(event6Start.getTime() + 30 * 60 * 1000),
732+
extendedProps: {
733+
calendarId: 'Calendar id 456',
734+
calendarName: 'Calendar displayname',
735+
calendarOrder: 1337,
736+
canModifyAllDay: false,
737+
davUrl: 'url1',
738+
objectId: '1',
739+
vobjectId: '7',
740+
objectType: 'VTODO',
741+
percent: null,
742+
recurrenceId: 123,
743+
description: undefined,
744+
location: undefined,
745+
attendeeCount: 0,
746+
},
747+
id: '1###7',
748+
start: event6Start,
749+
title: 'Task without Due but with a start date',
708750
}])
709751

710-
expect(eventComponentSet[0].startDate.getInTimezone).toHaveBeenCalledTimes(0)
711-
expect(eventComponentSet[0].endDate.getInTimezone).toHaveBeenCalledTimes(2)
752+
expect(eventComponentSet[6].startDate.getInTimezone).toHaveBeenCalledTimes(1)
753+
expect(eventComponentSet[6].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
754+
755+
expect(eventComponentSet[0].startDate.getInTimezone).toHaveBeenCalledTimes(1)
756+
expect(eventComponentSet[0].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
757+
expect(eventComponentSet[0].endDate.getInTimezone).toHaveBeenCalledTimes(1)
712758
expect(eventComponentSet[0].endDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
713-
expect(eventComponentSet[0].endDate.getInTimezone).toHaveBeenNthCalledWith(2, timezone)
714-
expect(eventComponentSet[1].startDate.getInTimezone).toHaveBeenCalledTimes(0)
715-
expect(eventComponentSet[1].endDate.getInTimezone).toHaveBeenCalledTimes(2)
759+
expect(eventComponentSet[1].startDate.getInTimezone).toHaveBeenCalledTimes(1)
760+
expect(eventComponentSet[1].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
761+
expect(eventComponentSet[1].endDate.getInTimezone).toHaveBeenCalledTimes(1)
716762
expect(eventComponentSet[1].endDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
717-
expect(eventComponentSet[1].endDate.getInTimezone).toHaveBeenNthCalledWith(2, timezone)
718-
expect(eventComponentSet[2].startDate.getInTimezone).toHaveBeenCalledTimes(0)
719-
expect(eventComponentSet[2].endDate.getInTimezone).toHaveBeenCalledTimes(2)
763+
expect(eventComponentSet[2].startDate.getInTimezone).toHaveBeenCalledTimes(1)
764+
expect(eventComponentSet[2].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
765+
expect(eventComponentSet[2].endDate.getInTimezone).toHaveBeenCalledTimes(1)
720766
expect(eventComponentSet[2].endDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
721-
expect(eventComponentSet[2].endDate.getInTimezone).toHaveBeenNthCalledWith(2, timezone)
722-
expect(eventComponentSet[3].startDate.getInTimezone).toHaveBeenCalledTimes(0)
723-
expect(eventComponentSet[3].endDate.getInTimezone).toHaveBeenCalledTimes(2)
767+
expect(eventComponentSet[3].startDate.getInTimezone).toHaveBeenCalledTimes(1)
768+
expect(eventComponentSet[3].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
769+
expect(eventComponentSet[3].endDate.getInTimezone).toHaveBeenCalledTimes(1)
724770
expect(eventComponentSet[3].endDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
725-
expect(eventComponentSet[3].endDate.getInTimezone).toHaveBeenNthCalledWith(2, timezone)
726-
expect(eventComponentSet[4].startDate.getInTimezone).toHaveBeenCalledTimes(0)
727-
expect(eventComponentSet[4].endDate.getInTimezone).toHaveBeenCalledTimes(2)
771+
expect(eventComponentSet[4].startDate.getInTimezone).toHaveBeenCalledTimes(1)
772+
expect(eventComponentSet[4].startDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
773+
expect(eventComponentSet[4].endDate.getInTimezone).toHaveBeenCalledTimes(1)
728774
expect(eventComponentSet[4].endDate.getInTimezone).toHaveBeenNthCalledWith(1, timezone)
729-
expect(eventComponentSet[4].endDate.getInTimezone).toHaveBeenNthCalledWith(2, timezone)
730775

731776
expect(translate).toHaveBeenCalledTimes(3)
732777
expect(translate).toHaveBeenNthCalledWith(1, 'calendar', 'Untitled task')

0 commit comments

Comments
 (0)