Skip to content

[WC-2892]: Calendar improvements #1628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c8a15ef
feat(calendar-web): add hour scope for the day view
rahmanunver Jun 13, 2025
d84d420
feat(calendar-web): add showAllEvents prop for month view
rahmanunver Jun 13, 2025
30b0ed8
feat(calendar-web): add custom week day selection
rahmanunver Jun 17, 2025
985eb82
feat(calendar-web): split standard and custom config
rahmanunver Jun 17, 2025
e666015
feat(calendar-web): editorconfig edits
rahmanunver Jun 17, 2025
50e3df0
test(calendar-web): update snapshot
rahmanunver Jun 19, 2025
ab8c1ab
fix: small text changes
samuelreichert Jun 24, 2025
35246a7
feat(calendar-web): create CustomWeekController to handle CustomWeek …
samuelreichert Jun 24, 2025
2541817
feat(calendar-web): create CalendarPropsBuilder
samuelreichert Jun 24, 2025
cfc723b
chore(calendar-web): update calendar-utils to have only functions nee…
samuelreichert Jun 24, 2025
bbadd54
feat(calendar-web): use the new CalendarPropsBuilder
samuelreichert Jun 24, 2025
553f6de
refactor(calendar-web): convert event handlers to arrow functions and…
samuelreichert Jun 25, 2025
cb7b8c3
test(calendar-web): update Calendar snapshot to reflect recent changes
samuelreichert Jun 25, 2025
86626c0
feat(calendar-web): add buildFormats method to customize event displa…
samuelreichert Jun 27, 2025
3bdf925
feat(calendar-web): add time format property to Calendar widget
samuelreichert Jun 27, 2025
537724e
feat(calendar-web): add custom formats handling in CalendarPropsBuilder
samuelreichert Jun 27, 2025
605f57f
test(calendar-web): update test with timezone set to UTC
rahmanunver Jul 4, 2025
4156b32
Revert "test(calendar-web): update test with timezone set to UTC"
rahmanunver Jul 4, 2025
490ab38
test(calendar-web): add TZ to test script, update snapshot
rahmanunver Jul 9, 2025
a4a8e43
feat(calendar-web): fix dart warnings, timeformat issue, event namings
rahmanunver Jul 14, 2025
5f2727e
feat(calendar-web): .xml edits, naming
rahmanunver Jul 17, 2025
2ac4114
feat(calendar-web): update props
rahmanunver Jul 17, 2025
ca747dd
feat(calendar-web): update props
rahmanunver Jul 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/calendar-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"publish-marketplace": "rui-publish-marketplace",
"release": "pluggable-widgets-tools release:web",
"start": "pluggable-widgets-tools start:server",
"test": "jest --projects jest.config.js",
"test": "cross-env TZ=UTC jest --projects jest.config.js",
"update-changelog": "rui-update-changelog-widget",
"verify": "rui-verify-package-format"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ export function getProperties(values: CalendarPreviewProps, defaultProperties: P
hidePropertiesIn(defaultProperties, values, ["maxHeight", "overflowY"]);
}

// Hide custom week range properties when the view is set to 'standard'
if (values.view === "standard") {
hidePropertiesIn(defaultProperties, values, [
"defaultViewCustom",
"customViewShowSunday",
"customViewShowMonday",
"customViewShowTuesday",
"customViewShowWednesday",
"customViewShowThursday",
"customViewShowFriday",
"customViewShowSaturday",
"customViewCaption"
]);
} else {
hidePropertyIn(defaultProperties, values, "defaultViewStandard");
}

