Skip to content

Commit c5a2ab3

Browse files
committed
Improve generic result view tag and add spdx parsing
1 parent 44cd503 commit c5a2ab3

File tree

5 files changed

+67
-28
lines changed

5 files changed

+67
-28
lines changed

src/components/result/GenericResultViewTag.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,41 @@ import { autoUnwrap } from "@/components/result/utils"
66
import { useCopyToClipboard } from "usehooks-ts"
77

88
export interface GenericResultViewTagProps {
9+
/**
10+
* The elasticsearch field that this tag will display
11+
*/
912
field: string
1013
result: SearchResult
14+
/**
15+
* Icon for this tag, can be any react component. Ideally a [lucide icon](https://lucide.dev) with 16px by 16px site.
16+
*/
1117
icon?: ReactNode
18+
/**
19+
* Label to display in a tooltip
20+
*/
1221
label?: string
13-
valueMapper?: (value: string | string[]) => string | ReactNode
22+
/**
23+
* Optional, here you can map each value or all values of the elasticsearch field to a string or a React component.
24+
* Can't the used together with `singleValueMapper`
25+
* @param value
26+
*/
27+
valueMapper?: (value: string | string[]) => ReactNode
28+
/**
29+
* Optional, here you can map each value of the elasticsearch field to a string or a React component. Can't be used
30+
* together with `valueMapper`
31+
* @param value
32+
*/
33+
singleValueMapper?: (value: string) => ReactNode
1434
}
1535

16-
export function GenericResultViewTag({ field, result, icon, label, valueMapper }: GenericResultViewTagProps) {
36+
export function GenericResultViewTag({ field, result, icon, label, valueMapper, singleValueMapper }: GenericResultViewTagProps) {
1737
const value = useMemo(() => {
18-
const value = autoUnwrap(result[field])
38+
const value: string | string[] = autoUnwrap(result[field])
1939
if (!value) return undefined
2040
if (valueMapper) return valueMapper(value)
41+
if (singleValueMapper) return Array.isArray(value) ? value.map(singleValueMapper) : singleValueMapper(value)
2142
else return value
22-
}, [field, result, valueMapper])
43+
}, [field, result, singleValueMapper, valueMapper])
2344

2445
const [, copy] = useCopyToClipboard()
2546

@@ -33,7 +54,7 @@ export function GenericResultViewTag({ field, result, icon, label, valueMapper }
3354
)
3455

