fix: Transparent ExpandableCalendar
The issue related to dynamic headerHeight was not set.
- Introduced a ref to conditionally measure header height only when necessary.
- Updated onHeaderLayout to prevent unnecessary state updates.
- Adjusted onLayout prop for Animated.View to utilize the new measurement logic.
Related issues: #2625 #2693 #2681 #2670 #2657 #2571 #2722
Hi @freely322 , Is there a ETA for this? This is really causing a issues even while using a loading flag from redux.
@nitzanyiz @Inbal-Tish @ethanshar Take a look please
I upgraded to "react-native-calendars": "1.1313.0" today and applied this PR to fix the invisible ExpandableCalendar. While everything was working fine on iOS, I had a styling issue on Android because of a header height that was too small.
Right now I just did a "dirty" fix by adding 5px to the height when using android…
const shouldMeasureHeader = useRef(true);
const onHeaderLayout = useCallback(
({
nativeEvent: {
layout: { height },
},
}) => {
const _height = Platform.OS === "android" ? height + 5 : height;
if (_height !== headerHeight) {
setHeaderHeight(_height || DEFAULT_HEADER_HEIGHT);
}
shouldMeasureHeader.current = false;
},
[headerHeight]
);
I upgraded to
"react-native-calendars": "1.1313.0"today and applied this PR to fix the invisible ExpandableCalendar. While everything was working fine on iOS, I had a styling issue on Android because of a header height that was too small.Right now I just did a "dirty" fix by adding 5px to the height when using android…
const shouldMeasureHeader = useRef(true); const onHeaderLayout = useCallback( ({ nativeEvent: { layout: { height }, }, }) => { const _height = Platform.OS === "android" ? height + 5 : height; if (_height !== headerHeight) { setHeaderHeight(_height || DEFAULT_HEADER_HEIGHT); } shouldMeasureHeader.current = false; }, [headerHeight] );![]()
thanks for testing, I will check soon
Here's a patch for this fix.
diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.js b/node_modules/react-native-calendars/src/expandableCalendar/index.js
index bea00e0..bf92441 100644
--- a/node_modules/react-native-calendars/src/expandableCalendar/index.js
+++ b/node_modules/react-native-calendars/src/expandableCalendar/index.js
@@ -61,9 +61,11 @@ const ExpandableCalendar = forwardRef((props, ref) => {
horizontal = true, calendarStyle, theme, style: propsStyle, firstDay = 0, onDayPress, hideArrows, onPressArrowLeft, onPressArrowRight, renderArrow, testID, ...others } = props;
const [screenReaderEnabled, setScreenReaderEnabled] = useState(false);
const [headerHeight, setHeaderHeight] = useState(0);
+ const shouldMeasureHeader = useRef(true);
const onHeaderLayout = useCallback(({ nativeEvent: { layout: { height } } }) => {
- setHeaderHeight(height || DEFAULT_HEADER_HEIGHT);
- }, []);
+ if (height !== headerHeight) { setHeaderHeight(height || DEFAULT_HEADER_HEIGHT); }
+ shouldMeasureHeader.current = false;
+ }, [headerHeight]);
/** Date */
const getYear = (date) => {
const d = new XDate(date);
@@ -411,7 +413,7 @@ const ExpandableCalendar = forwardRef((props, ref) => {
return (<CalendarList testID={`${testID}.calendarList`} horizontal={horizontal} firstDay={firstDay} calendarStyle={calendarStyle} onHeaderLayout={onHeaderLayout} {...others} current={date} theme={themeObject} ref={calendarList} onDayPress={_onDayPress} onVisibleMonthsChange={onVisibleMonthsChange} pagingEnabled scrollEnabled={isOpen} hideArrows={shouldHideArrows} onPressArrowLeft={_onPressArrowLeft} onPressArrowRight={_onPressArrowRight} hideExtraDays={!horizontal && isOpen} renderArrow={_renderArrow} staticHeader numberOfDays={numberOfDays} headerStyle={_headerStyle} timelineLeftInset={timelineLeftInset} context={_context}/>);
};
return (<View testID={testID} style={containerStyle}>
- {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} {...panResponder.panHandlers}>
+ {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} onLayout={shouldMeasureHeader.current ? onHeaderLayout : undefined} {...panResponder.panHandlers}>
{renderCalendarList()}
{renderWeekCalendar()}
{!hideKnob && renderKnob()}
Wow, this is a painful bug that wasted a bunch of time... When will this be merged in? I manually patched.
Here's a patch for this fix.
diff --git a/node_modules/react-native-calendars/src/expandableCalendar/index.js b/node_modules/react-native-calendars/src/expandableCalendar/index.js index bea00e0..bf92441 100644 --- a/node_modules/react-native-calendars/src/expandableCalendar/index.js +++ b/node_modules/react-native-calendars/src/expandableCalendar/index.js @@ -61,9 +61,11 @@ const ExpandableCalendar = forwardRef((props, ref) => { horizontal = true, calendarStyle, theme, style: propsStyle, firstDay = 0, onDayPress, hideArrows, onPressArrowLeft, onPressArrowRight, renderArrow, testID, ...others } = props; const [screenReaderEnabled, setScreenReaderEnabled] = useState(false); const [headerHeight, setHeaderHeight] = useState(0); + const shouldMeasureHeader = useRef(true); const onHeaderLayout = useCallback(({ nativeEvent: { layout: { height } } }) => { - setHeaderHeight(height || DEFAULT_HEADER_HEIGHT); - }, []); + if (height !== headerHeight) { setHeaderHeight(height || DEFAULT_HEADER_HEIGHT); } + shouldMeasureHeader.current = false; + }, [headerHeight]); /** Date */ const getYear = (date) => { const d = new XDate(date); @@ -411,7 +413,7 @@ const ExpandableCalendar = forwardRef((props, ref) => { return (<CalendarList testID={`${testID}.calendarList`} horizontal={horizontal} firstDay={firstDay} calendarStyle={calendarStyle} onHeaderLayout={onHeaderLayout} {...others} current={date} theme={themeObject} ref={calendarList} onDayPress={_onDayPress} onVisibleMonthsChange={onVisibleMonthsChange} pagingEnabled scrollEnabled={isOpen} hideArrows={shouldHideArrows} onPressArrowLeft={_onPressArrowLeft} onPressArrowRight={_onPressArrowRight} hideExtraDays={!horizontal && isOpen} renderArrow={_renderArrow} staticHeader numberOfDays={numberOfDays} headerStyle={_headerStyle} timelineLeftInset={timelineLeftInset} context={_context}/>); }; return (<View testID={testID} style={containerStyle}> - {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} {...panResponder.panHandlers}> + {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} onLayout={shouldMeasureHeader.current ? onHeaderLayout : undefined} {...panResponder.panHandlers}> {renderCalendarList()} {renderWeekCalendar()} {!hideKnob && renderKnob()}
+1 the above fixed for me... only thing that I had to change was that I always had to add height + 5 for both android and iOS, else it would look like how @carlbleick mentioned in this comment. For him it was fine on iOS, but for me it was hiding the days slightly a bit for both OS.
So, pretty much what @ajp8164 had but with +5... so looks like this
@@ -0,0 +1,28 @@
diff --git a/src/expandableCalendar/index.js b/src/expandableCalendar/index.js
index bea00e0168a338eb19d3eee2ddd7ee8d0470b745..404ae259b00571b78deb000509d2f5d09816d29c 100644
--- a/src/expandableCalendar/index.js
+++ b/src/expandableCalendar/index.js
@@ -61,9 +61,12 @@ const ExpandableCalendar = forwardRef((props, ref) => {
horizontal = true, calendarStyle, theme, style: propsStyle, firstDay = 0, onDayPress, hideArrows, onPressArrowLeft, onPressArrowRight, renderArrow, testID, ...others } = props;
const [screenReaderEnabled, setScreenReaderEnabled] = useState(false);
const [headerHeight, setHeaderHeight] = useState(0);
+ const shouldMeasureHeader = useRef(true);
const onHeaderLayout = useCallback(({ nativeEvent: { layout: { height } } }) => {
- setHeaderHeight(height || DEFAULT_HEADER_HEIGHT);
- }, []);
+ const _height = (height || DEFAULT_HEADER_HEIGHT) + 5;
+ if (height !== headerHeight) { setHeaderHeight(_height); }
+ shouldMeasureHeader.current = false;
+ }, [headerHeight]);
/** Date */
const getYear = (date) => {
const d = new XDate(date);
@@ -411,7 +414,7 @@ const ExpandableCalendar = forwardRef((props, ref) => {
return (<CalendarList testID={`${testID}.calendarList`} horizontal={horizontal} firstDay={firstDay} calendarStyle={calendarStyle} onHeaderLayout={onHeaderLayout} {...others} current={date} theme={themeObject} ref={calendarList} onDayPress={_onDayPress} onVisibleMonthsChange={onVisibleMonthsChange} pagingEnabled scrollEnabled={isOpen} hideArrows={shouldHideArrows} onPressArrowLeft={_onPressArrowLeft} onPressArrowRight={_onPressArrowRight} hideExtraDays={!horizontal && isOpen} renderArrow={_renderArrow} staticHeader numberOfDays={numberOfDays} headerStyle={_headerStyle} timelineLeftInset={timelineLeftInset} context={_context}/>);
};
return (<View testID={testID} style={containerStyle}>
- {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} {...panResponder.panHandlers}>
+ {screenReaderEnabled ? (<Calendar testID={`${testID}.calendarAccessible`} {...others} theme={themeObject} onHeaderLayout={onHeaderLayout} onDayPress={_onDayPress} hideExtraDays renderArrow={_renderArrow}/>) : (<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} onLayout={shouldMeasureHeader.current ? onHeaderLayout : undefined} {...panResponder.panHandlers}>
{renderCalendarList()}
{renderWeekCalendar()}
{!hideKnob && renderKnob()}
Will also fix #2722
@Inbal-Tish @nitzanyiz - please have a look at this PR 🙏🏽
The fix is not ideal. I render no header renderHeader={() => null} and it will show two rows .
I am using this hack to force a re-render so it can properly measure the row height.
const [forceRenderKey, setForceRenderKey] = useState(0)
// Force re-render to work around ExpandableCalendar headerHeight === 0 bug
useEffect(() => {
const timer = setTimeout(() => {
setForceRenderKey(1)
}, 10)
return () => clearTimeout(timer)
}, [])
<ExpandableCalendar
key={forceRenderKey}
testID="expandableCalendar"
@ethanshar @Inbal-Tish @nitzanyiz can you take a look at this?
also having this issue, @ethanshar @Inbal-Tish @nitzanyiz will anyone take a look at this?
I'm migrating to expo v54 + react native v0.81 and this issue is back even after applying the patch https://github.com/wix/react-native-calendars/pull/2653#issuecomment-3175741492
Did anyone managed to solve this issue? If so, how?
Not me, I had to roll back to v1.1289.0
I’ve found that using nativewind 4.2.0 together with tailwind 3.4.17 works fine in my project — not sure if that’ll work in your case though.