Refactoring; Separated EventGrouping class into separate classes for Day, Week, Month. Logic moved upstream into Calendar class. Simplified render selection in interface

This commit is contained in:
Franco Speziali 2022-03-23 11:28:11 +01:00
parent 1e3688ca04
commit 722ddb41e5
17 changed files with 240 additions and 204 deletions

View File

@ -1,13 +1,17 @@
import { GroupTypes } from "../types";
import Config from "../Config/Config";
import Events from "../Events/";
import EventGrouping from "../EventGrouping";
import { DayGrouping, WeekGrouping, MonthGrouping } from "../EventGrouping";
import {
DayRenderer,
WeekRenderer,
MonthRenderer,
} from "../EventGroupingHtmlRenderer";
export default class Calendar {
private readonly jsonContent: object;
public config: Config;
public events: Events;
public eventGrouping: EventGrouping;
constructor(json) {
this.jsonContent = json;
@ -15,9 +19,22 @@ export default class Calendar {
groupType: json["group"] ? json["group"].charAt(0) : GroupTypes.Day,
});
this.events = new Events(json["events"]);
this.eventGrouping = new EventGrouping(
this.events.sortedEvents,
this.config.groupType
);
}
render(): HTMLDivElement {
switch (this.config.groupType) {
case GroupTypes.Day:
return new DayRenderer(
new DayGrouping(this.events.sortedEvents)
).render();
case GroupTypes.Week:
return new WeekRenderer(
new WeekGrouping(this.events.sortedEvents)
).render();
case GroupTypes.Month:
return new MonthRenderer(
new MonthGrouping(this.events.sortedEvents)
).render();
}
}
}

View File

@ -0,0 +1,34 @@
import EventGrouping from "./EventGrouping";
import { Event, Groups } from "../types";
import { add, differenceInCalendarDays } from "date-fns";
export default class DayGrouping extends EventGrouping {
constructor(sortedEvents: Event[]) {
super(sortedEvents);
this.groups = this.group();
}
group(): Groups {
const numberOfGroups = differenceInCalendarDays(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarDays(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, this.generateEmptyGroups(numberOfGroups));
}
public getDateFromGroupIndex(index: number): Date {
const startDate = this.firstEvent.date;
return add(startDate, { days: index });
}
}

View File

@ -0,0 +1,28 @@
import { Event, Groups } from "../types";
export default abstract class EventGrouping {
protected readonly sortedEvents: Event[];
protected readonly firstEvent: Event;
protected readonly lastEvent: Event;
public groups: Groups;
protected constructor(sortedEvents: Event[]) {
this.sortedEvents = sortedEvents;
this.firstEvent = this.sortedEvents[0];
this.lastEvent = this.sortedEvents[this.sortedEvents.length - 1];
}
protected generateEmptyGroups(length: number): Groups {
const groups = [];
for (let i = 0; i <= length; i += 1) {
groups.push([]);
}
return groups;
}
public abstract getDateFromGroupIndex(index: number): Date;
public abstract group(): Groups;
}

View File

@ -0,0 +1,34 @@
import EventGrouping from "./EventGrouping";
import { Event, Groups } from "../types";
import { add, differenceInCalendarMonths } from "date-fns";
export default class MonthGrouping extends EventGrouping {
constructor(sortedEvents: Event[]) {
super(sortedEvents);
this.groups = this.group();
}
group(): Groups {
const numberOfGroups = differenceInCalendarMonths(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarMonths(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, this.generateEmptyGroups(numberOfGroups));
}
public getDateFromGroupIndex(index: number): Date {
const startDate = this.firstEvent.date;
return add(startDate, { months: index });
}
}

View File

@ -0,0 +1,34 @@
import EventGrouping from "./EventGrouping";
import { Event, Groups } from "../types";
import { add, differenceInCalendarWeeks } from "date-fns";
export default class WeekGrouping extends EventGrouping {
constructor(sortedEvents: Event[]) {
super(sortedEvents);
this.groups = this.group();
}
group(): Groups {
const numberOfGroups = differenceInCalendarWeeks(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarWeeks(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, this.generateEmptyGroups(numberOfGroups));
}
public getDateFromGroupIndex(index: number): Date {
const startDate = this.firstEvent.date;
return add(startDate, { weeks: index });
}
}

View File

@ -1,57 +0,0 @@
const EventGrouping = require("./index").default;
const { GroupTypes } = require("../types");
const mockSortedEvents = [
{
date: new Date(2022, 3, 20),
title: "first day of spring",
},
{
date: new Date(2022, 3, 25),
title: "my birthday",
},
{
date: new Date(2022, 3, 25),
title: "buy cake for 27th",
},
{
date: new Date(2022, 3, 27),
title: "d's birthday",
},
];
describe("groupEventsByDay should", () => {
const eventGrouping = new EventGrouping(mockSortedEvents, GroupTypes.Day);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(8);
});
test("place into the same group, events that occur on the same day", () => {
expect(eventGrouping.groups[5].length).toEqual(2);
});
});
describe("groupEventsByWeek should", () => {
const eventGrouping = new EventGrouping(mockSortedEvents, GroupTypes.Week);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(2);
});
test("place into the same group, events that occur on the same week", () => {
expect(eventGrouping.groups[1].length).toEqual(3);
});
});
describe("groupEventsByMonth should", () => {
const eventGrouping = new EventGrouping(mockSortedEvents, GroupTypes.Month);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(1);
});
test("place into the same group, events that occur on the same month", () => {
expect(eventGrouping.groups[0].length).toEqual(4);
});
});

