Skip to content

Commit b1c20ab

Browse files
committed
Allow specifying result views per index
1 parent be31c85 commit b1c20ab

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed

src/components/FairDOElasticSearch.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ import { FairDOConfigBuilder } from "@/config/FairDOConfigBuilder"
1313
import { ErrorBoundary, Facet, Paging, PagingInfo, Results, ResultsPerPage, SearchBox, SearchProvider, WithSearch } from "@elastic/react-search-ui"
1414
import { Layout, ResultViewProps } from "@elastic/react-search-ui-views"
1515
import { LoaderCircle } from "lucide-react"
16-
import { ComponentType, useMemo } from "react"
16+
import { ComponentType, useCallback, useMemo } from "react"
1717
import "../index.css"
1818
import "../elastic-ui.css"
1919
import { TooltipProvider } from "./ui/tooltip"
2020
import { useAutoDarkMode } from "@/components/utils"
21-
import { PlaceholderResultView } from "@/components/result/PlaceholderResultView"
2221
import { DefaultSorting } from "@/components/search/DefaultSorting"
2322
import { NodeTypes } from "@xyflow/react"
23+
import { ResultViewSelector } from "@/components/result/ResultViewSelector"
2424

2525
/**
2626
* All-in-one component for rendering an elastic search UI based on the provided configuration. Includes
@@ -40,6 +40,7 @@ import { NodeTypes } from "@xyflow/react"
4040
export function FairDOElasticSearch({
4141
config: rawConfig,
4242
resultView,
43+
resultViewPerIndex,
4344
facetOptionView,
4445
dark,
4546
graphNodeTypes
@@ -50,9 +51,27 @@ export function FairDOElasticSearch({
5051
config: FairDOConfig
5152

5253
/**
53-
* React Component that will be used to render the results from the current search. Consider using the `GenericResultView`
54+
* React Component that will be used to render the results from the current search. Consider using the `GenericResultView`.
55+
* You can set custom result views per view using the `resultViewPerIndex` prop. Will be used as the result view
56+
* for all indices that have no override configured in `resultViewPerIndex`
57+
* @optional Can be omitted when `resultViewPerIndex` is specified for each index
58+
* @example
59+
* resultView={ ({ result }) => <GenericResultView result={result} ... /> }
5460
*/
55-
resultView: ComponentType<ResultViewProps>
61+
resultView?: ComponentType<ResultViewProps>
62+
63+
/**
64+
* React Component that will be used to render the results from the current search. Consider using the `GenericResultView`.
65+
* In this prop you have to additionally specify which index the result view should be used for. If you want to use
66+
* the same result view for all indices, use `resultView`.
67+
* @optional can be omitted when `resultView` is set
68+
* @example
69+
* resultViewPerIndex={{
70+
* "my-index-1": ({ result }) => <GenericResultView result={result} ... />
71+
* "my-index-2": OtherResultView
72+
* }}
73+
*/
74+
resultViewPerIndex?: Record<string, ComponentType<ResultViewProps>>
5675

5776
/**
5877
* React Component that will be used to render the individual options (text right of the checkboxes) in a facet.
@@ -87,9 +106,12 @@ export function FairDOElasticSearch({
87106
return config.getFacetFields()
88107
}, [config])
89108

90-
const actualResultView = useMemo(() => {
91-
return resultView ?? ((props: ResultViewProps) => <PlaceholderResultView {...props} />)
92-
}, [resultView])
109+
const actualResultView = useCallback(
110+
(props: ResultViewProps) => {
111+
return <ResultViewSelector resultProps={props} resultView={resultView} resultViewPerIndex={resultViewPerIndex} />
112+
},
113+
[resultView, resultViewPerIndex]
114+
)
93115

94116
return (
95117
<SearchProvider config={elasticConfig}>

src/components/result/PlaceholderResultView.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { ObjectRender } from "@/components/result/ObjectRender"
66
export function PlaceholderResultView(props: ResultViewProps) {
77
return (
88
<div className="rfs-border rfs-p-4 rfs-mb-2 rfs-rounded-lg rfs-space-y-2">
9-
<div>Please specify resultView to render results</div>
9+
<div>
10+
No result view has been specified. Please specify <code>resultView</code> or <code>resultViewPerIndex</code> on the{" "}
11+
<code>FairDOElasticSearch</code> component
12+
</div>
1013
<Dialog>
1114
<DialogTrigger asChild>
1215
<Button variant="secondary" size="sm">
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ResultViewProps } from "@elastic/react-search-ui-views"
2+
import { ComponentType, useMemo } from "react"
3+
import { PlaceholderResultView } from "@/components/result/PlaceholderResultView"
4+
import { z } from "zod"
5+
6+
interface ResultViewSelectorProps {
7+
resultView?: ComponentType<ResultViewProps>
8+
resultViewPerIndex?: Record<string, ComponentType<ResultViewProps>>
9+
resultProps: ResultViewProps
10+
}
11+
12+
const resultWithMeta = z.object({
13+
_meta: z.object({
14+
rawHit: z.object({
15+
_index: z.string()
16+
})
17+
})
18+
})
19+
20+
export function ResultViewSelector({ resultProps, resultView, resultViewPerIndex }: ResultViewSelectorProps) {
21+
const fallbackResultView: ComponentType<ResultViewProps> = useMemo(() => {
22+
return resultView ?? ((props: ResultViewProps) => <PlaceholderResultView {...props} />)
23+
}, [resultView])
24+
25+
const index = useMemo(() => {
26+
try {
27+
const parsed = resultWithMeta.parse(resultProps.result)
28+
return parsed._meta.rawHit._index
29+
} catch (e) {
30+
console.error("Could not determine result source index in ResultViewSelector", e)
31+
return null
32+
}
33+
}, [resultProps.result])
34+
35+
const specificResultView = useMemo(() => {
36+
if (index === null) return null
37+
if (resultViewPerIndex && index in resultViewPerIndex) {
38+
return resultViewPerIndex[index]
39+
} else return null
40+
}, [index, resultViewPerIndex])
41+
42+
const Selected = useMemo(() => {
43+
return specificResultView ?? fallbackResultView
44+
}, [fallbackResultView, specificResultView])
45+
46+
return <Selected {...resultProps} />
47+
}

src/stories/FairDOElasticSearch.stories.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,34 @@ export const GenericResultRenderer: Story = {
219219
)
220220
}
221221
}
222+
223+
const test: FairDOConfig = {
224+
debug: false,
225+
alwaysSearchOnInitialLoad: true,
226+
// host: "https://matwerk.datamanager.kit.edu/search-proxy/api/v1",
227+
host: "https://demo.datamanager.kit.edu/base-repo/api/v1/",
228+
apiKey: "UGNoTW1KUUJ3WmluUHBTcEVpalo6cGloOUVKZ0tTdnlMYVlpTzV4SXBrUQ==",
229+
indices: [
230+
{
231+
name: "baserepo",
232+
facets: [
233+
{
234+
key: "created",
235+
type: "date_time_no_millis",
236+
label: "Created"
237+
}
238+
],
239+
resultFields: [], // Leave empty to get all fields
240+
searchFields: []
241+
}
242+
],
243+
disjunctiveFacets: [],
244+
imageProxy: (src) => `https://wsrv.nl/?url=${src}&h=1000&output=webp&ll`
245+
}
246+
247+
export const Test: Story = {
248+
args: {
249+
config: test,
250+
resultView: null!
251+
}
252+
}

0 commit comments

Comments
 (0)