// Show/hide title properties based on selection
if (values.titleType === "attribute") {
hidePropertyIn(defaultProperties, values, "titleExpression");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import classnames from "classnames";
import * as dateFns from "date-fns";
import { ReactElement, createElement } from "react";
import { Calendar, dateFnsLocalizer } from "react-big-calendar";
import { Calendar, dateFnsLocalizer, EventPropGetter } from "react-big-calendar";
import { CalendarPreviewProps } from "../typings/CalendarProps";
import { CustomToolbar } from "./components/Toolbar";
import { constructWrapperStyle, WrapperStyleProps } from "./utils/style-utils";
Expand Down Expand Up @@ -73,15 +73,19 @@ export function preview(props: CalendarPreviewProps): ReactElement {
const { class: className } = props;
const wrapperStyle = constructWrapperStyle(props as WrapperStyleProps);

// Cast eventPropGetter to satisfy preview Calendar generic
const previewEventPropGetter = eventPropGetter as unknown as EventPropGetter<(typeof events)[0]>;

return (
<div className={classnames("widget-events-preview", "widget-calendar", className)} style={wrapperStyle}>
<Calendar
components={{ toolbar: CustomToolbar }}
defaultView={props.defaultView}
defaultView={props.defaultViewStandard}
events={events}
localizer={localizer}
views={["day", "week", "month"]}
eventPropGetter={eventPropGetter}
messages={{ ...localizer.messages, work_week: "Custom" }}
views={["day", "week", "month", "work_week"]}
eventPropGetter={previewEventPropGetter}
/>
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/pluggableWidgets/calendar-web/src/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import classnames from "classnames";
import { ReactElement, createElement } from "react";
import { DnDCalendar, extractCalendarProps } from "./utils/calendar-utils";
import { CalendarContainerProps } from "../typings/CalendarProps";
import { CalendarPropsBuilder } from "./helpers/CalendarPropsBuilder";
import { DnDCalendar } from "./utils/calendar-utils";
import { constructWrapperStyle } from "./utils/style-utils";
import "./ui/Calendar.scss";

export default function MxCalendar(props: CalendarContainerProps): ReactElement {
const { class: className } = props;
const wrapperStyle = constructWrapperStyle(props);
const calendarProps = extractCalendarProps(props);
const calendarProps = new CalendarPropsBuilder(props).build();

return (
<div className={classnames("widget-calendar", className)} style={wrapperStyle}>
Expand Down
95 changes: 76 additions & 19 deletions packages/pluggableWidgets/calendar-web/src/Calendar.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
</property>
<property key="eventColor" type="attribute" dataSource="databaseDataSource" required="false">
<caption>Color attribute</caption>
<description>Attribute containing a valid html color eg: red #FF0000 rgb(250,10,20) rgba(10,10,10, 0.5)</description>
<description>Attribute containing a valid HTML color eg: red #FF0000 rgb(250,10,20) rgba(10,10,10, 0.5)</description>
<attributeTypes>
<attributeType name="Enum" />
<attributeType name="String" />
Expand All @@ -68,7 +68,7 @@
<description>Standard has day, week and month</description>
<enumerationValues>
<enumerationValue key="standard">Standard</enumerationValue>
<enumerationValue key="custom">Custom</enumerationValue>
<enumerationValue key="custom">Custom work-week</enumerationValue>
</enumerationValues>
</property>
<property key="editable" type="enumeration" defaultValue="default">
Expand All @@ -87,15 +87,24 @@
<caption>Show event date range</caption>
<description>Show the start and end date of the event</description>
</property>
<property key="defaultView" type="enumeration" defaultValue="month">
<property key="defaultViewCustom" type="enumeration" defaultValue="month">
<caption>Initial selected view</caption>
<description>Work week and agenda are only available in custom views</description>
<description>The default view showed when the calendar is loaded</description>
<enumerationValues>
<enumerationValue key="day">Day</enumerationValue>
<enumerationValue key="week">Week</enumerationValue>
<enumerationValue key="month">Month</enumerationValue>
<enumerationValue key="work_week">Custom</enumerationValue>
<enumerationValue key="agenda">Agenda</enumerationValue>
</enumerationValues>
</property>
<property key="defaultViewStandard" type="enumeration" defaultValue="month">
<caption>Initial selected view</caption>
<description>The default view showed when the calendar is loaded</description>
<enumerationValues>
<enumerationValue key="day">Day</enumerationValue>
<enumerationValue key="week">Week</enumerationValue>
<enumerationValue key="month">Month</enumerationValue>
<enumerationValue key="work_week">(Work week)</enumerationValue>
<enumerationValue key="agenda">(Agenda)</enumerationValue>
</enumerationValues>
</property>
<property key="startDateAttribute" type="attribute" required="false">
Expand All @@ -105,6 +114,60 @@
<attributeType name="DateTime" />
</attributeTypes>
</property>
<property key="timeFormat" type="textTemplate" required="false">
<caption>Time format</caption>
<description>Default time format is "hh:mm a"</description>
</property>
<property key="minHour" type="integer" defaultValue="0">
<caption>Day start hour</caption>
<description>The hour at which the day view starts (0–23)</description>
</property>
<property key="maxHour" type="integer" defaultValue="24">
<caption>Day end hour</caption>
<description>The hour at which the day view ends (1–24)</description>
</property>
<!-- Custom week caption -->
<property key="customViewCaption" type="textTemplate" required="false">
<caption>Custom view caption</caption>
<description>Label used for the custom work-week button and title. Defaults to "Custom".</description>
<translations>
<translation lang="en_US">Custom</translation>
</translations>
</property>
<property key="showAllEvents" type="boolean" defaultValue="true">
<caption>Show all events</caption>
<description>Auto-adjust calendar height to display all events without "more" links</description>
</property>
</propertyGroup>
<propertyGroup caption="Visible days">
<property key="customViewShowMonday" type="boolean" defaultValue="true">
<caption>Monday</caption>
<description>Show Monday in the custom work-week view</description>
</property>
<property key="customViewShowTuesday" type="boolean" defaultValue="true">
<caption>Tuesday</caption>
<description>Show Tuesday in the custom work-week view</description>
</property>
<property key="customViewShowWednesday" type="boolean" defaultValue="true">
<caption>Wednesday</caption>
<description>Show Wednesday in the custom work-week view</description>
</property>
<property key="customViewShowThursday" type="boolean" defaultValue="true">
<caption>Thursday</caption>
<description>Show Thursday in the custom work-week view</description>
</property>
<property key="customViewShowFriday" type="boolean" defaultValue="true">
<caption>Friday</caption>
<description>Show Friday in the custom work-week view</description>
</property>
<property key="customViewShowSaturday" type="boolean" defaultValue="false">
<caption>Saturday</caption>
<description>Show Saturday in the custom work-week view</description>
</property>
<property key="customViewShowSunday" type="boolean" defaultValue="false">
<caption>Sunday</caption>
<description>Show Sunday in the custom work-week view</description>
</property>
</propertyGroup>
</propertyGroup>
<propertyGroup caption="Events">
Expand All @@ -115,27 +178,21 @@
<attributeType name="String" />
</attributeTypes>
</property>
<property key="onClickEvent" type="action" required="false">
<caption>On click action</caption>
<property key="onEditEvent" type="action" required="false" dataSource="databaseDataSource">
<caption>On edit</caption>
<description />
<actionVariables>
<actionVariable key="startDate" type="DateTime" caption="Event start date" />
<actionVariable key="endDate" type="DateTime" caption="Event end date" />
<actionVariable key="allDay" type="Boolean" caption="Event all day" />
<actionVariable key="title" type="String" caption="Event title" />
</actionVariables>
</property>
<property key="onCreateEvent" type="action" required="false">
<caption>On create action</caption>
<caption>On create</caption>
<description>The create event is triggered when a time slot is selected, and the 'Enable create' property is set to 'true'</description>
<actionVariables>
<actionVariable key="startDate" type="DateTime" caption="New event start date" />
<actionVariable key="endDate" type="DateTime" caption="New event end date" />
<actionVariable key="allDay" type="Boolean" caption="All day flag" />
<actionVariable key="allDay" type="Boolean" caption="New event all day" />
</actionVariables>
</property>
<property key="onChange" type="action" required="false">
<caption>On change action</caption>
<property key="onDragDropResize" type="action" required="false" dataSource="databaseDataSource">
<caption>On drag/drop/resize</caption>
<description>The change event is triggered on moving/dragging an item or changing the start or end time of by resizing an item</description>
<actionVariables>
<actionVariable key="oldStart" type="DateTime" caption="Old start date" />
Expand All @@ -144,7 +201,7 @@
<actionVariable key="newEnd" type="DateTime" caption="New end date" />
</actionVariables>
</property>
<property key="onRangeChange" type="action" required="false">
<property key="onViewRangeChange" type="action" required="false">
<caption>On view range change</caption>
<description>Triggered when the calendar view range (start/end) changes</description>
<actionVariables>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,42 @@ import { ListValueBuilder } from "@mendix/widget-plugin-test-utils";

import Calendar from "../Calendar";
import { CalendarContainerProps } from "../../typings/CalendarProps";
const defaultProps: CalendarContainerProps = {
const customViewProps: CalendarContainerProps = {
name: "calendar-test",
class: "calendar-class",
tabIndex: 0,
databaseDataSource: new ListValueBuilder().withItems([]).build(),
titleType: "attribute",
view: "standard",
defaultView: "month",
view: "custom",
defaultViewStandard: "month",
defaultViewCustom: "work_week",
editable: "default",
enableCreate: true,
widthUnit: "percentage",
width: 100,
heightUnit: "pixels",
height: 400,
minHour: 0,
maxHour: 24,
minHeightUnit: "pixels",
minHeight: 400,
maxHeightUnit: "none",
maxHeight: 400,
overflowY: "auto",
showEventDate: true
showEventDate: true,
customViewShowSunday: false,
customViewShowMonday: true,
customViewShowTuesday: true,
customViewShowWednesday: true,
customViewShowThursday: true,
customViewShowFriday: true,
customViewShowSaturday: false,
showAllEvents: true
};

const standardViewProps: CalendarContainerProps = {
...customViewProps,
view: "standard"
};

beforeAll(() => {
Expand All @@ -37,13 +53,18 @@ afterAll(() => {

describe("Calendar", () => {
it("renders correctly with basic props", () => {
const calendar = render(<Calendar {...defaultProps} />);
const calendar = render(<Calendar {...customViewProps} />);
expect(calendar).toMatchSnapshot();
});

it("renders with correct class name", () => {
const { container } = render(<Calendar {...defaultProps} />);
const { container } = render(<Calendar {...customViewProps} />);
expect(container.querySelector(".widget-calendar")).toBeTruthy();
expect(container.querySelector(".calendar-class")).toBeTruthy();
});

it("does not render custom view button in standard view", () => {
const { queryByText } = render(<Calendar {...standardViewProps} />);
expect(queryByText("Custom")).toBeNull();
});
});
Loading
Loading