Files
MagicMirror/tests/configs/modules/calendar/calendarShowEndConfigs.js
Kristjan ESPERANTO e1c44a86bb fix(calendar): make showEnd behavior more consistent across time formats (#4059)
Closes #4053

This started as a small fix.
After feedback and more debugging, I found more issues and
inconsistencies and went a bit down the rabbit hole.
The PR is bigger now, but I think the result is better: behavior is more
predictable, and the output is more consistent.

## Changes

- Multi-day full-day events now show an end date in `relative` and
`dateheaders` when `showEnd` is enabled.
- With `absolute` + `nextDaysRelative`, full-day events now keep the end
date when the start is replaced by TODAY/TOMORROW/etc.
- Timed events in `absolute` now also respect
`showEndsOnlyWithDuration`.
- Tests were expanded and refactored to cover more showEnd cases and
keep the setup easier to maintain.
- I also refactored parts of the calendar time rendering to reduce
duplicate logic.

## Before

<img width="1816" height="756" alt="before"
src="https://github.com/user-attachments/assets/ebec81fd-0c4a-4f9f-bbe3-e2b32ef6756e"
/>

## After

<img width="1816" height="756" alt="after"
src="https://github.com/user-attachments/assets/8a2c652d-dddc-4f6b-9074-fbef3411f9ed"
/>
2026-03-18 00:32:55 +01:00

362 lines
7.9 KiB
JavaScript

const calendarShowEndConfigs = {
event_with_time_over_multiple_days_non_repeating_display_end: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM, HH:mm",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics"
}
]
}
}
]
},
event_with_time_over_multiple_days_non_repeating_display_end_dateheaders: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM, HH:mm",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "dateheaders",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_yearly.ics"
}
]
}
}
]
},
event_with_time_over_multiple_days_non_repeating_display_end_relative: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM, HH:mm",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "relative",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_yearly.ics"
}
]
}
}
]
},
event_with_time_over_multiple_days_non_repeating_no_display_end: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM, HH:mm",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
showEndsOnlyWithDuration: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_over_multiple_days_non_repeating.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_absolute: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_absolute_dateformat_lll: {
address: "0.0.0.0",
ipWhitelist: [],
language: "en",
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "LLL",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_absolute_dateformat_with_time: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM, HH:mm",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_dateheaders: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "dateheaders",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_relative: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "relative",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
event_with_time_same_day_yearly_display_end_relative_hide_time: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
dateFormat: "Do.MMM",
dateEndFormat: "Do.MMM, HH:mm",
fullDayEventDateFormat: "Do.MMM",
timeFormat: "relative",
getRelative: 0,
hideTime: true,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/event_with_time_same_day_yearly.ics"
}
]
}
}
]
},
fullday_multiday_showend_dateheaders: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
fullDayEventDateFormat: "Do.MMM",
timeFormat: "dateheaders",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics"
}
]
}
}
]
},
fullday_multiday_showend_nextdaysrelative: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
fullDayEventDateFormat: "Do.MMM",
timeFormat: "absolute",
getRelative: 0,
showEnd: true,
nextDaysRelative: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics"
}
]
}
}
]
},
fullday_multiday_showend_relative: {
address: "0.0.0.0",
ipWhitelist: [],
timeFormat: 24,
modules: [
{
module: "calendar",
position: "bottom_bar",
config: {
fade: false,
urgency: 0,
fullDayEventDateFormat: "Do.MMM",
timeFormat: "relative",
getRelative: 0,
showEnd: true,
calendars: [
{
maximumEntries: 100,
url: "http://localhost:8080/tests/mocks/fullday_event_over_multiple_days_nonrepeating.ics"
}
]
}
}
]
}
};
const defaultScenario = "event_with_time_over_multiple_days_non_repeating_display_end";
const selectedScenario = process.env.MM_CALENDAR_SHOWEND_SCENARIO || defaultScenario;
const config = calendarShowEndConfigs[selectedScenario];
if (!config) {
throw new Error(`Unknown MM_CALENDAR_SHOWEND_SCENARIO: ${selectedScenario}`);
}
module.exports = config;