Skip to content

Commit

Permalink
(PC-33638) feat(chronicle): add anchor on chronicle when see more but…
Browse files Browse the repository at this point in the history
…ton used
  • Loading branch information
clesausse-pass committed Feb 3, 2025
1 parent 10a1b8e commit fdb05b5
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export const ChronicleCardList: FunctionComponent<ChronicleCardListProps> = ({
separatorSize = SEPARATOR_DEFAULT_VALUE,
onScroll,
style,
offerId,
shouldShowSeeMoreButton,
selectedChronicle,
}) => {
const { isDesktopViewport } = useTheme()
const { width: windowWidth } = useWindowDimensions()
Expand Down Expand Up @@ -86,6 +89,9 @@ export const ChronicleCardList: FunctionComponent<ChronicleCardListProps> = ({
separatorSize={separatorSize}
contentContainerStyle={contentContainerStyle}
snapToInterval={isDesktopViewport ? CHRONICLE_CARD_WIDTH : undefined}
offerId={offerId}
shouldShowSeeMoreButton={shouldShowSeeMoreButton}
selectedChronicle={selectedChronicle}
/>
</View>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,16 @@ describe('ChronicleCardListBase', () => {

expect(screen.getByText('La Nature Sauvage')).toBeOnTheScreen()
})

it('should scroll to the selected chronicle when defined', () => {
render(
<ChronicleCardListBase
data={chroniclesSnap}
selectedChronicle={chroniclesSnap[4]}
ref={ref}
/>
)

expect(screen.getByText('La Magie des Étoiles')).toBeOnTheScreen()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ import React, {
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react'
import { FlatList, FlatListProps, StyleProp, ViewStyle } from 'react-native'
import styled from 'styled-components/native'
import {
FlatList,
FlatListProps,
LayoutChangeEvent,
StyleProp,
View,
ViewStyle,
} from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import styled, { useTheme } from 'styled-components/native'

import { ChronicleCardData } from 'features/chronicle/type'
import { getSpacing } from 'ui/theme'
Expand All @@ -32,9 +41,22 @@ export type ChronicleCardListProps = Pick<
separatorSize?: number
headerComponent?: ReactElement
style?: StyleProp<ViewStyle>
shouldShowSeeMoreButton?: boolean
offerId?: number
selectedChronicle?: ChronicleCardData
}

const renderItem = ({ item, cardWidth }: { item: ChronicleCardData; cardWidth?: number }) => {
const renderItem = ({
item,
cardWidth,
shouldShowSeeMoreButton,
offerId,
}: {
item: ChronicleCardData
cardWidth?: number
shouldShowSeeMoreButton?: boolean
offerId?: number
}) => {
return (
<ChronicleCard
id={item.id}
Expand All @@ -43,6 +65,8 @@ const renderItem = ({ item, cardWidth }: { item: ChronicleCardData; cardWidth?:
description={item.description}
date={item.date}
cardWidth={cardWidth}
navigateTo={{ screen: 'Chronicles', params: { offerId, chronicleId: item.id } }}
shouldShowSeeMoreButton={shouldShowSeeMoreButton}
/>
)
}
Expand All @@ -63,13 +87,31 @@ export const ChronicleCardListBase = forwardRef<
onContentSizeChange,
style,
separatorSize = SEPARATOR_DEFAULT_VALUE,
shouldShowSeeMoreButton,
offerId,
selectedChronicle,
},
ref
) {
const listRef = useRef<FlatList>(null)
const [isFlatListReady, setIsFlatListReady] = useState(false)
const [headerHeight, setHeaderHeight] = useState(0)
const [listHeight, setListHeight] = useState(1)
const { top } = useSafeAreaInsets()
const { appBarHeight } = useTheme()

const handleHeaderLayout = (event: LayoutChangeEvent) => {
setHeaderHeight(event.nativeEvent.layout.height)
}

const handleListLayout = (event: LayoutChangeEvent) => {
setListHeight(event.nativeEvent.layout.height)
setIsFlatListReady(true)
}

useImperativeHandle(ref, () => ({
scrollToOffset: (params) => listRef.current?.scrollToOffset(params),
scrollToItem: (params) => listRef.current?.scrollToItem(params),
}))

useEffect(() => {
Expand All @@ -78,6 +120,19 @@ export const ChronicleCardListBase = forwardRef<
}
}, [offset])

useEffect(() => {
if (listRef.current && isFlatListReady && !!selectedChronicle) {
const flatListTop = top + appBarHeight + headerHeight
const viewPosition = flatListTop / listHeight

listRef.current.scrollToItem({
item: selectedChronicle,
animated: true,
viewPosition,
})
}
}, [appBarHeight, headerHeight, selectedChronicle, isFlatListReady, listHeight, top])

const Separator = useMemo(
() =>
styled.View({
Expand All @@ -92,8 +147,10 @@ export const ChronicleCardListBase = forwardRef<
ref={listRef}
data={data}
style={style}
ListHeaderComponent={headerComponent}
renderItem={({ item }) => renderItem({ item, cardWidth })}
ListHeaderComponent={
headerComponent ? <View onLayout={handleHeaderLayout}>{headerComponent}</View> : null
}
renderItem={({ item }) => renderItem({ item, cardWidth, shouldShowSeeMoreButton, offerId })}
keyExtractor={keyExtractor}
ItemSeparatorComponent={Separator}
contentContainerStyle={contentContainerStyle}
Expand All @@ -105,6 +162,7 @@ export const ChronicleCardListBase = forwardRef<
decelerationRate="fast"
snapToInterval={snapToInterval}
testID="chronicle-list"
onLayout={handleListLayout}
/>
)
})
41 changes: 20 additions & 21 deletions src/features/chronicle/pages/Chronicles/Chronicles.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useRoute } from '@react-navigation/native'
import React, { FunctionComponent, useRef } from 'react'
import { ScrollView } from 'react-native'
import { FlatList } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import styled, { useTheme } from 'styled-components/native'

Expand All @@ -19,6 +19,7 @@ import { TypoDS, getSpacing } from 'ui/theme'
export const Chronicles: FunctionComponent = () => {
const route = useRoute<UseRouteType<'Chronicles'>>()
const offerId = route.params?.offerId
const chronicleId = route.params?.chronicleId
const { goBack } = useGoBack('Offer', { id: offerId })
const { data: offer } = useOffer({ offerId })
const { data: chronicleCardsData } = useChronicles<ChronicleCardData[]>({
Expand All @@ -29,9 +30,12 @@ export const Chronicles: FunctionComponent = () => {
const { headerTransition, onScroll } = useOpacityTransition()
const { appBarHeight } = useTheme()
const { top } = useSafeAreaInsets()
const { contentPage } = useTheme()
const headerHeight = appBarHeight + top

const scrollViewRef = useRef<ScrollView>(null)
const chroniclesListRef = useRef<FlatList<ChronicleCardData>>(null)

const selectedChronicle = chronicleCardsData?.find((item) => item.id === chronicleId)

if (!offer || !chronicleCardsData) return null

Expand All @@ -41,30 +45,25 @@ export const Chronicles: FunctionComponent = () => {
<React.Fragment>
<ChroniclesWebMetaHeader title={title} />
<ChroniclesHeader headerTransition={headerTransition} title={title} handleGoBack={goBack} />
<ScrollView
scrollEventThrottle={16}
bounces={false}
ref={scrollViewRef}
<ChronicleCardList
data={chronicleCardsData}
horizontal={false}
separatorSize={6}
headerComponent={<StyledTitle2>Tous les avis</StyledTitle2>}
ref={chroniclesListRef}
onScroll={onScroll}
contentContainerStyle={{ paddingTop: headerHeight, paddingBottom: getSpacing(10) }}>
<ChroniclesContainer>
<ChronicleCardList
data={chronicleCardsData}
horizontal={false}
separatorSize={6}
headerComponent={<StyledTitle2>Tous les avis</StyledTitle2>}
/>
</ChroniclesContainer>
</ScrollView>
contentContainerStyle={{
paddingTop: headerHeight,
paddingBottom: getSpacing(10),
marginTop: getSpacing(4),
marginHorizontal: contentPage.marginHorizontal,
}}
selectedChronicle={selectedChronicle}
/>
</React.Fragment>
)
}

const StyledTitle2 = styled(TypoDS.Title2)({
marginBottom: getSpacing(6),
})

const ChroniclesContainer = styled.View(({ theme }) => ({
marginTop: getSpacing(4),
marginHorizontal: theme.contentPage.marginHorizontal,
}))
10 changes: 9 additions & 1 deletion src/features/chronicle/pages/Chronicles/Chronicles.web.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useRoute } from '@react-navigation/native'
import React, { FunctionComponent } from 'react'
import React, { FunctionComponent, useRef } from 'react'
import { FlatList } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import styled, { useTheme } from 'styled-components/native'

Expand Down Expand Up @@ -28,6 +29,7 @@ import { TypoDS, getSpacing } from 'ui/theme'
export const Chronicles: FunctionComponent = () => {
const route = useRoute<UseRouteType<'Chronicles'>>()
const offerId = route.params?.offerId
const chronicleId = route.params?.chronicleId
const { goBack } = useGoBack('Offer', { id: offerId })
const { data: offer } = useOffer({ offerId })
const subcategoriesMapping = useSubcategoriesMapping()
Expand All @@ -51,6 +53,8 @@ export const Chronicles: FunctionComponent = () => {
const currency = useGetCurrencyToDisplay()
const euroToPacificFrancRate = useGetPacificFrancToEuroRate()

const chroniclesListRef = useRef<FlatList<ChronicleCardData>>(null)

const displayedPrice = getDisplayedPrice(
prices,
currency,
Expand All @@ -59,16 +63,20 @@ export const Chronicles: FunctionComponent = () => {
{ fractionDigits: 2 }
)

const selectedChronicle = chronicleCardsData?.find((item) => item.id === chronicleId)

if (!offer || !chronicleCardsData) return null

const title = `Tous les avis sur "${offer.name}"`

const listComponent = (
<StyledChronicleCardList
ref={chroniclesListRef}
data={chronicleCardsData}
onScroll={onScroll}
paddingTop={headerHeight}
headerComponent={<StyledTitle2>Tous les avis</StyledTitle2>}
selectedChronicle={selectedChronicle}
/>
)

Expand Down

0 comments on commit fdb05b5

Please sign in to comment.