Skip to content

Commit 689f433

Browse files
committed
feat: enhance highlightkeyword to check secondary fields when visible fields don't match
1 parent f14cffe commit 689f433

File tree

2 files changed

+111
-11
lines changed

2 files changed

+111
-11
lines changed

src/components/SearchPage/DatasetCard.tsx

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Typography, Card, CardContent, Stack, Chip } from "@mui/material";
22
import { Colors } from "design/theme";
33
import React from "react";
4+
import { useMemo } from "react";
45
import { Link } from "react-router-dom";
56
import RoutesEnum from "types/routes.enum";
67

@@ -17,14 +18,73 @@ interface DatasetCardProps {
1718
info?: {
1819
Authors?: string[];
1920
DatasetDOI?: string;
21+
[k: string]: any;
2022
};
23+
[k: string]: any;
2124
};
2225
};
2326
index: number;
2427
onChipClick: (key: string, value: string) => void;
2528
keyword?: string; // for keyword highlight
2629
}
2730

31+
/** ---------- utility helpers ---------- **/
32+
const normalize = (s: string) =>
33+
s
34+
?.replace(/[\u2018\u2019\u2032]/g, "'") // curly → straight
35+
?.replace(/[\u201C\u201D\u2033]/g, '"') ?? // curly → straight
36+
"";
37+
38+
const containsKeyword = (text?: string, kw?: string) => {
39+
if (!text || !kw) return false;
40+
const t = normalize(text).toLowerCase();
41+
const k = normalize(kw).toLowerCase();
42+
return t.includes(k);
43+
};
44+
45+
/** Find a short snippet in secondary fields if not already visible */
46+
function findMatchSnippet(
47+
v: any,
48+
kw?: string
49+
): { label: string; html: string } | null {
50+
if (!kw) return null;
51+
52+
// Which fields to scan (can add/remove fields here)
53+
const CANDIDATE_FIELDS: Array<[string, (v: any) => string | undefined]> = [
54+
["Acknowledgements", (v) => v?.info?.Acknowledgements],
55+
[
56+
"Funding",
57+
(v) =>
58+
Array.isArray(v?.info?.Funding)
59+
? v.info.Funding.join(" ")
60+
: v?.info?.Funding,
61+
],
62+
["ReferencesAndLinks", (v) => v?.info?.ReferencesAndLinks],
63+
];
64+
65+
const k = normalize(kw).toLowerCase();
66+
67+
for (const [label, getter] of CANDIDATE_FIELDS) {
68+
const raw = getter(v); // v = parsedJson.value
69+
if (!raw) continue;
70+
const text = normalize(String(raw));
71+
const i = text.toLowerCase().indexOf(k); // k is the lowercase version of keyword
72+
if (i >= 0) {
73+
const start = Math.max(0, i - 40);
74+
const end = Math.min(text.length, i + k.length + 40);
75+
const before = text.slice(start, i);
76+
const hit = text.slice(i, i + k.length);
77+
const after = text.slice(i + k.length, end);
78+
const html = `${
79+
start > 0 ? "…" : ""
80+
}${before}<mark>${hit}</mark>${after}${end < text.length ? "…" : ""}`;
81+
return { label, html };
82+
}
83+
}
84+
return null;
85+
}
86+
/** ---------- end of helpers ---------- **/
87+
2888
const DatasetCard: React.FC<DatasetCardProps> = ({
2989
dbname,
3090
dsname,
@@ -40,7 +100,29 @@ const DatasetCard: React.FC<DatasetCardProps> = ({
40100
const rawDOI = info?.DatasetDOI?.replace(/^doi:/, "");
41101
const doiLink = rawDOI ? `https://doi.org/${rawDOI}` : null;
42102

43-
// keyword hightlight functional component
103+
// precompute what’s visible & whether it already contains the keyword
104+
const authorsJoined = Array.isArray(info?.Authors)
105+
? info!.Authors.join(", ")
106+
: typeof info?.Authors === "string"
107+
? info!.Authors
108+
: "";
109+
110+
const visibleHasKeyword = useMemo(
111+
() =>
112+
containsKeyword(name, keyword) ||
113+
containsKeyword(readme, keyword) ||
114+
containsKeyword(authorsJoined, keyword),
115+
[name, readme, authorsJoined, keyword]
116+
);
117+
118+
// If not visible, produce a one-line snippet from other fields (for non-visible fields)
119+
const snippet = useMemo(
120+
() =>
121+
!visibleHasKeyword ? findMatchSnippet(parsedJson.value, keyword) : null,
122+
[parsedJson.value, keyword, visibleHasKeyword]
123+
);
124+
125+
// keyword hightlight functional component (only for visible fields)
44126
const highlightKeyword = (text: string, keyword?: string) => {
45127
if (!keyword || !text?.toLowerCase().includes(keyword.toLowerCase())) {
46128
return text;
@@ -99,7 +181,10 @@ const DatasetCard: React.FC<DatasetCardProps> = ({
99181
{highlightKeyword(name || "Untitled Dataset", keyword)}
100182
</Typography>
101183
<Typography>
102-
Database: {dbname} &nbsp;&nbsp;|&nbsp;&nbsp; Dataset: {dsname}
184+
{/* Database: {dbname} &nbsp;&nbsp;|&nbsp;&nbsp; Dataset: {dsname} */}
185+
<strong>Database:</strong> {highlightKeyword(dbname, keyword)}
186+
{" "}&nbsp;&nbsp;|&nbsp;&nbsp;{" "}
187+
<strong>Dataset:</strong> {highlightKeyword(dsname, keyword)}
103188
</Typography>
104189

105190
<Stack spacing={2} margin={1}>
@@ -168,20 +253,35 @@ const DatasetCard: React.FC<DatasetCardProps> = ({
168253
{info?.Authors && (
169254
<Typography variant="body2" mt={1}>
170255
<strong>Authors:</strong>{" "}
171-
{highlightKeyword(
172-
Array.isArray(info.Authors)
173-
? info.Authors.join(", ")
174-
: typeof info.Authors === "string"
175-
? info.Authors
176-
: "N/A",
177-
keyword
178-
)}
256+
{highlightKeyword(authorsJoined || "N/A", keyword)}
179257
</Typography>
180258
)}
181259
</Typography>
182260
)}
183261
</Stack>
184262

263+
{/* show why it matched if not visible in main fields */}
264+
{snippet && (
265+
<Stack direction="row" spacing={1} flexWrap="wrap" gap={1}>
266+
<Chip
267+
label={`Matched in ${snippet.label}`}
268+
size="small"
269+
sx={{
270+
height: 22,
271+
backgroundColor: "#f9f9ff",
272+
color: Colors.darkPurple,
273+
border: `1px solid ${Colors.lightGray}`,
274+
}}
275+
/>
276+
<Typography
277+
variant="body2"
278+
sx={{ mt: 0.5 }}
279+
// safe: snippet is derived from our own strings with <mark> only
280+
dangerouslySetInnerHTML={{ __html: snippet.html }}
281+
/>
282+
</Stack>
283+
)}
284+
185285
<Stack direction="row" spacing={1} flexWrap="wrap" gap={1}>
186286
{doiLink && (
187287
<Stack mt={1}>

src/pages/DatasetDetailPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ const DatasetDetailPage: React.FC = () => {
305305
useEffect(() => {
306306
if (datasetDocument) {
307307
// ✅ Extract External Data & Assign `index`
308-
console.log("datasetDocument", datasetDocument);
308+
// console.log("datasetDocument", datasetDocument);
309309
const links = extractDataLinks(datasetDocument, "").map(
310310
(link, index) => ({
311311
...link,

0 commit comments

Comments
 (0)