Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(PC-33463) refactor(Venue): refacto Venue page #7403

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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,188 changes: 5,585 additions & 5,603 deletions __snapshots__/features/venue/pages/Venue/Venue.native.test.tsx.native-snap

Large diffs are not rendered by default.

48 changes: 12 additions & 36 deletions src/features/venue/components/VenueBody/VenueBody.native.test.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
import mockdate from 'mockdate'
import React from 'react'
import { Linking } from 'react-native'
import Share, { Social } from 'react-native-share'
import { UseQueryResult } from 'react-query'

import { useRoute } from '__mocks__/@react-navigation/native'
import { gtlPlaylistAlgoliaSnapshot } from 'features/gtlPlaylist/fixtures/gtlPlaylistAlgoliaSnapshot'
import * as useGTLPlaylists from 'features/gtlPlaylist/hooks/useGTLPlaylists'
import * as useVenueOffers from 'features/venue/api/useVenueOffers'
import { useVenueOffers } from 'features/venue/api/useVenueOffers'
import { VenueBody } from 'features/venue/components/VenueBody/VenueBody'
import { venueDataTest } from 'features/venue/fixtures/venueDataTest'
import { VenueOffersResponseSnap } from 'features/venue/fixtures/venueOffersResponseSnap'
import { VenueOffers } from 'features/venue/types'
import { analytics } from 'libs/analytics'
import * as useFeatureFlag from 'libs/firebase/firestore/featureFlags/useFeatureFlag'
import { Network } from 'libs/share/types'
import { reactQueryProviderHOC } from 'tests/reactQueryProviderHOC'
import { fireEvent, render, screen } from 'tests/utils'

jest.spyOn(useFeatureFlag, 'useFeatureFlag').mockReturnValue(false)

mockdate.set(new Date('2021-08-15T00:00:00Z'))

jest.mock('features/venue/api/useVenue')
jest.mock('@react-native-clipboard/clipboard')
const mockShareSingle = jest.spyOn(Share, 'shareSingle')
const canOpenURLSpy = jest.spyOn(Linking, 'canOpenURL').mockResolvedValue(false)
jest
.spyOn(useGTLPlaylists, 'useGTLPlaylists')
.mockReturnValue({ isLoading: false, gtlPlaylists: gtlPlaylistAlgoliaSnapshot })
jest.spyOn(useVenueOffers, 'useVenueOffers').mockReturnValue({

jest.mock('features/venue/api/useVenueOffers')
const mockUseVenueOffers = useVenueOffers as jest.Mock
mockUseVenueOffers.mockReturnValue({
isLoading: false,
data: { hits: VenueOffersResponseSnap, nbHits: 10 },
} as unknown as UseQueryResult<VenueOffers, unknown>)

})
jest.mock('libs/location')

jest.mock('libs/subcategories/useSubcategories')
Expand All @@ -50,35 +45,23 @@ describe('<VenueBody />', () => {
canOpenURLSpy.mockResolvedValueOnce(true)
})

