Skip to content
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
8 changes: 5 additions & 3 deletions components/links.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BillTopic, CurrentCommittee } from "../functions/src/bills/types"
import { Testimony } from "components/db/testimony"
import { Bill, MemberContent } from "./db"
import { formatBillId } from "./formatting"
import { billsDefaultIndex } from "./search/bills/useBillSort"
import { TFunction } from "next-i18next"

type LinkProps = PropsWithChildren<
Expand Down Expand Up @@ -145,9 +146,10 @@ export const twitterShareLink = (publication: Testimony, t: TFunction) => {
}

export const billSearchByTopicLink = (court: number, topic: BillTopic) => {
// Routed uiState is keyed by the page's index name.
const params = {
"bills/sort/latestTestimonyAt:desc[multiselectHierarchicalMenu][topics.lvl1][0]": `${topic.category} > ${topic.topic}`,
"bills/sort/latestTestimonyAt:desc[refinementList][court][0]": `${court}`
[`${billsDefaultIndex}[multiselectHierarchicalMenu][topics.lvl1][0]`]: `${topic.category} > ${topic.topic}`,
[`${billsDefaultIndex}[refinementList][court][0]`]: `${court}`
}
return `/bills?${new URLSearchParams(params).toString()}`
return `${maple.billSearch()}?${new URLSearchParams(params).toString()}`
}
17 changes: 11 additions & 6 deletions components/search/bills/BillSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,20 @@ import { BillHit } from "./BillHit"
import { useBillRefinements } from "./useBillRefinements"
import { SortBy, SortByWithConfigurationItem } from "../SortBy"
import { getServerConfig, VirtualFilters } from "../common"
import { billsSearchParams } from "../searchParams"
import { useBillSort } from "./useBillSort"
import { billsRelevanceSort, billsSearchParams } from "../searchParams"
import { billsDefaultIndex, useBillSort } from "./useBillSort"
import { FC, useState } from "react"
import { pathToSearchState, searchStateToUrl } from "../routingHelpers"

const searchClient = new TypesenseInstantSearchAdapter({
server: getServerConfig(),
additionalSearchParameters: billsSearchParams
// sort_by only reaches Typesense under the default option, whose index name
// (billsDefaultIndex) has no sort segment; every other option's
// "bills/sort/<sort_by>" index name overrides it in the adapter.
additionalSearchParameters: {
...billsSearchParams,
sort_by: billsRelevanceSort
}
}).searchClient

