Skip to content

Commit

Permalink
Merge pull request #44 from NYPL/SCC-3771/search-results-sorting
Browse files Browse the repository at this point in the history
SCC-3771 - Search Results Sorting
  • Loading branch information
dgcohen authored Nov 27, 2023
2 parents 17e4637 + 4cd23cd commit ed4d955
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 19 deletions.
29 changes: 26 additions & 3 deletions __test__/pages/search/searchResults.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react"
import userEvent from "@testing-library/user-event"
import { render, screen } from "@testing-library/react"

import mockRouter from "next-router-mock"
Expand All @@ -11,18 +12,40 @@ jest.mock("next/router", () => jest.requireActual("next-router-mock"))

describe("Search Results page", () => {
describe("More than 50 bibs", () => {
it("displays many bibs", () => {
const query = "spaghetti"
let query: string

beforeEach(() => {
query = "spaghetti"
mockRouter.push(`/search?q=${query}`)
render(<SearchResults results={results} />)

})
it("displays many bibs", () => {
const displayingText = screen.getByRole("heading", { level: 2 })
expect(displayingText).toHaveTextContent(
`Displaying 1-50 of ${results.results.totalResults} results for keyword "${query}"`
)
const cards = screen.getAllByRole("heading", { level: 3 })
expect(cards).toHaveLength(50)
})
it("renders the sort select field and updates the query string in the url on changes", async () => {
const sortSelect = screen.getByLabelText("Sort by")
expect(sortSelect).toHaveValue("relevance")
await userEvent.selectOptions(sortSelect, "Title (A - Z)")
expect(sortSelect).toHaveValue("title_asc")

expect(mockRouter.asPath).toBe(
"/?q=spaghetti&sort=title&sort_direction=asc"
)
})
it("returns the user to the first page on sorting changes", async () => {
await mockRouter.push(`/search?q=${query}&page=2`)
const sortSelect = screen.getByLabelText("Sort by")
await userEvent.selectOptions(sortSelect, "Title (Z - A)")

expect(mockRouter.asPath).toBe(
"/?q=spaghetti&sort=title&sort_direction=desc"
)
})
})
describe("No bibs", () => {
it("displays No results message", () => {
Expand Down
61 changes: 52 additions & 9 deletions pages/search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import Head from "next/head"
import { Heading, SimpleGrid } from "@nypl/design-system-react-components"
import {
Heading,
SimpleGrid,
Select,
} from "@nypl/design-system-react-components"
import type { ChangeEvent } from "react"
import { useRouter } from "next/router"
import { parse } from "qs"

Expand All @@ -11,7 +16,10 @@ import { fetchResults } from "../api/search"
import {
mapQueryToSearchParams,
mapElementsToSearchResultsBibs,
getQueryString,
sortOptions,
} from "../../src/utils/searchUtils"
import type { SortKey, SortOrder } from "../../src/types/searchTypes"
import { mapWorksToDRBResults } from "../../src/utils/drbUtils"
import { SITE_NAME } from "../../src/config/constants"
import type SearchResultsBib from "../../src/models/SearchResultsBib"
Expand All @@ -21,7 +29,7 @@ import type SearchResultsBib from "../../src/models/SearchResultsBib"
* as well as displaying and controlling pagination and search filters.
*/
export default function Search({ results }) {
const { query } = useRouter()
const { push, query } = useRouter()
const { itemListElement: searchResultsElements, totalResults } =
results.results

Expand All @@ -37,6 +45,19 @@ export default function Search({ results }) {
// Map DRB Works from response to DRBResult objects
const drbResults = mapWorksToDRBResults(drbWorks)

const handleSortChange = async (e: ChangeEvent<HTMLInputElement>) => {
const selectedSortOption = e.target.value
// Extract sort key and order from selected sort option using "_" delineator
const [sortBy, order] = selectedSortOption.split("_") as [
SortKey,
SortOrder | undefined
]
// Push the new query values, removing the page number if set.
await push(
getQueryString({ ...searchParams, sortBy, order, page: undefined })
)
}

return (
<>
<Head>
Expand All @@ -45,13 +66,35 @@ export default function Search({ results }) {
<Layout
activePage="search"
sidebar={
drbResponse?.totalWorks && (
<DRBContainer
drbResults={drbResults}
totalWorks={drbResponse.totalWorks}
searchParams={searchParams}
/>
)
<>
{totalResults && (
<Select
name="sort_direction"
id="search-results-sort"
labelText="Sort by"
mb="l"
onChange={handleSortChange}
value={
searchParams.order
? `${searchParams.sortBy}_${searchParams.order}`
: searchParams.sortBy
}
>
{Object.keys(sortOptions).map((key) => (
<option value={key} key={`sort-by-${key}`}>
{sortOptions[key]}
</option>
))}
</Select>
)}
{drbResponse?.totalWorks && (
<DRBContainer
drbResults={drbResults}
totalWorks={drbResponse.totalWorks}
searchParams={searchParams}
/>
)}
</>
}
>
{totalResults ? (
Expand Down
2 changes: 1 addition & 1 deletion src/models/Item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default class Item {
this.accessMessage = item.accessMessage?.length
? item.accessMessage[0]?.prefLabel
: ""
this.callNumber = item.shelfMark.length ? item.shelfMark[0] : null
this.callNumber = item.shelfMark?.length ? item.shelfMark[0] : null
this.volume = item.enumerationChronology?.length
? item.enumerationChronology[0]
: null
Expand Down
11 changes: 7 additions & 4 deletions src/types/searchTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export interface Identifiers {
export interface SearchParams {
q?: string
field?: string
sortBy?: string
order?: string
sortBy?: SortKey
order?: SortOrder
filters?: SearchFilters
contributor?: string
title?: string
Expand All @@ -43,6 +43,9 @@ export interface SearchParams {
identifiers?: Identifiers
}

export type SortKey = "relevance" | "title" | "date"
export type SortOrder = "asc" | "desc"

type SearchFormField = { value: string }

export interface SearchResultsResponse {
Expand Down Expand Up @@ -86,8 +89,8 @@ export interface SearchQueryParams extends Identifiers {
title?: string
subject?: string
filters?: SearchFilters
sort?: string
sort_direction?: string
sort?: SortKey
sort_direction?: SortOrder
sort_scope?: string
search_scope?: string
page?: number
Expand Down
12 changes: 12 additions & 0 deletions src/utils/searchUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,18 @@ export function mapElementsToSearchResultsBibs(

/* eslint-disable @typescript-eslint/naming-convention */

/**
* sortOptions
* The allowed keys for the sort field and their respective labels
*/
export const sortOptions: Record<string, string> = {
relevance: "Relevance",
title_asc: "Title (A - Z)",
title_desc: "Title (Z - A)",
date_asc: "Date (Old to New)",
date_desc: "Date (New to Old)",
}

/**
* mapQueryToSearchParams
* Maps the SearchQueryParams structure from the request to a SearchParams object, which is expected by fetchResults
Expand Down
4 changes: 2 additions & 2 deletions src/utils/utilsTests/searchUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("searchUtils", () => {
isbn: "456",
search_scope: "contributor",
sort_direction: "asc",
sort: "spaghetti",
sort: "relevance",
})
).toEqual({
identifiers: {
Expand All @@ -39,7 +39,7 @@ describe("searchUtils", () => {
},
field: "contributor",
order: "asc",
sortBy: "spaghetti",
sortBy: "relevance",
})
})
})
Expand Down

0 comments on commit ed4d955

Please sign in to comment.