Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 10 additions & 1 deletion fixture/react-native/src/StickyHeaderExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const StickyHeaderExample = forwardRef(
const [withStickyHeaderOffset, setWithStickyHeaderOffset] = useState(false);
const [withStickyHeaderBackground, setWithStickyHeaderBackground] =
useState(false);
const [withHorizontalPadding, setWithHorizontalPadding] = useState(false);

// Memoize the renderItem function
const renderItem = useCallback(
Expand All @@ -86,7 +87,7 @@ export const StickyHeaderExample = forwardRef(
return (
<View
style={styles.container}
key={`${stickyHeadersEnabled}-${withStickyHeaderOffset}-${withStickyHeaderBackground}`}
key={`${stickyHeadersEnabled}-${withStickyHeaderOffset}-${withStickyHeaderBackground}-${withHorizontalPadding}`}
>
<View>
<Toggle
Expand All @@ -104,6 +105,11 @@ export const StickyHeaderExample = forwardRef(
value={withStickyHeaderBackground}
onChange={setWithStickyHeaderBackground}
/>
<Toggle
label="Horizontal Padding"
value={withHorizontalPadding}
onChange={setWithHorizontalPadding}
/>
</View>
<View style={styles.listContainer}>
<FlashList
Expand All @@ -115,6 +121,9 @@ export const StickyHeaderExample = forwardRef(
stickyHeadersEnabled ? headerIndices : undefined
}
stickyHeaderConfig={stickyHeaderConfig}
contentContainerStyle={
withHorizontalPadding ? { paddingHorizontal: 16 } : undefined
}
ItemSeparatorComponent={ItemSeparator}
/>
</View>
Expand Down
129 changes: 129 additions & 0 deletions src/__tests__/StickyHeaders.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,133 @@ describe("StickyHeaders - Compute Function", () => {
});
});
});

describe("Content Container Style Padding", () => {
it("should apply paddingHorizontal from contentContainerStyle", () => {
const layouts = createStandardLayouts();

const manager = createMockRecyclerViewManager({
scrollOffset: 100,
layouts,
});

const result = render(
<StickyHeaders
stickyHeaderIndices={[0, 10, 20]}
stickyHeaderOffset={0}
data={testData}
scrollY={new Animated.Value(0)}
renderItem={renderItem}
stickyHeaderRef={createRef()}
recyclerViewManager={manager}
extraData={undefined}
onChangeStickyIndex={jest.fn()}
contentContainerStyle={{ paddingHorizontal: 16 }}
/>
);

const animatedView = result.find(Animated.View);
expect(animatedView).toHaveReactProps({
style: expect.objectContaining({
paddingLeft: 16,
paddingRight: 16,
}),
});
});

it("should apply individual paddingLeft and paddingRight from contentContainerStyle", () => {
const layouts = createStandardLayouts();

const manager = createMockRecyclerViewManager({
scrollOffset: 100,
layouts,
});

const result = render(
<StickyHeaders
stickyHeaderIndices={[0, 10, 20]}
stickyHeaderOffset={0}
data={testData}
scrollY={new Animated.Value(0)}
renderItem={renderItem}
stickyHeaderRef={createRef()}
recyclerViewManager={manager}
extraData={undefined}
onChangeStickyIndex={jest.fn()}
contentContainerStyle={{ paddingLeft: 10, paddingRight: 20 }}
/>
);

const animatedView = result.find(Animated.View);
expect(animatedView).toHaveReactProps({
style: expect.objectContaining({
paddingLeft: 10,
paddingRight: 20,
}),
});
});

it("should apply padding shorthand from contentContainerStyle", () => {
const layouts = createStandardLayouts();

const manager = createMockRecyclerViewManager({
scrollOffset: 100,
layouts,
});

const result = render(
<StickyHeaders
stickyHeaderIndices={[0, 10, 20]}
stickyHeaderOffset={0}
data={testData}
scrollY={new Animated.Value(0)}
renderItem={renderItem}
stickyHeaderRef={createRef()}
recyclerViewManager={manager}
extraData={undefined}
onChangeStickyIndex={jest.fn()}
contentContainerStyle={{ padding: 24 }}
/>
);

const animatedView = result.find(Animated.View);
expect(animatedView).toHaveReactProps({
style: expect.objectContaining({
paddingLeft: 24,
paddingRight: 24,
}),
});
});

it("should not apply horizontal padding when contentContainerStyle is undefined", () => {
const layouts = createStandardLayouts();

const manager = createMockRecyclerViewManager({
scrollOffset: 100,
layouts,
});

const result = render(
<StickyHeaders
stickyHeaderIndices={[0, 10, 20]}
stickyHeaderOffset={0}
data={testData}
scrollY={new Animated.Value(0)}
renderItem={renderItem}
stickyHeaderRef={createRef()}
recyclerViewManager={manager}
extraData={undefined}
onChangeStickyIndex={jest.fn()}
/>
);

const animatedView = result.find(Animated.View);
expect(animatedView).toHaveReactProps({
style: expect.objectContaining({
paddingLeft: undefined,
paddingRight: undefined,
}),
});
});
});
});
5 changes: 5 additions & 0 deletions src/recyclerview/RecyclerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,9 @@ const RecyclerViewComponent = <T,>(
recyclerViewManager={recyclerViewManager}
extraData={extraData}
inverted={inverted}
contentContainerStyle={
overrideProps?.contentContainerStyle ?? rest.contentContainerStyle
}
onChangeStickyIndex={(newStickyHeaderIndex) => {
if (stickyHeaderHideRelatedCell) {
setCurrentStickyIndex(newStickyHeaderIndex);
Expand All @@ -464,6 +467,8 @@ const RecyclerViewComponent = <T,>(
onChangeStickyIndex,
stickyHeaderHideRelatedCell,
inverted,
rest.contentContainerStyle,
overrideProps?.contentContainerStyle,
]);

// Set up scroll event handling with animation support for sticky headers
Expand Down
30 changes: 29 additions & 1 deletion src/recyclerview/components/StickyHeaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import React, {
useCallback,
useEffect,
} from "react";
import { Animated, NativeScrollEvent } from "react-native";
import {
Animated,
NativeScrollEvent,
StyleProp,
StyleSheet,
ViewStyle,
} from "react-native";

import { FlashListProps } from "../..";
import { PlatformConfig } from "../../native/config/PlatformHelper";
Expand Down Expand Up @@ -46,6 +52,8 @@ export interface StickyHeaderProps<TItem> {
extraData: FlashListProps<TItem>["extraData"];
/** Whether the list is inverted */
inverted: FlashListProps<TItem>["inverted"];
/** Content container style from which horizontal padding is extracted */
contentContainerStyle?: StyleProp<ViewStyle>;
}

/**
Expand All @@ -72,6 +80,7 @@ export const StickyHeaders = <TItem,>({
extraData,
onChangeStickyIndex,
inverted,
contentContainerStyle,
}: StickyHeaderProps<TItem>) => {
const [stickyHeaderState, setStickyHeaderState] = useState<StickyHeaderState>(
{
Expand Down Expand Up @@ -192,6 +201,23 @@ export const StickyHeaders = <TItem,>({
stickyHeaderOffset,
]);

// Extract horizontal padding from contentContainerStyle
const horizontalPadding = useMemo(() => {
const flatStyle = StyleSheet.flatten(contentContainerStyle) ?? {};
return {
paddingLeft:
flatStyle.paddingLeft ??
flatStyle.paddingHorizontal ??
flatStyle.padding,
paddingRight:
flatStyle.paddingRight ??
flatStyle.paddingHorizontal ??
flatStyle.padding,
paddingStart: flatStyle.paddingStart,
paddingEnd: flatStyle.paddingEnd,
};
}, [contentContainerStyle]);

// Memoize header content
const headerContent = useMemo(() => {
return (
Expand All @@ -204,6 +230,7 @@ export const StickyHeaders = <TItem,>({
zIndex: 2,
transform: [{ translateY }],
opacity,
...horizontalPadding,
}}
>
{currentStickyIndex !== -1 && currentStickyIndex < data.length ? (
Expand Down Expand Up @@ -232,6 +259,7 @@ export const StickyHeaders = <TItem,>({
extraData,
stickyHeaderOffset,
inverted,
horizontalPadding,
]);

if (PlatformConfig.isRN083OrAbove && currentStickyIndex === -1) {
Expand Down