Merge pull request #4305 from nextcloud/fix/4290/color-dot

Fix color dot and event alignment
This commit is contained in:
Richard Steinmetz 2022-06-21 16:06:32 +02:00 committed by GitHub
commit c3f6da5e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 335 additions and 36 deletions

View File

@ -233,6 +233,10 @@
word-break: break-all;
white-space: normal;
}
:not(.fc-timegrid-event-short) > .fc-event-main .fc-event-title-container {
width: 100%;
}
}
.fc-v-event {

View File

@ -20,23 +20,24 @@
-
-->
<template>
<div>
<div class="fc-event-main-frame">
<div class="fc-event-time">
{{ eventDetails.timeText }}
</div>
<div class="fc-event-title-container fc-event-main-frame--icons">
<div class="fc-event-title fc-sticky">
<span class="fc-event-title">{{ eventDetails.event.title }}</span>
</div>
<Bell v-if="hasAlarms"
class="icon-event-reminder"
:size="14"
:style="{ color: iconColor }" />
<AccountMultiple v-if="hasAttendees"
:size="14"
:style="{ color: iconColor }" />
<div class="fc-event-main-frame">
<div v-if="!allDay && viewType === 'dayGridMonth'"
class="fc-daygrid-event-dot"
:style="{'border-color': borderColor }" />
<div class="fc-event-time">
{{ eventDetails.timeText }}
</div>
<div class="fc-event-title-container fc-event-main-frame--icons">
<div class="fc-event-title fc-sticky">
<span class="fc-event-title">{{ eventDetails.event.title }}</span>
</div>
<Bell v-if="hasAlarms"
class="icon-event-reminder"
:size="14"
:style="{ color: iconColor }" />
<AccountMultiple v-if="hasAttendees"
:size="14"
:style="{ color: iconColor }" />
</div>
</div>
</template>
@ -73,16 +74,45 @@ export default {
isDarkText() {
return this.eventDetails?.event?._def?.extendedProps?.darkText
},
/**
* @return {string|undefined}
*/
borderColor() {
return this.eventDetails?.event?.borderColor ?? undefined
},
/**
* @return {boolean}
*/
allDay() {
return this.eventDetails?.event?.allDay ?? false
},
},
}
</script>
<style scoped>
.fc-event-title.fc-sticky {
flex-grow: 1;
}
.fc-event-main-frame--icons {
<style lang="scss" scoped>
.fc-event-main-frame {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
.fc-daygrid-event-dot {
align-self: center;
}
&--icons {
display: flex;
justify-content: space-between;
}
.fc-event-title-container {
min-width: 0;
}
.fc-event-title.fc-sticky {
flex-grow: 1;
}
}
</style>

View File

@ -150,13 +150,12 @@ export function eventSourceFunction(calendarObjects, calendar, start, end, timez
},
}
if (object.color) {
const customColor = getHexForColorName(object.color)
if (customColor) {
fcEvent.backgroundColor = customColor
fcEvent.borderColor = customColor
fcEvent.textColor = generateTextColorForHex(customColor)
}
// Color of event object is a name while calendar color already is a hex value
const customColor = getHexForColorName(object.color) ?? calendar.color
if (customColor) {
fcEvent.backgroundColor = customColor
fcEvent.borderColor = customColor
fcEvent.textColor = generateTextColorForHex(customColor)
}
fcEvents.push(fcEvent)

View File

@ -0,0 +1,233 @@
/**
* @copyright Copyright (c) 2022 Richard Steinmetz <richard@steinmetz.cloud>
*
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import { eventSourceFunction } from '../../../../../src/fullcalendar/eventSources/eventSourceFunction.js'
import {
generateTextColorForHex,
getHexForColorName, hexToRGB,
isLight,
} from '../../../../../src/utils/color.js'
import { getAllObjectsInTimeRange } from '../../../../../src/utils/calendarObject.js'
jest.mock('../../../../../src/utils/color.js')
jest.mock('../../../../../src/utils/calendarObject.js')
describe('fullcalendar/eventSourceFunction test suite', () => {
beforeEach(() => {
generateTextColorForHex.mockClear()
getAllObjectsInTimeRange.mockClear()
getHexForColorName.mockClear()
hexToRGB.mockClear()
isLight.mockClear()
})
it('should prefer the event color', () => {
const calendar = {
id: 'calendar-id-123',
color: '#ff00ff',
readOnly: false,
}
const start = new Date()
start.setHours(start.getHours() - 24)
const end = new Date()
end.setHours(end.getHours() + 24)
const timezone = { calendarJsTimezone: true, tzid: 'America/New_York' }
const calendarObjects = [
{
calendarObject: true,
dav: {
url: 'url1',
},
id: '1',
},
]
const eventComponents = [
{
name: 'VEVENT',
id: '1-1',
status: 'CONFIRMED',
isAllDay: jest.fn().mockReturnValue(false),
getReferenceRecurrenceId: jest.fn().mockReturnValue({ unixTime: 123 }),
canModifyAllDay: jest.fn().mockReturnValue(true),
startDate: {
getInTimezone: jest.fn().mockReturnValue({
jsDate: new Date(start),
}),
},
endDate: {
getInTimezone: jest.fn().mockReturnValue({
jsDate: new Date(end),
}),
},
hasComponent: jest.fn().mockReturnValue(false),
hasProperty: jest.fn().mockReturnValue(false),
color: 'red',
},
]
getAllObjectsInTimeRange
.mockReturnValueOnce(eventComponents)
hexToRGB
.mockReturnValueOnce({ red: 255, green: 0, blue: 255 })
isLight
.mockReturnValueOnce(false)
getHexForColorName
.mockReturnValueOnce('#ff0000')
generateTextColorForHex
.mockReturnValueOnce('#eeeeee')
expect(eventSourceFunction(calendarObjects, calendar, new Date(start), new Date(end), timezone)).toEqual([
{
id: '1###1-1',
title: 'Untitled event',
allDay: false,
start,
end,
classNames: [],
extendedProps: {
objectId: '1',
recurrenceId: 123,
canModifyAllDay: true,
calendarId: 'calendar-id-123',
davUrl: 'url1',
objectType: 'VEVENT',
percent: null,
hasAlarms: false,
hasAttendees: false,
darkText: false,
},
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
},
])
expect(hexToRGB).toHaveBeenCalledTimes(1)
expect(hexToRGB).toHaveBeenNthCalledWith(1, '#ff00ff')
expect(isLight).toHaveBeenCalledTimes(1)
expect(isLight).toHaveBeenNthCalledWith(1, { red: 255, green: 0, blue: 255 })
expect(getAllObjectsInTimeRange).toHaveBeenCalledTimes(1)
expect(getAllObjectsInTimeRange).toHaveBeenNthCalledWith(1, calendarObjects[0], start, end)
expect(getHexForColorName).toHaveBeenCalledTimes(1)
expect(getHexForColorName).toHaveBeenNthCalledWith(1, 'red')
expect(generateTextColorForHex).toHaveBeenCalledTimes(1)
expect(generateTextColorForHex).toHaveBeenNthCalledWith(1, '#ff0000')
})
it('should fallback to the calendar color', () => {
const calendar = {
id: 'calendar-id-123',
color: '#ff00ff',
readOnly: false,
}
const start = new Date()
start.setHours(start.getHours() - 24)
const end = new Date()
end.setHours(end.getHours() + 24)
const timezone = { calendarJsTimezone: true, tzid: 'America/New_York' }
const calendarObjects = [
{
calendarObject: true,
dav: {
url: 'url1',
},
id: '1',
},
]
const eventComponents = [
{
name: 'VEVENT',
id: '1-1',
status: 'CONFIRMED',
isAllDay: jest.fn().mockReturnValue(false),
getReferenceRecurrenceId: jest.fn().mockReturnValue({ unixTime: 123 }),
canModifyAllDay: jest.fn().mockReturnValue(true),
startDate: {
getInTimezone: jest.fn().mockReturnValue({
jsDate: new Date(start),
}),
},
endDate: {
getInTimezone: jest.fn().mockReturnValue({
jsDate: new Date(end),
}),
},
hasComponent: jest.fn().mockReturnValue(false),
hasProperty: jest.fn().mockReturnValue(false),
},
]
getAllObjectsInTimeRange
.mockReturnValueOnce(eventComponents)
hexToRGB
.mockReturnValueOnce({ red: 255, green: 0, blue: 255 })
isLight
.mockReturnValueOnce(false)
getHexForColorName
.mockReturnValueOnce(null)
generateTextColorForHex
.mockReturnValueOnce('#eeeeee')
expect(eventSourceFunction(calendarObjects, calendar, new Date(start), new Date(end), timezone)).toEqual([
{
id: '1###1-1',
title: 'Untitled event',
allDay: false,
start,
end,
classNames: [],
extendedProps: {
objectId: '1',
recurrenceId: 123,
canModifyAllDay: true,
calendarId: 'calendar-id-123',
davUrl: 'url1',
objectType: 'VEVENT',
percent: null,
hasAlarms: false,
hasAttendees: false,
darkText: false,
},
backgroundColor: '#ff00ff',
borderColor: '#ff00ff',
textColor: '#eeeeee',
},
])
expect(hexToRGB).toHaveBeenCalledTimes(1)
expect(hexToRGB).toHaveBeenNthCalledWith(1, '#ff00ff')
expect(isLight).toHaveBeenCalledTimes(1)
expect(isLight).toHaveBeenNthCalledWith(1, { red: 255, green: 0, blue: 255 })
expect(getAllObjectsInTimeRange).toHaveBeenCalledTimes(1)
expect(getAllObjectsInTimeRange).toHaveBeenNthCalledWith(1, calendarObjects[0], start, end)
expect(getHexForColorName).toHaveBeenCalledTimes(1)
expect(getHexForColorName).toHaveBeenNthCalledWith(1, undefined)
expect(generateTextColorForHex).toHaveBeenCalledTimes(1)
expect(generateTextColorForHex).toHaveBeenNthCalledWith(1, '#ff00ff')
})
})

View File

@ -2,6 +2,7 @@
* @copyright Copyright (c) 2019 Georg Ehrke
*
* @author Georg Ehrke <oc.list@georgehrke.com>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
@ -227,6 +228,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
hasAlarms: false,
hasAttendees: false,
},
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
},
{
id: '1###1-2',
@ -249,6 +253,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
hasAlarms: false,
hasAttendees: false,
},
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
},
{
id: '1###1-3',
@ -271,6 +278,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
hasAlarms: true,
hasAttendees: false,
},
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
},
{
id: '2###2-1',
@ -293,6 +303,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
hasAlarms: false,
hasAttendees: false,
},
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
},
{
id: '4###3-1',
@ -358,11 +371,14 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
expect(getAllObjectsInTimeRange).toHaveBeenNthCalledWith(3, calendarObjects[2], start, end)
expect(getAllObjectsInTimeRange).toHaveBeenNthCalledWith(4, calendarObjects[3], start, end)
expect(getHexForColorName).toHaveBeenCalledTimes(1)
expect(getHexForColorName).toHaveBeenNthCalledWith(1, 'red')
expect(getHexForColorName).toHaveBeenCalledTimes(5)
for (let i = 1; i < 5; i++) {
expect(getHexForColorName).toHaveBeenNthCalledWith(i, undefined)
}
expect(getHexForColorName).toHaveBeenNthCalledWith(5, 'red')
expect(generateTextColorForHex).toHaveBeenCalledTimes(1)
expect(generateTextColorForHex).toHaveBeenNthCalledWith(1, '#ff0000')
expect(generateTextColorForHex).toHaveBeenCalledTimes(5)
expect(generateTextColorForHex).toHaveBeenCalledWith('#ff0000')
// Make sure the following dates have not been touched
expect(event11Start.getFullYear()).toEqual(2020)
@ -617,6 +633,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
id: '1###1',
start: event1End,
title: 'Untitled task',
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
}, {
allDay: false,
classNames: [
@ -640,6 +659,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
id: '1###2',
start: event2End,
title: 'Untitled task',
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
}, {
allDay: false,
classNames: [
@ -663,6 +685,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
id: '1###3',
start: event3End,
title: 'Untitled task (99%)',
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
}, {
allDay: false,
classNames: [
@ -681,11 +706,14 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
percent: null,
recurrenceId: 123,
hasAlarms: false,
hasAttendees: false
hasAttendees: false,
},
id: '1###4',
start: event4End,
title: 'This task has a title',
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
}, {
allDay: false,
classNames: [
@ -709,6 +737,9 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
id: '1###5',
start: event5End,
title: 'This task has a title and percent (99%)',
backgroundColor: '#ff0000',
borderColor: '#ff0000',
textColor: '#eeeeee',
}])
expect(eventComponentSet[0].startDate.getInTimezone).toHaveBeenCalledTimes(0)
@ -740,8 +771,10 @@ describe('fullcalendar/freeBusyResourceEventSourceFunction test suite', () => {
expect(getAllObjectsInTimeRange).toHaveBeenCalledTimes(1)
expect(getAllObjectsInTimeRange).toHaveBeenNthCalledWith(1, calendarObjects[0], start, end)
expect(getHexForColorName).toHaveBeenCalledTimes(0)
expect(generateTextColorForHex).toHaveBeenCalledTimes(0)
expect(getHexForColorName).toHaveBeenCalledTimes(5)
expect(getHexForColorName).toHaveBeenCalledWith(undefined)
expect(generateTextColorForHex).toHaveBeenCalledTimes(5)
expect(getHexForColorName).toHaveBeenCalledWith(undefined)
})
})