ExpandableCalendar initial position "open" is partially cut off in latest version (v1.1311.0)
Description:
In the latest version of react-native-calendars, setting the ExpandableCalendar's initial position to "open" does not fully display the calendar. Only part of the calendar appears, and it seems to be cut off visually.
This issue can be reproduced using one of the existing examples provided in the repo — specifically, the TimelineScreen example.
This behavior was not present in a previous version (1.1307.0) where the calendar would correctly render in its fully expanded state when the initialPosition prop was set to Positions.OPEN.
Code Sample:
I just added the following to the example initialPosition={Positions.OPEN}
import groupBy from "lodash/groupBy";
import filter from "lodash/filter";
import find from "lodash/find";
import React, { Component } from "react";
import { Alert } from "react-native";
import {
ExpandableCalendar,
TimelineEventProps,
TimelineList,
CalendarProvider,
TimelineProps,
CalendarUtils,
} from "react-native-calendars";
import { timelineEvents, getDate } from "./timelineEvents";
import { Positions } from "react-native-calendars/src/expandableCalendar";
const INITIAL_TIME = { hour: 9, minutes: 0 };
const EVENTS: TimelineEventProps[] = timelineEvents;
export default class TimelineCalendarScreen extends Component {
state = {
currentDate: getDate(),
events: EVENTS,
eventsByDate: groupBy(EVENTS, (e) =>
CalendarUtils.getCalendarDateString(e.start)
) as {
[key: string]: TimelineEventProps[];
},
};
marked = {
[`${getDate(-1)}`]: { marked: true },
[`${getDate()}`]: { marked: true },
[`${getDate(1)}`]: { marked: true },
[`${getDate(2)}`]: { marked: true },
[`${getDate(4)}`]: { marked: true },
};
onDateChanged = (date: string, source: string) => {
console.log("TimelineCalendarScreen onDateChanged: ", date, source);
this.setState({ currentDate: date });
};
onMonthChange = (month: any, updateSource: any) => {
console.log("TimelineCalendarScreen onMonthChange: ", month, updateSource);
};
createNewEvent: TimelineProps["onBackgroundLongPress"] = (
timeString,
timeObject
) => {
const { eventsByDate } = this.state;
const hourString = `${(timeObject.hour + 1).toString().padStart(2, "0")}`;
const minutesString = `${timeObject.minutes.toString().padStart(2, "0")}`;
const newEvent = {
id: "draft",
start: `${timeString}`,
end: `${timeObject.date} ${hourString}:${minutesString}:00`,
title: "New Event",
color: "white",
};
if (timeObject.date) {
if (eventsByDate[timeObject.date]) {
eventsByDate[timeObject.date] = [
...eventsByDate[timeObject.date],
newEvent,
];
this.setState({ eventsByDate });
} else {
eventsByDate[timeObject.date] = [newEvent];
this.setState({ eventsByDate: { ...eventsByDate } });
}
}
};
approveNewEvent: TimelineProps["onBackgroundLongPressOut"] = (
_timeString,
timeObject
) => {
const { eventsByDate } = this.state;
Alert.prompt("New Event", "Enter event title", [
{
text: "Cancel",
onPress: () => {
if (timeObject.date) {
eventsByDate[timeObject.date] = filter(
eventsByDate[timeObject.date],
(e) => e.id !== "draft"
);
this.setState({
eventsByDate,
});
}
},
},
{
text: "Create",
onPress: (eventTitle) => {
if (timeObject.date) {
const draftEvent = find(eventsByDate[timeObject.date], {
id: "draft",
});
if (draftEvent) {
draftEvent.id = undefined;
draftEvent.title = eventTitle ?? "New Event";
draftEvent.color = "lightgreen";
eventsByDate[timeObject.date] = [
...eventsByDate[timeObject.date],
];
this.setState({
eventsByDate,
});
}
}
},
},
]);
};
private timelineProps: Partial<TimelineProps> = {
format24h: true,
onBackgroundLongPress: this.createNewEvent,
onBackgroundLongPressOut: this.approveNewEvent,
unavailableHours: [
{ start: 0, end: 6 },
{ start: 22, end: 24 },
],
overlapEventsSpacing: 8,
rightEdgeSpacing: 24,
};
render() {
const { currentDate, eventsByDate } = this.state;
return (
<CalendarProvider
date={currentDate}
showTodayButton
disabledOpacity={0.6}
>
<ExpandableCalendar
firstDay={1}
markedDates={this.marked}
initialPosition={Positions.OPEN} // ONLY ADDED THIS LINE
/>
<TimelineList
events={eventsByDate}
timelineProps={this.timelineProps}
showNowIndicator
scrollToFirst
initialTime={INITIAL_TIME}
/>
</CalendarProvider>
);
}
}
Please let me know if a fix or workaround is available for this regression.
Hello i have the same bug with version 1.1311.0, does anyone know how to fix it please?
+1
I fixed it by patching it like this
// wrap the getOpenHeight in a useCallback and use it as a dependency
const getOpenHeight = useCallback(() => {
if (!horizontal) {
return Math.max(constants.screenHeight, constants.screenWidth);
}
return headerHeight + (WEEK_HEIGHT * (numberOfWeeks.current)) + (hideKnob ? 0 : KNOB_CONTAINER_HEIGHT);
}, [headerHeight, horizontal, hideKnob, numberOfWeeks]);
// always calculate with getOpenHeight()
const startHeight = useMemo(() => isOpen ? getOpenHeight() : closedHeight, [isOpen, closedHeight, getOpenHeight]);
useEffect(() => {
_height.current = startHeight;
deltaY.setValue(startHeight);
// update wrapper height
_wrapperStyles.current.style.height = startHeight;
}, [startHeight]);
It seems to work pretty well
+1 @KimGnab's patch worked for me!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.