3556
const base = useCallback(
36-
(value: string) => {
57+
(value: ReactNode) => {
3758
return (
3859
<Badge variant="secondary" className="rfs-truncate" onClick={copyTagValue}>
3960
<span className="rfs-flex rfs-truncate">

src/components/result/PidDisplay.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PidResolver, pidResolver } from "@/lib/pidResolver"
22
import { memo, useCallback } from "react"
3-
import { ontobeeResolver } from "@/lib/OntobeeResolver"
3+
import { tempResolver } from "@/lib/TempResolver"
44
import useSWRImmutable from "swr/immutable"
55

66
/**
@@ -14,17 +14,15 @@ export const PidDisplay = memo(function PidDisplay({ pid }: { pid: string }) {
1414
const content = await pidResolver.resolve(pid)
1515
return content.name
1616
} else if (pid.startsWith("http://purl.obolibrary.org")) {
17-
return ontobeeResolver.parse(pid)
17+
return tempResolver.resolvePurl(pid)
18+
} else if (pid.startsWith("https://spdx.org")) {
19+
return tempResolver.resolveSpdx(pid)
1820
} else {
1921
return pid
2022
}
2123
}, [])
2224

2325
const { data, error } = useSWRImmutable(pid, resolveContent)
2426

25-
if (error) {
26-
return <div className="rfs-text-red-500">{pid}</div>
27-
}
28-
29-
return <span>{data ?? ""}</span>
27+
return <span>{error ? pid : (data ?? "")}</span>
3028
})

src/lib/OntobeeResolver.ts renamed to src/lib/TempResolver.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { z } from "zod"
22

3+
/**
4+
* !TODO Replace with resolver from pid-component
5+
*/
6+
37
const TS4TIBResponse = z.object({
48
response: z.object({
59
docs: z.object({ label: z.string(), iri: z.string() }).array()
610
})
711
})
812

9-
export class OntobeeResolver {
10-
async parse(url: string) {
13+
export class TempResolver {
14+
async resolvePurl(url: string) {
1115
try {
1216
const result = await fetch(`https://service.tib.eu/ts4tib/api/select?q=${url}`)
1317
const data = await result.json()
@@ -23,6 +27,17 @@ export class OntobeeResolver {
2327
return url
2428
}
2529
}
30+
31+
async resolveSpdx(url: string) {
32+
try {
33+
const result = await fetch(url)
34+
const data = await result.json()
35+
console.log(data)
36+
} catch (e) {
37+
console.error(e)
38+
return url
39+
}
40+
}
2641
}
2742

28-
export const ontobeeResolver = new OntobeeResolver()
43+
export const tempResolver = new TempResolver()

src/lib/pidResolver.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,15 @@ export class PidResolver {
4343
return PIDDataSchema.parse(data)
4444
} catch (e) {
4545
console.error(`Failed to parse response for resolve request of pid ${pid}`, e)
46-
throw undefined
46+
return null
4747
}
48+
} else {
49+
console.error(`Network request failed for parsing pid ${pid}`, request.status)
50+
return null
4851
}
4952
} catch (e) {
5053
console.error(`Network request failed for parsing pid ${pid}`, e)
51-
throw undefined
54+
return null
5255
}
5356
}
5457
}

src/stories/FairDOElasticSearch.stories.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,16 @@ const demoConfig: FairDOConfig = {
2525
apiKey: "UGNoTW1KUUJ3WmluUHBTcEVpalo6cGloOUVKZ0tTdnlMYVlpTzV4SXBrUQ==",
2626
indices: [
2727
{
28-
name: "fdo-test-6",
28+
name: "fdo-test-8",
2929
facets: [
3030
{
3131
key: "resourceType.keyword",
3232
label: "Resource Type"
3333
},
3434
{
3535
key: "hadPrimarySource.keyword",
36-
label: "Source",
37-
prettyPrintURLs: true
36+
label: "Source"
37+
// usePidResolver: true
3838
},
3939
{
4040
key: "NMR_Method.keyword",
@@ -47,7 +47,8 @@ const demoConfig: FairDOConfig = {
4747
},
4848
{
4949
key: "Acquisition_Nucleus.keyword",
50-
label: "Acquisition Nucleus"
50+
label: "Acquisition Nucleus",
51+
usePidResolver: true
5152
},
5253
{
5354
key: "dateCreatedRfc3339",
@@ -62,7 +63,7 @@ const demoConfig: FairDOConfig = {
6263
{
6364
key: "licenseURL.keyword",
6465
label: "License",
65-
prettyPrintURLs: true
66+
usePidResolver: true
6667
},
6768
{
6869
key: "digitalObjectType.keyword",
@@ -168,7 +169,7 @@ export const GenericResultRenderer: Story = {
168169
icon: <UserIcon className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
169170
label: "Contact",
170171
field: "contact",
171-
valueMapper: (v) => (Array.isArray(v) ? v.map((vv) => <OrcidDisplay orcid={vv} />) : <OrcidDisplay orcid={v} />)
172+
singleValueMapper: (v) => <OrcidDisplay orcid={v} />
172173
},
173174
{
174175
icon: <GraduationCap className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
@@ -178,26 +179,26 @@ export const GenericResultRenderer: Story = {
178179
{
179180
icon: <GlobeIcon className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
180181
field: "hadPrimarySource",
181-
valueMapper: (v) => tryURLPrettyPrint(v + ""),
182+
// singleValueMapper: (v) => <PidDisplay pid={v} />,
182183
label: "Source"
183184
},
184185
{
185186
icon: <ScaleIcon className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
186187
field: "licenseURL",
187-
valueMapper: (v) => tryURLPrettyPrint(v + ""),
188+
singleValueMapper: (v) => tryURLPrettyPrint(v),
188189
label: "License URL"
189190
},
190191
{
191192
icon: <AtomIcon className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
192193
field: "Compound.Molar_mass",
193194
label: "Molar Mass",
194-
valueMapper: (v) => v + " g/mol"
195+
singleValueMapper: (v) => v + " g/mol"
195196
},
196197
{
197198
icon: <Microscope className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
198199
label: "NMR Method",
199200
field: "NMR_Method",
200-
valueMapper: (v) => <PidDisplay pid={v + ""} />
201+
singleValueMapper: (v) => <PidDisplay pid={v} />
201202
},
202203
{
203204
icon: <AudioLines className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
@@ -207,7 +208,8 @@ export const GenericResultRenderer: Story = {
207208
{
208209
icon: <CircleDot className="rfs-shrink-0 rfs-size-4 rfs-mr-2" />,
209210
label: "Acquisition Nucleus",
210-
field: "Acquisition_Nucleus"
211+
field: "Acquisition_Nucleus",
212+
singleValueMapper: (v) => <PidDisplay pid={v} />
211213
}
212214
]}
213215
titleField="name"

0 commit comments

Comments
 (0)