it('should display withdrawal details', async () => {
it('should display expected tabs', async () => {
render(reactQueryProviderHOC(<VenueBody venue={venueDataTest} />))
await waitUntilRendered()

fireEvent.press(screen.getByText('Infos pratiques'))

expect(screen.getByText('How to withdraw, https://test.com')).toBeOnTheScreen()
expect(await screen.findByText('Offres disponibles')).toBeOnTheScreen()
expect(await screen.findByText('Infos pratiques')).toBeOnTheScreen()
})

it('should share on Instagram', async () => {
it('should display withdrawal details', async () => {
render(reactQueryProviderHOC(<VenueBody venue={venueDataTest} />))

const instagramButton = await screen.findByText(`Envoyer sur ${Network.instagram}`)

fireEvent.press(instagramButton)
fireEvent.press(screen.getByText('Infos pratiques'))

expect(mockShareSingle).toHaveBeenCalledWith({
social: Social.Instagram,
message: encodeURIComponent(
`Retrouve "${venueDataTest.name}" sur le pass Culture\u00a0:\nhttps://webapp-v2.example.com/lieu/5543?utm_gen=product&utm_campaign=share_venue&utm_medium=social_media&utm_source=Instagram`
),
type: 'text',
url: undefined,
})
expect(screen.getByText('How to withdraw, https://test.com')).toBeOnTheScreen()
})

it('should log event when pressing on Infos pratiques tab', async () => {
render(reactQueryProviderHOC(<VenueBody venue={venueDataTest} />))
await waitUntilRendered()

fireEvent.press(screen.getByText('Infos pratiques'))

Expand All @@ -89,16 +72,9 @@ describe('<VenueBody />', () => {

it('should log event when pressing on Offres disponibles tab', async () => {
render(reactQueryProviderHOC(<VenueBody venue={venueDataTest} />))
await waitUntilRendered()

fireEvent.press(screen.getByText('Offres disponibles'))

expect(analytics.logConsultVenueOffers).toHaveBeenCalledWith({ venueId: venueDataTest.id })
})
})

const waitUntilRendered = async () => {
// We wait until the full render is done
// This is due to asynchronous calls to check the media on the phone
await screen.findByText(`Envoyer sur ${Network.instagram}`)
}
57 changes: 18 additions & 39 deletions src/features/venue/components/VenueBody/VenueBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,29 @@ import { VenueResponse } from 'api/gen'
import { GtlPlaylistData } from 'features/gtlPlaylist/types'
import { PracticalInformation } from 'features/venue/components/PracticalInformation/PracticalInformation'
import { TabLayout } from 'features/venue/components/TabLayout/TabLayout'
import { VENUE_CTA_HEIGHT_IN_SPACES } from 'features/venue/components/VenueCTA/VenueCTA'
import { VenueMessagingApps } from 'features/venue/components/VenueMessagingApps/VenueMessagingApps'
import { VenueOffers } from 'features/venue/components/VenueOffers/VenueOffers'
import { VenueThematicSection } from 'features/venue/components/VenueThematicSection/VenueThematicSection'
import type { VenueOffersArtists, VenueOffers as VenueOffersType } from 'features/venue/types'
import { Tab } from 'features/venue/types'
import { analytics } from 'libs/analytics'
import { SectionWithDivider } from 'ui/components/SectionWithDivider'
import { Spacer } from 'ui/theme'

interface Props {
venue: VenueResponse
venueArtists?: VenueOffersArtists
venueOffers?: VenueOffersType
playlists?: GtlPlaylistData[]
shouldDisplayCTA?: boolean
}

export const VenueBody: FunctionComponent<Props> = ({
venue,
venueArtists,
venueOffers,
playlists,
shouldDisplayCTA,
}) => {
const { isDesktopViewport, isTabletViewport } = useTheme()
const isLargeScreen = isDesktopViewport || isTabletViewport

const FirstSectionContainer = isLargeScreen ? View : SectionWithDivider
const SectionContainer = isLargeScreen ? View : SectionWithDivider

const tabPanels = {
[Tab.OFFERS]: (
Expand All @@ -49,37 +43,22 @@ export const VenueBody: FunctionComponent<Props> = ({
}

return (
<React.Fragment>
<FirstSectionContainer visible gap={6}>
<TabLayout
tabPanels={tabPanels}
tabs={[{ key: Tab.OFFERS }, { key: Tab.INFOS }]}
defaultTab={Tab.OFFERS}
onTabChange={{
'Offres disponibles': () =>
analytics.logConsultVenueOffers({
venueId: venue.id,
}),
'Infos pratiques': () =>
analytics.logConsultPracticalInformations({
venueId: venue.id,
}),
}}
/>
</FirstSectionContainer>

<Spacer.Column numberOfSpaces={6} />

<VenueThematicSection venue={venue} />

<SectionWithDivider visible margin gap={6}>
<VenueMessagingApps venue={venue} />
<Spacer.Column numberOfSpaces={4} />
</SectionWithDivider>

<SectionWithDivider visible={!!shouldDisplayCTA} gap={VENUE_CTA_HEIGHT_IN_SPACES}>
<Spacer.Column numberOfSpaces={6} />
</SectionWithDivider>
</React.Fragment>
<SectionContainer visible gap={6}>
<TabLayout
tabPanels={tabPanels}
tabs={[{ key: Tab.OFFERS }, { key: Tab.INFOS }]}
defaultTab={Tab.OFFERS}
onTabChange={{
'Offres disponibles': () =>
analytics.logConsultVenueOffers({
venueId: venue.id,
}),
'Infos pratiques': () =>
analytics.logConsultPracticalInformations({
venueId: venue.id,
}),
}}
/>
</SectionContainer>
)
}
40 changes: 23 additions & 17 deletions src/features/venue/components/VenueCTA/VenueCTA.native.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { push } from '__mocks__/@react-navigation/native'
import { SearchState } from 'features/search/types'
import { VenueCTA } from 'features/venue/components/VenueCTA/VenueCTA'
import { venueDataTest } from 'features/venue/fixtures/venueDataTest'
import * as useNavigateToSearchWithVenueOffers from 'features/venue/helpers/useNavigateToSearchWithVenueOffers'
import { SearchNavConfig } from 'features/venue/types'
import { analytics } from 'libs/analytics'
import { LocationMode } from 'libs/location/types'
import { fireEvent, render, screen, waitFor } from 'tests/utils'
import { render, screen, userEvent, waitFor } from 'tests/utils'

const defaultParams: SearchState = {
beginningDatetime: undefined,
Expand All @@ -34,27 +34,28 @@ const defaultParams: SearchState = {
},
} as SearchState

jest
.spyOn(useNavigateToSearchWithVenueOffers, 'useNavigateToSearchWithVenueOffers')
.mockReturnValue({
screen: 'TabNavigator',
const searchNavConfigMock: SearchNavConfig = {
screen: 'TabNavigator',
params: {
screen: 'SearchStackNavigator',
params: {
screen: 'SearchStackNavigator',
params: {
screen: 'SearchResults',
params: defaultParams,
},
screen: 'SearchResults',
params: defaultParams,
},
withPush: true,
})
},
withPush: true,
}

jest.mock('libs/firebase/analytics/analytics')

jest.useFakeTimers()
const user = userEvent.setup()

describe('<VenueCTA />', () => {
it('should navigate to the search page when pressed on', async () => {
render(<VenueCTA venue={venueDataTest} />)
render(<VenueCTA searchNavConfig={searchNavConfigMock} onBeforeNavigate={jest.fn()} />)

fireEvent.press(screen.getByText('Rechercher une offre'))
await user.press(await screen.findByText('Rechercher une offre'))

await waitFor(() => {
expect(push).toHaveBeenCalledWith('TabNavigator', {
Expand All @@ -76,9 +77,14 @@ describe('<VenueCTA />', () => {
})

it('should log event when pressed on', async () => {
render(<VenueCTA venue={venueDataTest} />)
render(
<VenueCTA
searchNavConfig={searchNavConfigMock}
onBeforeNavigate={() => analytics.logVenueSeeAllOffersClicked(venueDataTest.id)}
/>
)

fireEvent.press(screen.getByText('Rechercher une offre'))
await user.press(await screen.findByText('Rechercher une offre'))

expect(analytics.logVenueSeeAllOffersClicked).toHaveBeenCalledWith(venueDataTest.id)
})
Expand Down
12 changes: 5 additions & 7 deletions src/features/venue/components/VenueCTA/VenueCTA.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { FunctionComponent } from 'react'
import styled from 'styled-components/native'

import { VenueResponse } from 'api/gen'
import { useNavigateToSearchWithVenueOffers } from 'features/venue/helpers/useNavigateToSearchWithVenueOffers'
import { analytics } from 'libs/analytics'
import { SearchNavConfig } from 'features/venue/types'
import { ButtonPrimary } from 'ui/components/buttons/ButtonPrimary'
import { StickyBottomWrapper } from 'ui/components/StickyBottomWrapper/StickyBottomWrapper'
import { InternalTouchableLink } from 'ui/components/touchableLink/InternalTouchableLink'
Expand All @@ -13,18 +11,18 @@ import { Spacer } from 'ui/theme'
export const VENUE_CTA_HEIGHT_IN_SPACES = 6 + 10 + 6

interface Props {
venue: VenueResponse
searchNavConfig: SearchNavConfig
onBeforeNavigate: () => void
}

export const VenueCTA: FunctionComponent<Props> = ({ venue }) => {
const searchNavConfig = useNavigateToSearchWithVenueOffers(venue)
export const VenueCTA: FunctionComponent<Props> = ({ searchNavConfig, onBeforeNavigate }) => {
return (
<StickyBottomWrapper>
<CallToActionContainer>
<Spacer.Column numberOfSpaces={6} />
<InternalTouchableLink
navigateTo={searchNavConfig}
onBeforeNavigate={() => analytics.logVenueSeeAllOffersClicked(venue.id)}
onBeforeNavigate={onBeforeNavigate}
as={ButtonPrimary}
wording="Rechercher une offre"
icon={SmallMagnifyingGlass}
Expand Down
Loading
Loading