Skip to content

Commit

Permalink
1258: Refactor pois and bottom sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
steffenkleinle committed Feb 12, 2025
1 parent 87ca7b9 commit 52d196d
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 214 deletions.
70 changes: 0 additions & 70 deletions native/src/components/BottomSheet.tsx

This file was deleted.

28 changes: 2 additions & 26 deletions native/src/components/BottomSheetHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import React, { ReactElement } from 'react'
import { View } from 'react-native'
import styled from 'styled-components/native'

const SheetHeader = styled.View`
justify-content: center;
flex-direction: row;
margin-bottom: 20px;
`
const Title = styled.Text`
color: ${props => props.theme.colors.textColor};
font-family: ${props => props.theme.fonts.native.decorativeFontBold};
font-size: 18px;
`
const Indicator = styled.View`
const Handle = styled.View`
width: 34px;
border: 1px solid ${props => props.theme.colors.textSecondaryColor};
background-color: ${props => props.theme.colors.textSecondaryColor};
Expand All @@ -21,19 +10,6 @@ const Indicator = styled.View`
margin: 20px 0;
`

type BottomSheetHandleProps = {
title?: string
}

const BottomSheetHandle = ({ title }: BottomSheetHandleProps): ReactElement => (
<View>
<Indicator />
{!!title && (
<SheetHeader>
<Title>{title}</Title>
</SheetHeader>
)}
</View>
)
const BottomSheetHandle = (): ReactElement => <Handle />

export default BottomSheetHandle
2 changes: 1 addition & 1 deletion native/src/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { ReactElement } from 'react'
import { FlatList, RefreshControl, ViewStyle } from 'react-native'
import styled from 'styled-components/native'