const extractLastSegmentOfRefinements = (items: any[]) => {
Expand Down Expand Up @@ -61,13 +67,12 @@ const extractLastSegmentOfRefinements = (items: any[]) => {

export const BillSearch = () => {
const items = useBillSort()
const initialSortByValue = items[0].value
return (
<SearchErrorBoundary>
<InstantSearch
indexName={initialSortByValue}
indexName={billsDefaultIndex}
initialUiState={{
[initialSortByValue]: {
[billsDefaultIndex]: {
refinementList: { court: [String(currentGeneralCourt)] }
}
}}
Expand Down
19 changes: 14 additions & 5 deletions components/search/bills/useBillSort.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { useMemo, useRef } from "react"
import { SortByWithConfigurationItem } from "../SortBy"
import { useTranslation } from "next-i18next"
import { billsRelevanceSort } from "../searchParams"
/** The InstantSearch index name of the default (Relevance) sort. It is the
* bare collection rather than "bills/sort/<sort_by>": the adapter only reads a
* sort from an index name that has a sort segment, and otherwise uses the
* sort_by BillSearch.tsx pins on the adapter. That keeps the long relevance
* clause out of Browse Bills URLs, where the index name is the qs key of the
* routed uiState (it would vanish entirely under InstantSearch's singleIndex
* state mapping, which the router does not use yet). BillSearch.tsx keys the
* page on it and links.tsx keys deep links on it.
*/
export const billsDefaultIndex = "bills"

export const useBillSort = () => {
const now = useRef(new Date().getTime())
Expand All @@ -12,12 +21,12 @@ export const useBillSort = () => {
const items: SortByWithConfigurationItem[] = useMemo(
() => [
{
label: t("sort_by.most_recent_testimony"),
value: "bills/sort/latestTestimonyAt:desc"
label: t("sort_by.relevance"),
value: billsDefaultIndex
},
{
label: t("sort_by.relevance"),
value: `bills/sort/${billsRelevanceSort}`
label: t("sort_by.most_recent_testimony"),
value: "bills/sort/latestTestimonyAt:desc"
},
{
label: t("sort_by.testimony_count"),
Expand Down
22 changes: 12 additions & 10 deletions components/search/searchParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ export const billsSearchParams = {
exclude_fields: "body,numberVariants"
} satisfies SearchParameters

/** The app's "Relevance" sort option (see useBillSort.tsx). The eval harness
* must use this sort; the UI's default latestTestimonyAt:desc sort does not
* measure text relevance.
/** The app's "Relevance" sort option and the Browse Bills default (see
* useBillSort.tsx; BillSearch.tsx pins it as the adapter's sort_by). The eval
* harness must use this sort; every other bills sort is a date or a count and
* does not measure text relevance.
*
* The `_eval` clause sorts every Order and Extension Order below every other
* document. They are procedural — "Extension Order - Education", "Order
Expand All @@ -98,11 +99,11 @@ export const billsSearchParams = {
* legislationType refinement (useBillRefinements.tsx) is how a searcher who
* wants them gets them back, which is why the facet ships with this sort.
*
* Written as two `!=` clauses rather than the equivalent `!=[...]` list form:
* this string doubles as the InstantSearch index name, which travels through
* the URL as a qs key, and qs parses percent-encoded square brackets in a key
* as nesting — mangling the routed uiState so a reload or shared link loses
* the query, refinements and sort. No other character here is special to qs.
* Written as two `!=` clauses rather than the equivalent `!=[...]` list form.
* A sort embedded in an InstantSearch index name becomes a qs key in the URL,
* and qs parses percent-encoded square brackets in a key as nesting, mangling
* the routed uiState. This string no longer rides in the index name, but the
* hearings relevance sort does, so all three stay bracket-free.
*/
export const billsRelevanceSort =
"_eval(legislationType:!=`Order` && legislationType:!=`Extension Order`):desc,_text_match:desc,testimonyCount:desc"
Expand All @@ -129,8 +130,9 @@ export const testimonySearchParams = {
exclude_fields: "billIdVariants"
} satisfies SearchParameters

/** The app's "Relevance" sort option (see useTestimonySort in
* testimony/TestimonySearch.tsx), and the sort the eval harness must use.
/** The app's "Relevance" sort option and the Browse Testimony default (see
* useTestimonySort in testimony/TestimonySearch.tsx, which pins it as the
* adapter's sort_by), and the sort the eval harness must use.
*/
export const testimonyRelevanceSort = "_text_match:desc,publishedAt:desc"

Expand Down
29 changes: 21 additions & 8 deletions components/search/testimony/TestimonySearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,40 @@ import { FollowContext, OrgFollowStatus } from "components/shared/FollowContext"
import { pathToSearchState, searchStateToUrl } from "../routingHelpers"
import { useTranslation } from "next-i18next"

/** The InstantSearch index name of the default (Relevance) sort: the bare
* collection, with the sort pinned on the adapter below, the same way Browse
* Bills does it (see billsDefaultIndex in bills/useBillSort.tsx). With no
* query every document's text match is equal, so the landing page falls
* through to the sort's publishedAt:desc tiebreak and reads newest-first.
*/
export const testimonyDefaultIndex = "publishedTestimony"

const searchClient = new TypesenseInstantSearchAdapter({
server: getServerConfig(),
additionalSearchParameters: testimonySearchParams
// sort_by only reaches Typesense under the default option, whose index name
// has no sort segment; the other options' "publishedTestimony/sort/<sort_by>"
// index names override it in the adapter.
additionalSearchParameters: {
...testimonySearchParams,
sort_by: testimonyRelevanceSort
}
}).searchClient

export const useTestimonySort = () => {
const { t } = useTranslation("search")
const items: SortByItem[] = useMemo(
() => [
{
label: t("sort_by.relevance"),
value: testimonyDefaultIndex
},
{
label: t("sort_by.newest"),
value: "publishedTestimony/sort/publishedAt:desc"
},
{
label: t("sort_by.oldest"),
value: "publishedTestimony/sort/publishedAt:asc"
},
{
label: t("sort_by.relevance"),
value: `publishedTestimony/sort/${testimonyRelevanceSort}`
}
],
[t]
Expand All @@ -60,13 +74,12 @@ export const useTestimonySort = () => {
}

export const TestimonySearch = () => {
const initialSortByValue = useTestimonySort()[0].value
return (
<SearchErrorBoundary>
<InstantSearch
indexName={initialSortByValue}
indexName={testimonyDefaultIndex}
initialUiState={{
[initialSortByValue]: {
[testimonyDefaultIndex]: {
refinementList: { court: [String(currentGeneralCourt)] }
}
}}
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/browse-bills.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ test.describe("Search result test", () => {
})

// Array of sorting test configurations
// Need to add test for sort by relevant
const sortingTests: string[] = [
"Sort by Relevance",
"Sort by Testimony Count",
"Sort by Cosponsor Count",
"Sort by Next Hearing Date",
Expand Down
1 change: 0 additions & 1 deletion tests/e2e/page_objects/testimony.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export class TestimonyPage {
}

async sort(option: string) {
// previoud code: await this.page.getByText("Sort by New -> Old").click()
await this.page
.getByText(/Sort by/i)
.first()
Expand Down
27 changes: 14 additions & 13 deletions tests/e2e/testimony.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,19 @@ test.describe("Testimony Filtering", () => {
})
})

test.describe("Testimony Sorting", () => {
test("should sort by new -> old", async ({ page }) => {
const testimonyPage = new TestimonyPage(page)
await testimonyPage.sort("Sort by New -> Old")
const sortValue = page.getByText("Sort by New -> Old", { exact: true })
await expect(sortValue).toBeVisible()
})
const sortingTests: string[] = [
"Sort by Relevance",
"Sort by Newest",
"Sort by Oldest"
]

test("should sort by old -> new", async ({ page }) => {
const testimonyPage = new TestimonyPage(page)
await testimonyPage.sort("Sort by Old -> New")
const sortValue = page.getByText("Sort by Old -> New", { exact: true })
await expect(sortValue).toBeVisible()
})
test.describe("Testimony Sorting", () => {
for (const option of sortingTests) {
test(`should sort testimony by ${option}`, async ({ page }) => {
const testimonyPage = new TestimonyPage(page)
await testimonyPage.sort(option)
const sortValue = page.getByText(option, { exact: true })
await expect(sortValue).toBeVisible()
})
}
})
Loading