Skip to content

Commit

Permalink
fix: Search Results expand bug and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ccallendar committed Aug 2, 2024
1 parent 496070f commit 0f6d8c2
Show file tree
Hide file tree
Showing 17 changed files with 247 additions and 144 deletions.
141 changes: 112 additions & 29 deletions frontend/src/features/omrr/omrr-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@ import { useLocation } from 'react-router-dom'
import rfdc from 'rfdc'

import { RootState } from '@/app/store'
import { MIN_SEARCH_LENGTH } from '@/constants/constants'
import { MIN_CIRCLE_RADIUS, MIN_SEARCH_LENGTH } from '@/constants/constants'
import { LoadingStatusType } from '@/interfaces/loading-status'
import OmrrData from '@/interfaces/omrr'
import {
facilityTypeFilters,
OmrrFilter,
CircleFilter,
PolygonFilter,
} from '@/interfaces/omrr-filter'
import { facilityTypeFilters, OmrrFilter } from '@/interfaces/omrr-filter'
import { SEARCH_BY_ACTIVE } from '@/interfaces/types'
import OmrrResponse from '@/interfaces/omrr-response'
import apiService from '@/service/api-service'
Expand Down Expand Up @@ -52,10 +47,13 @@ export interface OmrrSliceState {
searchByFilteredResults: OmrrData[]
filteredResults: OmrrData[]
searchTextFilter: string
// The timestamp when the user last performed a search or filter
// The timestamp when the user last performed a search or filter, this is
// used to expand the search results & clear selected item when it changes
lastSearchTime?: number
polygonFilter?: PolygonFilter
circleFilter?: CircleFilter
pointFilterCenter?: LatLngTuple
pointFilterRadius: number
polygonFilterPositions: LatLngTuple[]
polygonFilterFinished: boolean
}