const NoItemsMessage = styled.Text`
export const NoItemsMessage = styled.Text`
color: ${props => props.theme.colors.textColor};
font-family: ${props => props.theme.fonts.native.contentFontRegular};
align-self: center;
Expand Down
9 changes: 3 additions & 6 deletions native/src/components/MapView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ type MapViewProps = {
selectedFeature: MapFeature | null
userLocation: LocationType | null
setUserLocation: (userLocation: LocationType | null) => void
iconPosition: string | number
selectFeature: (feature: MapFeature | null) => void
setSheetSnapPointIndex: (index: number) => void
bottomSheetHeight: number
bottomSheetFullscreen: boolean
zoom: number | undefined
Overlay?: ReactElement
}
Expand All @@ -78,13 +77,12 @@ const MapView = ({
boundingBox,
features,
selectedFeature,
iconPosition,
userLocation,
setUserLocation,
selectFeature,
setSheetSnapPointIndex,
Overlay,
bottomSheetHeight,
bottomSheetFullscreen,
zoom,
}: MapViewProps): ReactElement => {
const cameraRef = useRef<MapLibreGL.Camera>(null)
Expand Down Expand Up @@ -158,7 +156,6 @@ const MapView = ({

const feature = featureCollection?.features.find((it): it is MapFeature => it.geometry.type === 'Point')
selectFeature(feature ?? null)
setSheetSnapPointIndex(1)

zoomOnClusterPress(pressedCoordinates)
}
Expand Down Expand Up @@ -202,7 +199,7 @@ const MapView = ({
<StyledIcon
icon={<Icon Icon={locationPermissionIcon} />}
onPress={onRequestLocation}
position={iconPosition}
position={bottomSheetFullscreen ? 0 : bottomSheetHeight}
accessibilityLabel={t('showOwnLocation')}
/>
</MapContainer>
Expand Down
2 changes: 1 addition & 1 deletion native/src/components/PoiListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const StyledPressable = styled(Pressable)<{ language: string }>`
border-bottom-width: 1px;
border-bottom-color: ${props => props.theme.colors.textDisabledColor};
flex-direction: ${props => contentDirection(props.language)};
padding: 24px 0;
padding: 16px 0;
`

const Description = styled.View`
Expand Down
117 changes: 117 additions & 0 deletions native/src/components/PoisBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import BottomSheet, {
BottomSheetFlatList,
BottomSheetFlatListMethods,
BottomSheetScrollView,
} from '@gorhom/bottom-sheet'
import React, { memo, ReactElement, Ref } from 'react'
import { useTranslation } from 'react-i18next'
import { Platform } from 'react-native'
import styled from 'styled-components/native'

import { LocationType } from 'shared'
import { ErrorCode, PoiModel } from 'shared/api'

import useCityAppContext from '../hooks/useCityAppContext'
import BottomSheetHandle from './BottomSheetHandle'
import Failure from './Failure'
import { NoItemsMessage } from './List'
import PoiDetails from './PoiDetails'
import PoiListItem from './PoiListItem'

const StyledBottomSheet = styled(BottomSheet)<{ isFullscreen: boolean }>`
${props => props.isFullscreen && `background-color: ${props.theme.colors.backgroundColor};`}
`

const BottomSheetContent = styled.View`
flex: 1;
margin: 0 24px;
`

const Title = styled.Text`
color: ${props => props.theme.colors.textColor};
font-family: ${props => props.theme.fonts.native.decorativeFontBold};
font-size: 18px;
font-weight: bold;
`

type PoiBottomSheetProps = {
poiListRef: Ref<BottomSheetFlatListMethods>
pois: PoiModel[]
poi: PoiModel | undefined
userLocation: LocationType | null
slug: string | undefined
selectPoi: (poi: PoiModel) => void
deselectAll: () => void
snapPoints: number[]
snapPointIndex: number
setSnapPointIndex: (index: number) => void
setScrollPosition: (position: number) => void
isFullscreen: boolean
}

const PoisBottomSheet = ({
poiListRef,
pois,
poi,
userLocation,
slug,
selectPoi,
deselectAll,
snapPoints,
snapPointIndex,
setSnapPointIndex,
setScrollPosition,
isFullscreen,
}: PoiBottomSheetProps): ReactElement | null => {
const { languageCode } = useCityAppContext()
const { t } = useTranslation('pois')
// ios has scrolling issues if content panning gesture is not enabled
const enableContentPanningGesture = Platform.OS === 'ios' || !isFullscreen

const PoiDetail = poi ? (
<PoiDetails language={languageCode} poi={poi} distance={userLocation && poi.distance(userLocation)} />
) : (
<Failure code={ErrorCode.PageNotFound} buttonAction={deselectAll} buttonLabel={t('detailsHeader')} />
)

const renderPoiListItem = ({ item: poi }: { item: PoiModel }): ReactElement => (
<PoiListItem
key={poi.path}
poi={poi}
language={languageCode}
navigateToPoi={() => selectPoi(poi)}
distance={userLocation && poi.distance(userLocation)}
/>
)

return (
<StyledBottomSheet
index={snapPointIndex}
isFullscreen={isFullscreen}
snapPoints={snapPoints}
enableContentPanningGesture={enableContentPanningGesture}
enableDynamicSizing={false}
animateOnMount
handleComponent={BottomSheetHandle}
onChange={setSnapPointIndex}>
<BottomSheetContent>
{slug ? (
<BottomSheetScrollView showsVerticalScrollIndicator={false}>{PoiDetail}</BottomSheetScrollView>
) : (
<BottomSheetFlatList
ref={poiListRef}
data={pois}
role='list'
renderItem={renderPoiListItem}
onMomentumScrollBegin={event => setScrollPosition(event.nativeEvent.contentOffset.y)}
showsVerticalScrollIndicator={false}
ListHeaderComponent={<Title>{t('listTitle')}</Title>}
ListEmptyComponent={<NoItemsMessage>{t('noPois')}</NoItemsMessage>}
/>
)}
</BottomSheetContent>
</StyledBottomSheet>
)
}

export default memo(PoisBottomSheet)
82 changes: 82 additions & 0 deletions native/src/components/__tests__/PoisBottomSheet.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { fireEvent } from '@testing-library/react-native'
import React from 'react'

import { PoiModelBuilder } from 'shared/api'

import TestingAppContext from '../../testing/TestingAppContext'
import renderWithTheme from '../../testing/render'
import PoisBottomSheet from '../PoisBottomSheet'

jest.mock('../../components/Page')
jest.mock('@react-native-clipboard/clipboard', () => () => ({ setString: jest.fn() }))
jest.mock('react-i18next')
jest.mock('styled-components')
jest.mock('@gorhom/bottom-sheet', () => ({
__esModule: true,
...require('@gorhom/bottom-sheet/mock'),
}))

describe('PoisBottomSheet', () => {
const pois = new PoiModelBuilder(3).build()
const poi0 = pois[0]!
const poi1 = pois[1]!
const poi2 = pois[2]!
const deselectAll = jest.fn()
const selectPoi = jest.fn()

const renderPois = ({ slug = undefined }: { slug?: string; multipoi?: number; poiCategoryId?: number }) =>
renderWithTheme(
<TestingAppContext>
<PoisBottomSheet
slug={slug}
poi={pois.find(it => it.slug === slug)}
pois={pois}
snapPoints={[]}
snapPointIndex={0}
userLocation={null}
poiListRef={jest.fn()}
setScrollPosition={jest.fn()}
setSnapPointIndex={jest.fn()}
deselectAll={deselectAll}
selectPoi={selectPoi}
isFullscreen={false}
/>
</TestingAppContext>,
)

it('should show failure if poi is not found', () => {
const { queryByText, getByText } = renderPois({ slug: 'invalid' })

expect(getByText('pageNotFound')).toBeTruthy()
expect(queryByText(poi0.title)).toBeFalsy()
expect(queryByText(poi1.title)).toBeFalsy()
expect(queryByText(poi2.title)).toBeFalsy()

fireEvent.press(getByText('detailsHeader'))

expect(deselectAll).toHaveBeenCalledTimes(1)
})

it('should show list', () => {
const { getByText } = renderPois({})

expect(getByText(poi0.title)).toBeTruthy()
expect(getByText(poi1.title)).toBeTruthy()
expect(getByText(poi2.title)).toBeTruthy()

fireEvent.press(getByText(poi1.title))
expect(selectPoi).toHaveBeenCalledTimes(1)
expect(selectPoi).toHaveBeenCalledWith(poi1)
})

it('should show poi', () => {
const { getByText, queryByText } = renderPois({ slug: poi2.slug })

expect(getByText(poi2.title)).toBeTruthy()
expect(getByText(poi2.category.name)).toBeTruthy()
expect(getByText(poi2.content)).toBeTruthy()
expect(getByText(poi2.category.name)).toBeTruthy()
expect(queryByText(poi0.title)).toBeFalsy()
expect(queryByText(poi1.title)).toBeFalsy()
})
})
4 changes: 2 additions & 2 deletions native/src/constants/dimensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type DimensionsType = {
*/
fontScaling: number
headerTextSize: number
bottomSheetHandler: {
bottomSheetHandle: {
height: number
}
locationFab: {
Expand All @@ -29,7 +29,7 @@ const dimensions: DimensionsType = {
},
fontScaling: 0.04,
headerTextSize: 20,
bottomSheetHandler: {
bottomSheetHandle: {
height: 40,
},
locationFab: {
Expand Down
Loading

0 comments on commit 52d196d

Please sign in to comment.