View File

@ -1,112 +1,5 @@
import {
add,
differenceInCalendarDays,
differenceInCalendarWeeks,
differenceInCalendarMonths,
} from "date-fns";
import DayGrouping from "./DayGrouping";
import WeekGrouping from "./WeekGrouping";
import MonthGrouping from "./MonthGrouping";
import { Event, Groups, GroupTypes } from "../types";
export default class EventGrouping {
private readonly sortedEvents: Event[];
private readonly firstEvent: Event;
private readonly lastEvent: Event;
public readonly groupType: GroupTypes;
public readonly groups: Groups;
public static generateEmptyGroups(length: number): Groups {
const groups = [];
for (let i = 0; i <= length; i += 1) {
groups.push([]);
}
return groups;
}
constructor(sortedEvents: Event[], groupType: GroupTypes) {
this.sortedEvents = sortedEvents;
this.firstEvent = this.sortedEvents[0];
this.lastEvent = this.sortedEvents[this.sortedEvents.length - 1];
this.groupType = groupType;
this.groups = this.group(groupType);
}
private group(groupType: GroupTypes): Groups {
switch (groupType) {
case GroupTypes.Day:
return this.byDay();
case GroupTypes.Week:
return this.byWeek();
case GroupTypes.Month:
return this.byMonth();
}
}
private byDay(): Groups {
const numberOfGroups = differenceInCalendarDays(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarDays(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, EventGrouping.generateEmptyGroups(numberOfGroups));
}
private byWeek(): Groups {
const numberOfGroups = differenceInCalendarWeeks(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarWeeks(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, EventGrouping.generateEmptyGroups(numberOfGroups));
}
private byMonth(): Groups {
const numberOfGroups = differenceInCalendarMonths(
this.lastEvent.date,
this.firstEvent.date
);
return this.sortedEvents.reduce((prev, event) => {
const groupNumber = differenceInCalendarMonths(
event.date,
this.firstEvent.date
);
prev[groupNumber].push(event);
return prev;
}, EventGrouping.generateEmptyGroups(numberOfGroups));
}
public getDateFromGroupIndex(index: number): Date {
const startDate = this.firstEvent.date;
switch (this.groupType) {
case GroupTypes.Day:
return add(startDate, { days: index });
case GroupTypes.Week:
return add(startDate, { weeks: index });
case GroupTypes.Month:
return add(startDate, { months: index });
}
}
}
export { DayGrouping, WeekGrouping, MonthGrouping };

View File

@ -0,0 +1,15 @@
const { GroupTypes } = require("../../types");
const DayGrouping = require("../DayGrouping").default;
const mockSortedEvents = require("./mockSortedEvents").default;
describe("groupEventsByDay should", () => {
const eventGrouping = new DayGrouping(mockSortedEvents, GroupTypes.Day);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(8);
});
test("place into the same group, events that occur on the same day", () => {
expect(eventGrouping.groups[5].length).toEqual(2);
});
});

View File

@ -0,0 +1,15 @@
const { GroupTypes } = require("../../types");
const MonthGrouping = require("../MonthGrouping").default;
const mockSortedEvents = require("./mockSortedEvents").default;
describe("groupEventsByMonth should", () => {
const eventGrouping = new MonthGrouping(mockSortedEvents, GroupTypes.Month);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(1);
});
test("place into the same group, events that occur on the same month", () => {
expect(eventGrouping.groups[0].length).toEqual(4);
});
});

View File