export const initialState: OmrrSliceState = {
Expand All @@ -78,6 +76,12 @@ export const initialState: OmrrSliceState = {
filteredResults: [],
// global search text value
searchTextFilter: '',
// The point filter is active when the center point is defined
pointFilterCenter: undefined,
pointFilterRadius: MIN_CIRCLE_RADIUS,
// The polygon filter is active when finished is true
polygonFilterPositions: [],
polygonFilterFinished: false,
}

function performSearch(state: OmrrSliceState, setSearchTime = true) {
Expand Down Expand Up @@ -143,26 +147,89 @@ export const omrrSlice = createSlice({
state.page = action.payload
},
setSearchTextFilter: (state, action: PayloadAction<string>) => {
if (state.searchTextFilter !== action.payload) {
state.searchTextFilter = action.payload
const oldText = state.searchTextFilter
const newText = action.payload
state.searchTextFilter = newText
// Only perform the search when the search text changes AND
// when there are now 3+ characters or when there was 3+ characters
// This prevents doing the search extra times when only 0-2 characters
const hasSearchChanged =
oldText !== newText &&
(newText.length >= MIN_SEARCH_LENGTH ||
oldText.length >= MIN_SEARCH_LENGTH)
if (hasSearchChanged) {
performSearch(state)
}
},
setPolygonFilter: (state, action: PayloadAction<PolygonFilter>) => {
state.polygonFilter = action.payload
state.circleFilter = undefined
performSearch(state)
addPolygonFilterPosition: (state, action: PayloadAction<LatLngTuple>) => {
// Adds a new polygon position, but don't perform search
state.polygonFilterPositions = [
...state.polygonFilterPositions,
action.payload,
]
},
deleteLastPolygonFilterPosition: (state) => {
// Removes the last polygon position
if (state.polygonFilterPositions.length > 0) {
const newPositions = [...state.polygonFilterPositions]
newPositions.pop()
state.polygonFilterPositions = newPositions
}
},
resetPolygonFilter: (state) => {
// Resets the polygon filter, also performs the search again
state.polygonFilterPositions = []
if (state.polygonFilterFinished) {
state.polygonFilterFinished = false
performSearch(state, false)
}
},
setPolygonFilterFinished: (state) => {
// Marks the polygon filter as finished, and performs the search
if (!state.polygonFilterFinished) {
state.polygonFilterFinished = true
// Reset point filter
state.pointFilterCenter = undefined
state.pointFilterRadius = MIN_CIRCLE_RADIUS
performSearch(state)
}
},
setCircleFilter: (state, action: PayloadAction<CircleFilter>) => {
state.circleFilter = action.payload
state.polygonFilter = undefined
setPointFilterCenter: (state, action: PayloadAction<LatLngTuple>) => {
state.pointFilterCenter = action.payload
// Reset polygon filter
state.polygonFilterPositions = []
state.polygonFilterFinished = false
performSearch(state)
},
setPointFilterRadius: (state, action: PayloadAction<number>) => {
const newRadius = action.payload
if (newRadius !== state.pointFilterRadius) {
state.pointFilterRadius = newRadius
// Only perform the search when there is a center point
if (state.pointFilterCenter) {
// Reset polygon filter
state.polygonFilterPositions = []
state.polygonFilterFinished = false
performSearch(state)
}
}
},
resetPointFilter: (state) => {
// Reset point filter and search if necessary
state.pointFilterRadius = MIN_CIRCLE_RADIUS
if (state.pointFilterCenter) {
state.pointFilterCenter = undefined
performSearch(state, false)
}
},
clearShapeFilters: (state) => {
if (state.polygonFilter || state.circleFilter) {
state.polygonFilter = undefined
state.circleFilter = undefined
performSearch(state)
state.polygonFilterPositions = []
state.pointFilterRadius = MIN_CIRCLE_RADIUS
// Only perform search if either of these were set
if (state.polygonFilterFinished || state.pointFilterCenter) {
state.polygonFilterFinished = false
state.pointFilterCenter = undefined
performSearch(state, false)
}
},
},
Expand Down Expand Up @@ -209,8 +276,13 @@ export const {
setUserLocation,
setPage,
setSearchTextFilter,
setPolygonFilter,
setCircleFilter,
addPolygonFilterPosition,
deleteLastPolygonFilterPosition,
resetPolygonFilter,
setPolygonFilterFinished,
setPointFilterCenter,
setPointFilterRadius,
resetPointFilter,
clearShapeFilters,
} = omrrSlice.actions

Expand Down Expand Up @@ -269,7 +341,18 @@ export const useFindByAuthorizationNumber = (
const selectLastSearchTime = (state: RootState) => state.omrr.lastSearchTime
export const useLastSearchTime = () => useSelector(selectLastSearchTime)

const selectPolygonFilter = (state: RootState) => state.omrr.polygonFilter
export const usePolygonFilter = () => useSelector(selectPolygonFilter)
const selectCircleFilter = (state: RootState) => state.omrr.circleFilter
export const useCircleFilter = () => useSelector(selectCircleFilter)
const selectPolygonFilterPositions = (state: RootState) =>
state.omrr.polygonFilterPositions
const selectPolygonFilterFinished = (state: RootState) =>
state.omrr.polygonFilterFinished
export const usePolygonFilterPositions = () =>
useSelector(selectPolygonFilterPositions)
export const usePolygonFilterFinished = () =>
useSelector(selectPolygonFilterFinished)

const selectPointFilterCenter = (state: RootState) =>
state.omrr.pointFilterCenter
const selectPointFilterRadius = (state: RootState) =>
state.omrr.pointFilterRadius
export const usePointFilterCenter = () => useSelector(selectPointFilterCenter)
export const usePointFilterRadius = () => useSelector(selectPointFilterRadius)
9 changes: 5 additions & 4 deletions frontend/src/features/omrr/omrr-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,12 @@ export function filterData(state: OmrrSliceState): OmrrData[] {
// Now apply global text search
filteredData = filterByTextSearch(filteredData, searchTextFilter)
// Apply polygon or point filters
if (state.polygonFilter?.finished) {
const { positions } = state.polygonFilter
if (state.polygonFilterFinished) {
const positions = state.polygonFilterPositions
filteredData = filterDataInsidePolygon(filteredData, positions)
} else if (state.circleFilter?.center) {
const { center, radius } = state.circleFilter
} else if (state.pointFilterCenter) {
const center = state.pointFilterCenter
const radius = state.pointFilterRadius
filteredData = filterDataInsideCircle(filteredData, center, radius)
}
// Sort data by closest to user's location
Expand Down
11 changes: 0 additions & 11 deletions frontend/src/interfaces/omrr-filter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import OmrrData from './omrr'
import { LatLngTuple } from 'leaflet'

export interface OmrrFilter {
value: string
Expand Down Expand Up @@ -65,13 +64,3 @@ export const facilityTypeFilters: OmrrFilter[] = [
on: false,
},
]

export interface CircleFilter {
center?: LatLngTuple
radius: number
}

export interface PolygonFilter {
positions: LatLngTuple[]
finished?: boolean
}
2 changes: 1 addition & 1 deletion frontend/src/pages/map/drawer/MapBottomDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ export function MapBottomDrawer() {
setFullHeight(false)
// Clear active tool and filters too
if (activeTool) {
dispatch(clearShapeFilters())
dispatch(clearActiveTool())
}
dispatch(clearShapeFilters())
}

const swipeCallback = (direction: SwipeDirection) => {
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/pages/map/hooks/useBottomDrawerState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { useEffect, useRef, useState } from 'react'
import { useDispatch } from 'react-redux'

import {
useCircleFilter,
useLastSearchTime,
usePolygonFilter,
usePointFilterCenter,
usePolygonFilterFinished,
} from '@/features/omrr/omrr-slice'
import {
setBottomDrawerHeight,
Expand Down Expand Up @@ -37,8 +37,8 @@ export function useBottomDrawerState() {
const activeTool = useActiveTool()
const activeToolRef = useRef<ActiveToolEnum | undefined>(undefined)
const heightRef = useRef<number>(0)
const polygonFilter = usePolygonFilter()
const circleFilter = useCircleFilter()
const polygonFilterFinished = usePolygonFilterFinished()
const pointFilterReady = Boolean(usePointFilterCenter())

const isDataLayersVisible = activeTool === ActiveToolEnum.dataLayers
const isSearchByVisible = activeTool === ActiveToolEnum.searchBy
Expand All @@ -49,7 +49,7 @@ export function useBottomDrawerState() {
// Show search results whenever there isn't an active tool
// or when polygon/point search are finished/ready
const isSearchResultsVisible =
!activeTool || polygonFilter?.finished || Boolean(circleFilter?.center)
!activeTool || polygonFilterFinished || pointFilterReady

// Expand bottom drawer when the active tool changes
useEffect(() => {
Expand Down Expand Up @@ -80,8 +80,8 @@ export function useBottomDrawerState() {
height = MAP_BOTTOM_DRAWER_HEIGHT
// Use small height when polygon/point filters are not finished or search by is active
if (
(isPolygonSearchVisible && !polygonFilter?.finished) ||
(isPointSearchVisible && !circleFilter?.center) ||
(isPolygonSearchVisible && !polygonFilterFinished) ||
(isPointSearchVisible && !pointFilterReady) ||
isSearchByVisible
) {
height = MAP_BOTTOM_DRAWER_HEIGHT_SMALL
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/pages/map/hooks/useMapCrosshairsCursor.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { useEffect } from 'react'
import L from 'leaflet'

export function useMapCrosshairsCursor(map: L.Map) {
export function useMapCrosshairsCursor(map: L.Map, enabled = true) {
// Show crosshairs cursor
useEffect(() => {
// @ts-ignore
const container = map._container
if (container) {
L.DomUtil.addClass(container, 'crosshairs-cursor')
if (enabled) {
L.DomUtil.addClass(container, 'crosshairs-cursor')
}
return () => {
L.DomUtil.removeClass(container, 'crosshairs-cursor')
}
}
}, [map])
}, [map, enabled])

return null
}
8 changes: 7 additions & 1 deletion frontend/src/pages/map/hooks/useSidebarState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,21 @@ export function useSidebarState() {
const selectedItem = useSelectedItem()
const selectedItemTime = useSelectedItemTime()
const lastSearchTime = useLastSearchTime()
const lastSearchTimeRef = useRef<number | undefined>(lastSearchTime)
const initialExpandRef = useRef<boolean>(false)
const widthRef = useRef<number>(0)

// Only expand the sidebar once after the first search is performed
useEffect(() => {
if (lastSearchTime && !initialExpandRef.current) {
if (
lastSearchTime &&
lastSearchTime !== lastSearchTimeRef.current &&
!initialExpandRef.current
) {
initialExpandRef.current = true
setExpanded(true)
}
lastSearchTimeRef.current = lastSearchTime
}, [lastSearchTime])

// Always expand when there is a selected item
Expand Down
24 changes: 3 additions & 21 deletions frontend/src/pages/map/layers/AuthorizationMarkers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import OmrrData from '@/interfaces/omrr'
import { TestMapContainer } from './TestMapContainer'
import { AuthorizationMarkers } from './AuthorizationMarkers'
import { ActiveToolEnum, MIN_CIRCLE_RADIUS } from '@/constants/constants'
import { ActiveToolEnum } from '@/constants/constants'

interface State {
selectedItem?: OmrrData
Expand Down Expand Up @@ -107,10 +107,8 @@ describe('Test suite for AuthorizationMarkers', () => {
it('should render AuthorizationMarkers in polygon search mode with positions', async () => {
const { user, state } = renderComponent(
{
polygonFilter: {
positions: [[48.123, -123.123]],
finished: false,
},
polygonFilterPositions: [[48.123, -123.123]],
polygonFilterFinished: false,
},
{ activeTool: ActiveToolEnum.polygonSearch },
)
Expand All @@ -134,20 +132,4 @@ describe('Test suite for AuthorizationMarkers', () => {

expect(state.selectedItem).toBeUndefined()
})

it('should render AuthorizationMarkers in point search mode with radius', async () => {
const { user, state } = renderComponent(
{
circleFilter: { radius: MIN_CIRCLE_RADIUS },
},
{ activeTool: ActiveToolEnum.pointSearch },
500,
)

const marker = screen.getAllByAltText('Authorization marker')[0]
expect(marker).toBeDefined()
await user.click(marker)

expect(state.selectedItem).toBeUndefined()
})
})
Loading

0 comments on commit 0f6d8c2

Please sign in to comment.