Skip to content

Commit

Permalink
feat(SearchResultsByCategory): Group all vehicle results into a singl…
Browse files Browse the repository at this point in the history
…e category (#2168)

* feat(SearchPageState): Add categoryResultLimits field

This will replace the properties field

* feat(SearchResultsByCategory): Group all vehicle results into a single category

* feat: Update default search result limit

* style(searchPageState.test): Remove blank line

* Update assets/tests/components/mapPage/searchResultsByCategory.test.tsx

Co-authored-by: Kayla Firestack <firestack@users.noreply.github.com>

---------

Co-authored-by: Kayla Firestack <firestack@users.noreply.github.com>
  • Loading branch information
KaylaBrady and firestack authored Aug 4, 2023
1 parent f177d8f commit 6a91ed3
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 304 deletions.
4 changes: 2 additions & 2 deletions assets/src/components/mapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { VisualSeparator } from "./visualSeparator"
import OldSearchForm from "./oldSearchForm"
import inTestGroup, { TestGroups } from "../userInTestGroup"
import { Socket } from "phoenix"
import SearchResultsByProperty from "./mapPage/searchResultsByProperty"
import SearchResultsByCategory from "./mapPage/searchResultsByCategory"
import { LocationSearchResult } from "../models/locationSearchResult"

const thereIsAnActiveSearch = (
Expand Down Expand Up @@ -98,7 +98,7 @@ const SearchMode = ({
<div className="c-search-display u-hideable">
{searchPageState.isActive ? (
inTestGroup(TestGroups.LocationSearch) ? (
<SearchResultsByProperty
<SearchResultsByCategory
onSelectVehicleResult={onSelectVehicleResult}
onSelectLocationResult={onSelectLocationResult}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import { useContext } from "react"
import { SocketContext } from "../../contexts/socketContext"
import { StateDispatchContext } from "../../contexts/stateDispatchContext"
import { LimitedSearchResults } from "../../hooks/useSearchResults"
import {
SearchProperty,
searchPropertyDisplayConfig,
} from "../../models/searchQuery"
import { SearchResultCategory } from "../../models/searchQuery"
import { Vehicle, Ghost } from "../../realtime"
import { setPropertyMatchLimit } from "../../state/searchPageState"
import SearchResults, { NoResults } from "../searchResults"
import React from "react"
import { Card, CardBody } from "../card"
Expand All @@ -19,14 +15,13 @@ import {
isOk,
} from "../../util/fetchResult"
import Loading from "../loading"
import useSearchResultsByProperty from "../../hooks/useSearchResultsByProperty"
import useSearchResultsByCategory from "../../hooks/useSearchResultsByCategory"
import { setCategoryMatchLimit } from "../../state/searchPageState"

const VehicleSearchResultSection = ({
property,
results,
onSelectVehicle,
}: {
property: SearchProperty
results: LoadingResult | Ok<LimitedSearchResults<Vehicle | Ghost>> | null
onSelectVehicle: (vehicle: Vehicle | Ghost) => void
}) => {
Expand All @@ -37,13 +32,13 @@ const VehicleSearchResultSection = ({
return (
<section
className="c-map-page__search_results_section"
aria-labelledby={`search-results__${property}`}
aria-labelledby="search-results__vehicle"
>
<h2
className="c-map-page__search_results_header"
id={`search-results__${property}`}
id="search-results__vehicle"
>
{searchPropertyDisplayConfig[property].name}
Vehicles
</h2>
{isLoading(results) ? (
<Loading />
Expand All @@ -54,7 +49,7 @@ const VehicleSearchResultSection = ({
selectedVehicleId={null}
onClick={onSelectVehicle}
/>
{results.ok.hasMoreMatches && <ShowMore property={property} />}
{results.ok.hasMoreMatches && <ShowMore category={"vehicle"} />}
</>
)}
</section>
Expand All @@ -80,7 +75,7 @@ const LocationSearchResultSection = ({
className="c-map-page__search_results_header"
id={`search-results__location`}
>
{searchPropertyDisplayConfig.location.name}
Locations
</h2>
{isLoading(results) ? (
<Loading />
Expand All @@ -104,22 +99,22 @@ const LocationSearchResultSection = ({
</li>
))}
</ul>
{results.ok.hasMoreMatches && <ShowMore property={"location"} />}
{results.ok.hasMoreMatches && <ShowMore category={"location"} />}
</>
)}
</section>
)
}

const ShowMore = ({ property }: { property: SearchProperty }) => {
const ShowMore = ({ category }: { category: SearchResultCategory }) => {
const [{ searchPageState }, dispatch] = useContext(StateDispatchContext)
const currentLimit = searchPageState.query.properties[property] || 0
const currentLimit = searchPageState.query.categoryResultLimits[category]
return (
<div className="c-map_page__search_results_actions">
<button
className="c-map-page__show_more button-text"
onClick={() =>
dispatch(setPropertyMatchLimit(property, currentLimit + 25))
dispatch(setCategoryMatchLimit(category, currentLimit + 25))
}
>
Show more
Expand All @@ -128,19 +123,24 @@ const ShowMore = ({ property }: { property: SearchProperty }) => {
)
}

const SearchResultsByProperty = ({
const SearchResultsByCategory = ({
onSelectVehicleResult,
onSelectLocationResult,
}: {
onSelectVehicleResult: (result: Vehicle | Ghost | null) => void
onSelectLocationResult: (result: LocationSearchResult | null) => void
}) => {
const [{ searchPageState }] = useContext(StateDispatchContext)
const [
{
searchPageState: { query },
},
] = useContext(StateDispatchContext)
const { socket } = useContext(SocketContext)
const resultsByProperty = useSearchResultsByProperty(
const resultsByProperty = useSearchResultsByCategory(
socket,
searchPageState.query.text,
searchPageState.query.properties
query.text,
query.property,
query.categoryResultLimits
)

const searchHasNoResults = Object.values(resultsByProperty)
Expand All @@ -161,33 +161,24 @@ const SearchResultsByProperty = ({
{searchHasNoResults ? (
<NoResults />
) : (
Object.entries(searchPageState.query.properties)
.filter(([, limit]) => limit != null)
.map(([property, _limit]) => property as SearchProperty)
.sort(
(first_property, second_property) =>
searchPropertyDisplayConfig[first_property].order -
searchPropertyDisplayConfig[second_property].order
)
.map((property) =>
property === "location" ? (
<LocationSearchResultSection
key={property}
results={resultsByProperty[property]}
onSelectLocation={onSelectLocationResult}
/>
) : (
<VehicleSearchResultSection
key={property}
property={property}
results={resultsByProperty[property]}
onSelectVehicle={onSelectVehicleResult}
/>
)
)
<>
{(query.property === "all" || query.property !== "location") && (
<VehicleSearchResultSection
results={resultsByProperty.vehicle}
onSelectVehicle={onSelectVehicleResult}
/>
)}

{(query.property === "all" || query.property === "location") && (
<LocationSearchResultSection
results={resultsByProperty.location}
onSelectLocation={onSelectLocationResult}
/>
)}
</>
)}
</div>
)
}

export default SearchResultsByProperty
export default SearchResultsByCategory
6 changes: 4 additions & 2 deletions assets/src/components/oldSearchForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useContext } from "react"
import { StateDispatchContext } from "../contexts/stateDispatchContext"
import { CircleXIcon, SearchIcon } from "../helpers/icon"
import { isValidSearchQuery } from "../models/searchQuery"
import { SearchPropertyQuery, isValidSearchQuery } from "../models/searchQuery"
import {
setOldSearchProperty,
setSearchText,
Expand Down Expand Up @@ -45,7 +45,9 @@ const OldSearchForm = ({
const handlePropertyChange = (
event: React.FormEvent<HTMLInputElement>
): void => {
dispatch(setOldSearchProperty(event.currentTarget.value))
dispatch(
setOldSearchProperty(event.currentTarget.value as SearchPropertyQuery)
)
dispatch(submitSearch())
}

Expand Down
8 changes: 6 additions & 2 deletions assets/src/hooks/useAutocompleteResults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Socket } from "phoenix"

import { SearchProperties, SearchProperty } from "../models/searchQuery"
import {
SearchProperties,
SearchProperty,
VehiclePropertyQuery,
} from "../models/searchQuery"
import { Ghost, Vehicle } from "../realtime"
import { useLimitedSearchResults } from "./useSearchResults"
import { isLoading } from "../util/fetchResult"
Expand Down Expand Up @@ -44,7 +48,7 @@ export const useAutocompleteResults = (

function useLimitedSearchResultsForProperty(
enableSearch: boolean,
property: SearchProperty
property: VehiclePropertyQuery
): (Vehicle | Ghost)[] {
// Search for the property we need to return, but if it's filtered,
// set `query` parameter to `null`
Expand Down
4 changes: 2 additions & 2 deletions assets/src/hooks/useSearchResults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Socket } from "phoenix"
import { Infer, array, boolean, type, union } from "superstruct"
import { SearchQuery, SearchProperty } from "../models/searchQuery"
import { SearchQuery, VehiclePropertyQuery } from "../models/searchQuery"
import {
GhostData,
VehicleData,
Expand Down Expand Up @@ -59,7 +59,7 @@ const loadingState: Loading = { is_loading: true }

export const useLimitedSearchResults = (
socket: Socket | undefined,
query: { property: SearchProperty; text: string; limit: number } | null
query: { property: VehiclePropertyQuery; text: string; limit: number } | null
): Ok<LimitedSearchResults<Vehicle | Ghost>> | Loading | null => {
const topic: string | null =
query && `vehicles_search:limited:${query.property}:${query.text}`
Expand Down
66 changes: 66 additions & 0 deletions assets/src/hooks/useSearchResultsByCategory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Socket } from "phoenix"
import {
CategoryResultLimits,
SearchPropertyQuery,
} from "../models/searchQuery"
import { Loading, Ok } from "../util/fetchResult"
import {
LimitedSearchResults,
useLimitedSearchResults,
} from "./useSearchResults"
import { useLocationSearchResults } from "./useLocationSearchResults"
import { LocationSearchResult } from "../models/locationSearchResult"
import { Vehicle, Ghost } from "../realtime"

export type VehicleResultType =
| Ok<LimitedSearchResults<Vehicle | Ghost>>
| Loading
| null

type LocationResultsType =
| Ok<LimitedSearchResults<LocationSearchResult>>
| Loading
| null

export type SearchResultsByCategory = {
vehicle: VehicleResultType
location: LocationResultsType
}

const useSearchResultsByCategory = (
socket: Socket | undefined,
text: string,
queryType: SearchPropertyQuery,
categoryLimits: CategoryResultLimits
): SearchResultsByCategory => {
// To avoid conditionally calling hooks, call hook to fetch results for each property, conditionally passing null
// param when the property limit is null so that no results will actually be fetched & returned.
const shouldSearchLocation = queryType === "all" || queryType === "location"
const shouldSearchVehicles = queryType === "all" || queryType !== "location"
const rawLocationResults = useLocationSearchResults(
(shouldSearchLocation && text) || null
)

const limitedLocationResults: LocationResultsType = !shouldSearchLocation
? null
: (rawLocationResults && {
ok: {
matches: rawLocationResults.slice(0, categoryLimits.location),
hasMoreMatches: rawLocationResults.length > categoryLimits.location,
},
}) || { is_loading: true }
return {
vehicle: useLimitedSearchResults(
socket,
(shouldSearchVehicles && {
property: queryType,
text: text,
limit: categoryLimits.vehicle,
}) ||
null
),
location: limitedLocationResults,
}
}

export default useSearchResultsByCategory
82 changes: 0 additions & 82 deletions assets/src/hooks/useSearchResultsByProperty.ts

This file was deleted.

Loading

0 comments on commit 6a91ed3

Please sign in to comment.