@ -0,0 +1,15 @@
const { GroupTypes } = require("../../types");
const WeekGrouping = require("../WeekGrouping").default;
const mockSortedEvents = require("./mockSortedEvents").default;
describe("groupEventsByWeek should", () => {
const eventGrouping = new WeekGrouping(mockSortedEvents, GroupTypes.Week);
test("generate the correct number of groups", () => {
expect(eventGrouping.groups.length).toEqual(2);
});
test("place into the same group, events that occur on the same week", () => {
expect(eventGrouping.groups[1].length).toEqual(3);
});
});

View File

@ -0,0 +1,18 @@
export default [
{
date: new Date(2022, 3, 20),
title: "first day of spring",
},
{
date: new Date(2022, 3, 25),
title: "my birthday",
},
{
date: new Date(2022, 3, 25),
title: "buy cake for 27th",
},
{
date: new Date(2022, 3, 27),
title: "d's birthday",
},
];

View File

@ -1,8 +1,8 @@
import EventGrouping from "../EventGrouping";
import { DayGrouping } from "../EventGrouping";
import Renderer from "./Renderer";
export default class DayRenderer extends Renderer {
constructor(eventGrouping: EventGrouping) {
constructor(eventGrouping: DayGrouping) {
super(eventGrouping);
this.container.className += " scale-day";
}

View File

@ -1,8 +1,8 @@
import EventGrouping from "../EventGrouping";
import { MonthGrouping } from "../EventGrouping";
import Renderer from "./Renderer";
export default class MonthRenderer extends Renderer {
constructor(eventGrouping: EventGrouping) {
constructor(eventGrouping: MonthGrouping) {
super(eventGrouping);
this.container.className += " scale-month";
}

View File

@ -1,6 +1,6 @@
import { format } from "date-fns";
import { generateRandomColor } from "../utilities";
import EventGrouping from "../EventGrouping";
import EventGrouping from "../EventGrouping/EventGrouping";
import { Event } from "../types";
export default abstract class Renderer {
@ -105,7 +105,7 @@ export default abstract class Renderer {
return html;
}
public render() {
public render(): HTMLDivElement {
const groups = this.renderGroups();
groups.forEach((group) => this.container.appendChild(group));

View File

@ -1,9 +1,10 @@
import { getWeek } from "date-fns";
import EventGrouping from "../EventGrouping";
import { WeekGrouping } from "../EventGrouping";
import Renderer from "./Renderer";
export default class WeekRenderer extends Renderer {
constructor(eventGrouping: EventGrouping) {
constructor(eventGrouping: WeekGrouping) {
super(eventGrouping);
this.container.className += " scale-week";
}

View File

@ -0,0 +1,5 @@
import DayRenderer from "./DayRenderer";
import WeekRenderer from "./WeekRenderer";
import MonthRenderer from "./MonthRenderer";
export { DayRenderer, WeekRenderer, MonthRenderer };

View File

@ -1,11 +1,6 @@
import { GroupTypes } from "./types";
const YAML = require("yaml");
import Calendar from "./Calendar/";
import DayRenderer from "./EventGroupingHtmlRenderer/DayRenderer";
import WeekRenderer from "./EventGroupingHtmlRenderer/WeekRenderer";
import MonthRenderer from "./EventGroupingHtmlRenderer/MonthRenderer";
export default function () {
return {
@ -32,27 +27,16 @@ export default function () {
const token = tokens[idx];
if (token.info !== "joplin-plugin-event-calendar")
return defaultRender(tokens, idx, options, env, self);
let jsonContent: object;
let calendar: Calendar;
let contentHtml = document.createElement("div");
try {
jsonContent = YAML.parse(markdownIt.utils.escapeHtml(token.content));
calendar = new Calendar(jsonContent);
switch (calendar.config.groupTypes) {
case GroupTypes.Day:
contentHtml = new DayRenderer(calendar.eventGrouping).render();
break;
case GroupTypes.Week:
contentHtml = new WeekRenderer(calendar.eventGrouping).render();
break;
case GroupTypes.Month:
contentHtml = new MonthRenderer(calendar.eventGrouping).render();
break;
}
const jsonContent = YAML.parse(
markdownIt.utils.escapeHtml(token.content)
);
const calendar = new Calendar(jsonContent);
const contentHtml = calendar.render();
return `<div class="joplin-editable">${contentHtml.outerHTML}</div>`;
} catch (error) {
console.log(error);
}
return `<div class="joplin-editable">${contentHtml.outerHTML}</div>`;
};
},
assets: function () {