Skip to content

Commit

Permalink
feat: implement adding a single menu arg to the URL pathname
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinstadler committed Jan 6, 2025
1 parent 16575f4 commit f5cb4ba
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 48 deletions.
6 changes: 1 addition & 5 deletions app/[locale]/languages/[language]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ export default function LanguagesPage(props: LanguagesPageProps) {
const tl = useTranslations("Languages");
return (
<MainContent>
<InstantSearchView
pageName="languages"
pathnameField="language"
// queryArgsToMenuFields={{ language: "language" }}
>
<InstantSearchView pageName="languages" pathnameField="language">
<SingleRefinementList
allLabel={t("all languages")}
attribute={"language"}
Expand Down
6 changes: 3 additions & 3 deletions app/[locale]/translators/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import type { MenuItem } from "instantsearch.js/es/connectors/menu/connectMenu";
import { useTranslations } from "next-intl";
import { useMenu } from "react-instantsearch";

import { InstantSearchProvider } from "@/components/instantsearch/instantsearchprovider";
import { SingleRefinementDropdown } from "@/components/instantsearch/single-refinement-dropdown";
import { ThomasBernhardInstantSearchProvider } from "@/components/instantsearch/thomas-bernhard/thomasbernhard-instantsearchprovider";
import { MainContent } from "@/components/main-content";
import { TranslatorLink } from "@/components/translator-link";

Expand Down Expand Up @@ -49,7 +49,7 @@ export default function TranslatorsPage() {
const tl = useTranslations("Languages");
return (
<MainContent className="mx-auto w-screen max-w-screen-lg p-6">
<InstantSearchProvider
<ThomasBernhardInstantSearchProvider
filters="contains.has_translators:true"
queryArgsToMenuFields={{ language: "language" }}
>
Expand All @@ -74,7 +74,7 @@ export default function TranslatorsPage() {
/>
</div>
<TranslatorsList />
</InstantSearchProvider>
</ThomasBernhardInstantSearchProvider>
</MainContent>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,26 @@ import { MainContent } from "@/components/main-content";

interface WorksPageProps {
params: {
id_or_category: string;
category: string;
};
}

export default function WorksPage(props: WorksPageProps) {
const ct = useTranslations("BernhardCategories");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const categoryLabel = ct(props.params.id_or_category as any);
const categoryLabel = ct(props.params.category as any);
// TODO validate category
const t = useTranslations("InstantSearch");
const tl = useTranslations("Languages");

// TODO get id -> year+title dictionaries from backend

return (
<ThomasBernhardInstantSearchProvider
filters={`contains.work.category:${props.params.id_or_category}`}
queryArgsToMenuFields={{ language: "language", work: "contains.work.yeartitle" }}
filters={`contains.work.category:=${props.params.category}`}
// pageName={props.params.category} // hack
// pathnameField="contains.work.yeartitle"
queryArgsToMenuFields={{ work: "contains.work.yeartitle", language: "language" }}
>
<MainContent>
<div className="grid h-full grid-cols-[25%_75%] gap-6 px-2">
Expand All @@ -38,10 +43,10 @@ export default function WorksPage(props: WorksPageProps) {
transformItems: (items: Array<RefinementListItem>) => {
return items
.filter((item) => {
return item.label.startsWith(props.params.id_or_category);
return item.label.startsWith(props.params.category);
})
.map((item) => {
const [_cat, year, title] = item.label.split("$");
const [year, title] = item.label.split("_");
item.label = Number.isNaN(parseInt(year!)) ? title! : `${title!} (${year!})`;
return item;
});
Expand Down
27 changes: 15 additions & 12 deletions components/instantsearch/instantsearchprovider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { UiState } from "instantsearch.js";
// eslint-disable-next-line no-restricted-imports
import singletonRouter from "next/router";
import type { ReactNode } from "react";
import { Configure } from "react-instantsearch";
import { InstantSearchNext } from "react-instantsearch-nextjs";
import { createInstantSearchRouterNext } from "react-instantsearch-router-nextjs";
import type { SearchClient } from "typesense-instantsearch-adapter";
Expand All @@ -16,7 +15,6 @@ export interface InstantSearchProviderProps {
pathnameField?: string;
queryArgsToMenuFields?: Record<string, string>;
defaultSort?: string;
filters?: string;
searchClient: SearchClient;
}

Expand All @@ -26,18 +24,13 @@ export function InstantSearchProvider(props: InstantSearchProviderProps): ReactN
const {
children,
collectionName,
filters,
defaultSort,
pageName,
pathnameField,
queryArgsToMenuFields,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
searchClient,
} = props;
const filter = filters
? // '&&' is typesense convention, not instantsearch!
`erstpublikation:true && ${filters}`
: "erstpublikation:true";
return (
<InstantSearchNext
indexName={collectionName}
Expand All @@ -47,10 +40,21 @@ export function InstantSearchProvider(props: InstantSearchProviderProps): ReactN
// https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch-router-nextjs
singletonRouter,
routerOptions: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
createURL({ location }) {
// this function doesn't get called for some reason
return `/en/languages/dummy`;
createURL({ location, routeState, qsModule }) {
// BUG this function never gets called
const parts = location.pathname.split("/");
if (pageName) {
if (parts.at(-1) !== pageName) {
parts.pop();
}
// TODO also remove value if not in routestate
if (pathnameField && pathnameField in routeState) {
parts.push(routeState[pathnameField] as string);
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete routeState[pathnameField];
}
}
return `${parts.join("/")}?${qsModule.stringify(routeState)}`;
},
parseURL({ qsModule, location }) {
const queryArgs = qsModule.parse(location.search.slice(1));
Expand Down Expand Up @@ -130,7 +134,6 @@ export function InstantSearchProvider(props: InstantSearchProviderProps): ReactN
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
searchClient={searchClient}
>
<Configure filters={filter} hitsPerPage={30} />
{children}
</InstantSearchNext>
);
Expand Down
7 changes: 1 addition & 6 deletions components/instantsearch/sortby.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,9 @@ export function InstantSearchSortBy(props: InstantSearchSortByProps): ReactNode
const { currentRefinement, options, refine } = useSortBy({
items: sortByItems,
});
// enforce initial sort
if (currentRefinement === collectionName) {
refine(`${collectionName}/sort/${props.sortOptions[0]!}`);
}

return (
<Select
defaultSelectedKey={sortByItems[0]?.value}
defaultSelectedKey={currentRefinement}
onSelectionChange={(selected) => {
refine(selected as string);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import type { ReactNode } from "react";
import { Configure } from "react-instantsearch";
import TypesenseInstantSearchAdapter, { type SearchClient } from "typesense-instantsearch-adapter";

import { env } from "@/config/env.config";
Expand Down Expand Up @@ -28,13 +29,16 @@ const typesenseInstantsearchAdapter = new TypesenseInstantSearchAdapter({
const searchClient = typesenseInstantsearchAdapter.searchClient as unknown as SearchClient;

export interface ThomasBernhardInstantSearchProviderProps
extends Partial<InstantSearchProviderProps> {}
extends Partial<InstantSearchProviderProps> {
filters?: string;
hitsPerPage?: number;
}

export function ThomasBernhardInstantSearchProvider(
props: ThomasBernhardInstantSearchProviderProps,
): ReactNode {
const { children, filters } = props;
const _filter = filters
const { children, filters, hitsPerPage = 30 } = props;
const filter = filters
? // '&&' is typesense convention, not instantsearch!
`erstpublikation:true && ${filters}`
: "erstpublikation:true";
Expand All @@ -46,6 +50,7 @@ export function ThomasBernhardInstantSearchProvider(
searchClient={searchClient}
{...props}
>
<Configure filters={filter} hitsPerPage={hitsPerPage} />
{children}
</InstantSearchProvider>
);
Expand Down
48 changes: 35 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f5cb4ba

Please sign in to comment.