Skip to content

Commit

Permalink
(PC-31867) feat(Offer): Create new XP cine component and add FF (#6938)
Browse files Browse the repository at this point in the history
* [CLEAN]: Remove unused component and condition to declutter code

* [REFACTO]: Rethink existing styling logic to ease future component integration

* [FIX]: remove empty `other offers` block

* [DEV]: Create new components + use FF + fix styling

* [TESTS]: Add test and revert unwanted change

* [FIX]: styling cleanup

* [DEV-REVIEW]: Inverse condition
  • Loading branch information
inesmouandjolobe authored Oct 2, 2024
1 parent d1d5cc4 commit bede96a
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 132 deletions.
4 changes: 2 additions & 2 deletions src/features/offer/components/MovieCalendar/MovieCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const MovieCalendar: React.FC<Props> = ({
ref={flatListRef}
data={dates}
horizontal
contentContainerStyle={flatListContainer}
contentContainerStyle={contentContainerStyle}
showsHorizontalScrollIndicator={false}
onScroll={onScroll}
onContentSizeChange={onContentSizeChange}
Expand Down Expand Up @@ -114,7 +114,7 @@ export const MovieCalendar: React.FC<Props> = ({
)
}

const flatListContainer = {
const contentContainerStyle = {
paddingHorizontal: MOVIE_CALENDAR_PADDING,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ export const MovieScreeningCalendar: FunctionComponent<Props> = ({ offer, subcat
flatListRef={flatListRef}
/>
<Spacer.Column numberOfSpaces={4} />
{eventCardData === undefined ? null : <EventCardList data={eventCardData} />}
{eventCardData ? (
<EventCardListContainer>
<EventCardList data={eventCardData} />
</EventCardListContainer>
) : null}
{CTAOfferModal}
</MovieCalendarContainer>
)
Expand All @@ -67,3 +71,7 @@ export const MovieScreeningCalendar: FunctionComponent<Props> = ({ offer, subcat
const MovieCalendarContainer = styled(View)(({ theme }) => ({
marginRight: theme.isDesktopViewport ? -getSpacing(16) : 0, // cancels padding of the parent container
}))

const EventCardListContainer = styled(View)(({ theme }) => ({
marginHorizontal: theme.contentPage.marginHorizontal,
}))
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { FC, useCallback, useMemo } from 'react'
import { FlatList } from 'react-native'
import { FlatList, View } from 'react-native'
import styled from 'styled-components/native'

import type { OfferPreviewResponse } from 'api/gen'
Expand All @@ -18,7 +18,7 @@ import { useSubcategoriesMapping } from 'libs/subcategories'
import { useScrollToAnchor } from 'ui/components/anchor/AnchorContext'
import { EventCardList } from 'ui/components/eventCard/EventCardList'
import { HorizontalOfferTile } from 'ui/components/tiles/HorizontalOfferTile'
import { getSpacing, Spacer } from 'ui/theme'
import { Spacer } from 'ui/theme'

type MovieOfferTileProps = {
movieOffer: MovieOffer
Expand Down Expand Up @@ -88,7 +88,7 @@ export const MovieOfferTile: FC<MovieOfferTileProps> = ({
)
return (
<React.Fragment>
<ContainerWithMargin>
<View>
{offerScreeningOnSelectedDates ? (
<HorizontalOfferTile
offer={offerScreeningOnSelectedDates}
Expand All @@ -100,10 +100,10 @@ export const MovieOfferTile: FC<MovieOfferTileProps> = ({
withRightArrow
/>
) : null}
</ContainerWithMargin>
</View>
<Spacer.Column numberOfSpaces={4} />
{nextScreeningDate ? (
<ContainerWithMargin>
<View>
<NextScreeningButton
date={nextScreeningDate}
onPress={() => {
Expand All @@ -112,7 +112,7 @@ export const MovieOfferTile: FC<MovieOfferTileProps> = ({
scrollToMiddleElement(nextDateIndex)
}}
/>
</ContainerWithMargin>
</View>
) : (
<EventCardList
data={eventCardData}
Expand All @@ -138,9 +138,4 @@ const getSubtitles = (offer: OfferPreviewResponse): string[] => {
const Divider = styled.View(({ theme }) => ({
height: 1,
backgroundColor: theme.colors.greyMedium,
marginHorizontal: getSpacing(6),
}))

const ContainerWithMargin = styled.View({
marginHorizontal: getSpacing(6),
})
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRoute } from '@react-navigation/native'
import { isSameDay, startOfDay } from 'date-fns'
import React, { FunctionComponent, useMemo, useRef, useState, useCallback, useEffect } from 'react'
import { FlatList, Animated, Easing } from 'react-native'
import { FlatList, Animated, Easing, View } from 'react-native'
import styled from 'styled-components/native'

import { SubcategoryIdEnum } from 'api/gen'
Expand Down Expand Up @@ -172,33 +172,35 @@ export const MoviesScreeningCalendar: FunctionComponent<Props> = ({ venueOffers
/>
</Anchor>
<Spacer.Column numberOfSpaces={4} />
<Animated.View
onLayout={({ nativeEvent }) => {
setWidth(nativeEvent.layout.width)
}}
style={{
opacity: fadeAnim,
transform: [
{ translateX: Animated.subtract(Animated.multiply(translateAnim, width), width) },
],
}}>
{moviesOffers.map((movie, index) => (
<MovieOfferTile
key={movie.offer.id}
movieOffer={movie}
venueOffers={venueOffers}
date={selectedDate}
isLast={getIsLast(index)}
setSelectedDate={setSelectedDate}
nextScreeningDate={movie.nextDate}
nextDateIndex={movie.nextDate ? getNextDateIndex(movie.nextDate) : 0}
flatListRef={flatListRef}
flatListWidth={flatListWidth}
itemWidth={itemWidth}
/>
))}
</Animated.View>
{nonScreeningOffers ? (
<Container>
<Animated.View
onLayout={({ nativeEvent }) => {
setWidth(nativeEvent.layout.width)
}}
style={{
opacity: fadeAnim,
transform: [
{ translateX: Animated.subtract(Animated.multiply(translateAnim, width), width) },
],
}}>
{moviesOffers.map((movie, index) => (
<MovieOfferTile
key={movie.offer.id}
movieOffer={movie}
venueOffers={venueOffers}
date={selectedDate}
isLast={getIsLast(index)}
setSelectedDate={setSelectedDate}
nextScreeningDate={movie.nextDate}
nextDateIndex={movie.nextDate ? getNextDateIndex(movie.nextDate) : 0}
flatListRef={flatListRef}
flatListWidth={flatListWidth}
itemWidth={itemWidth}
/>
))}
</Animated.View>
</Container>
{nonScreeningOffers.length ? (
<SectionWithDivider visible margin={false} gap={6}>
<PassPlaylist
testID="offersModuleList"
Expand All @@ -216,4 +218,8 @@ export const MoviesScreeningCalendar: FunctionComponent<Props> = ({ venueOffers
)
}

const Container = styled(View)(({ theme }) => ({
marginHorizontal: theme.contentPage.marginHorizontal,
}))

const PlaylistTitleText = styled(Typo.Title3).attrs(getHeadingAttrs(2))``
38 changes: 38 additions & 0 deletions src/features/offer/components/OfferNewXPCine/CineBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { FunctionComponent } from 'react'
import { View } from 'react-native'
import styled from 'styled-components/native'

import { OfferResponseV2 } from 'api/gen'
import { VenueBlock } from 'features/offer/components/OfferVenueBlock/VenueBlock'
import { EventCardProps } from 'ui/components/eventCard/EventCard'
import { EventCardList } from 'ui/components/eventCard/EventCardList'
import { Spacer } from 'ui/theme'

type Props = {
offer: OfferResponseV2
distance?: string
onSeeVenuePress?: VoidFunction
CTAOfferModal?: React.ReactElement<unknown, string | React.JSXElementConstructor<unknown>> | null
eventCardData: EventCardProps[]
}
export const CineBlock: FunctionComponent<Props> = ({
offer,
distance,
onSeeVenuePress,
CTAOfferModal,
eventCardData,
}) => {
return (
<CineBlockContainer>
<Spacer.Column numberOfSpaces={6} />
<VenueBlock distance={distance} offer={offer} onSeeVenuePress={onSeeVenuePress} />
<Spacer.Column numberOfSpaces={4} />
{eventCardData ? <EventCardList data={eventCardData} /> : null}
{CTAOfferModal}
</CineBlockContainer>
)
}

const CineBlockContainer = styled(View)(({ theme }) => ({
marginHorizontal: theme.contentPage.marginHorizontal,
}))
109 changes: 109 additions & 0 deletions src/features/offer/components/OfferNewXPCine/OfferNewXPCineBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useEffect, useMemo, useRef } from 'react'
import { FlatList, View } from 'react-native'
import styled, { useTheme } from 'styled-components/native'

import { OfferResponseV2 } from 'api/gen'
import { MovieCalendar } from 'features/offer/components/MovieCalendar/MovieCalendar'
import { useMovieScreeningCalendar } from 'features/offer/components/MovieScreeningCalendar/useMovieScreeningCalendar'
import { useSelectedDateScreening } from 'features/offer/components/MovieScreeningCalendar/useSelectedDateScreenings'
import { useOfferCTAButton } from 'features/offer/components/OfferCTAButton/useOfferCTAButton'
import { CineBlock } from 'features/offer/components/OfferNewXPCine/CineBlock'
import { Subcategory } from 'libs/subcategories/types'
import { getSpacing, Spacer, TypoDS } from 'ui/theme'
import { getHeadingAttrs } from 'ui/theme/typographyAttrs/getHeadingAttrs'

type Props = {
title: string
distance?: string
offer: OfferResponseV2
subcategory: Subcategory
onSeeVenuePress?: VoidFunction
}

export function OfferNewXPCineBlock({
title,
distance,
onSeeVenuePress,
offer,
subcategory,
}: Readonly<Props>) {
const theme = useTheme()
const { stocks, isExternalBookingsDisabled } = offer
const offerVenueId = offer.venue.id

const { movieScreeningDates, selectedDate, setSelectedDate, selectedScreeningStock } =
useMovieScreeningCalendar(stocks)

const { bookingData, selectedDateScreenings } = useSelectedDateScreening(
selectedScreeningStock,
isExternalBookingsDisabled
)

const {
onPress: onPressOfferCTA,
CTAOfferModal,
movieScreeningUserData,
} = useOfferCTAButton(offer, subcategory, bookingData)

const flatListRef = useRef<FlatList | null>(null)

const eventCardData = useMemo(
() => selectedDateScreenings(offerVenueId, onPressOfferCTA, movieScreeningUserData),
[offerVenueId, onPressOfferCTA, selectedDateScreenings, movieScreeningUserData]
)

useEffect(() => {
if (flatListRef?.current) {
setSelectedDate(movieScreeningDates[0])
flatListRef.current?.scrollToOffset({ offset: 0 })
}
}, [flatListRef, movieScreeningDates, setSelectedDate])

return (
<Container testID="offer-new-xp-cine-block">
<TitleContainer>
<TypoDS.Title3 {...getHeadingAttrs(2)}>{title}</TypoDS.Title3>
</TitleContainer>

<Spacer.Column numberOfSpaces={4} />

<MovieCalendarContainer>
<MovieCalendar
dates={movieScreeningDates}
selectedDate={selectedDate}
onTabChange={setSelectedDate}
flatListRef={flatListRef}
/>
</MovieCalendarContainer>

<View>
<CineBlock
offer={offer}
distance={distance}
onSeeVenuePress={onSeeVenuePress}
CTAOfferModal={CTAOfferModal}
eventCardData={eventCardData}
/>
<Spacer.Column numberOfSpaces={theme.isDesktopViewport ? 6 : 4} />
<Divider />
</View>
</Container>
)
}

const Container = styled(View)({
marginVertical: 0,
})

const MovieCalendarContainer = styled(View)(({ theme }) => ({
marginRight: theme.isDesktopViewport ? -getSpacing(16) : 0, // cancels padding of the parent container
}))

const TitleContainer = styled(View)(({ theme }) => ({
marginHorizontal: theme.isDesktopViewport ? undefined : theme.contentPage.marginHorizontal,
}))

const Divider = styled.View(({ theme }) => ({
height: 1,
backgroundColor: theme.colors.greyMedium,
}))
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,7 @@ jest.mock('api/useSearchVenuesOffer/useSearchVenueOffers', () => ({
useSearchVenueOffers: () => mockUseSearchVenueOffers(),
}))

jest
.spyOn(useFeatureFlag, 'useFeatureFlag')
// this value corresponds to WIP_ENABLE_MULTIVENUE_OFFER feature flag
.mockReturnValue(true)
const useFeatureFlagSpy = jest.spyOn(useFeatureFlag, 'useFeatureFlag')

const offerPlaceProps: OfferPlaceProps = {
offer: mockOffer,
Expand Down Expand Up @@ -117,6 +114,7 @@ describe('<OfferPlace />', () => {
mockDistance = null
mockdate.set(new Date('2021-01-01'))
mockUseSearchVenueOffers.mockReturnValue(searchVenueOfferWithVenues)
useFeatureFlagSpy.mockReturnValue(false) // this value corresponds to TARGET_XP_CINE_FROM_OFFER feature flag
})

it('should display change venue button when offer subcategory is "Livres audio", offer has an EAN and that there are other venues offering the same offer', () => {
Expand All @@ -132,6 +130,20 @@ describe('<OfferPlace />', () => {
expect(screen.getByText('Changer le lieu de retrait')).toBeOnTheScreen()
})

it('should display new xp cine block when offer subcategory is "Seance cine" and FF is on', () => {
useFeatureFlagSpy.mockReturnValueOnce(true) // this value corresponds to TARGET_XP_CINE_FROM_OFFER feature flag
renderOfferPlace({
...offerPlaceProps,
offer: {
...mockOffer,
subcategoryId: SubcategoryIdEnum.SEANCE_CINE,
extraData: { allocineId: 2765410054 },
},
})

expect(screen.getByTestId('offer-new-xp-cine-block')).toBeOnTheScreen()
})

it('should display change venue button when offer subcategory is "Seance cine", offer has an allocineId and that there are other venues offering the same offer', () => {
renderOfferPlace({
...offerPlaceProps,
Expand Down
Loading

0 comments on commit bede96a

Please sign in to comment.