Skip to content

Commit

Permalink
fix(platform): Automatically scroll to search hits on same page (#11700)
Browse files Browse the repository at this point in the history
  • Loading branch information
chargome authored Oct 31, 2024
1 parent b607226 commit 5af5d29
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 20 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"remark-prism": "^1.3.6",
"rss": "^1.2.2",
"sass": "^1.69.5",
"search-insights": "^2.2.3",
"search-insights": "^2.17.2",
"server-only": "^0.0.1",
"sharp": "^0.33.4",
"tailwindcss-scoped-preflight": "^3.0.4",
Expand Down Expand Up @@ -137,4 +137,4 @@
"node": "20.11.0",
"yarn": "1.22.21"
}
}
}
75 changes: 61 additions & 14 deletions src/components/search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import {Fragment, useCallback, useEffect, useRef, useState} from 'react';
import {captureException} from '@sentry/nextjs';
import {
Hit,
Result,
Expand All @@ -9,7 +10,7 @@ import {
} from '@sentry-internal/global-search';
import DOMPurify from 'dompurify';
import Link from 'next/link';
import {useRouter} from 'next/navigation';
import {usePathname, useRouter} from 'next/navigation';
import algoliaInsights from 'search-insights';

import {useOnClickOutside} from 'sentry-docs/clientUtils';
Expand Down Expand Up @@ -90,6 +91,7 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
const [showOffsiteResults, setShowOffsiteResults] = useState(false);
const [loading, setLoading] = useState(true);
const router = useRouter();
const pathname = usePathname();

const handleClickOutside = useCallback((ev: MouseEvent) => {
// don't close the search results if the user is clicking the expand button
Expand Down Expand Up @@ -196,21 +198,66 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
});

const trackSearchResultClick = useCallback((hit: Hit, position: number): void => {
if (hit.id === undefined) {
return;
try {
algoliaInsights('clickedObjectIDsAfterSearch', {
eventName: 'documentation_search_result_click',
userToken: randomUserToken,
index: hit.index,
objectIDs: [hit.id],
// Positions in Algolia are 1 indexed
queryID: hit.queryID ?? '',
positions: [position + 1],
});
} catch (error) {
captureException(error);
}
}, []);

algoliaInsights('clickedObjectIDsAfterSearch', {
eventName: 'documentation_search_result_click',
userToken: randomUserToken,
index: hit.index,
objectIDs: [hit.id],
// Positions in Algolia are 1 indexed
queryID: hit.queryID ?? '',
positions: [position + 1],
});
const removeTags = useCallback((str: string) => {
return str.replace(/<\/?[^>]+(>|$)/g, '');
}, []);

const handleSearchResultClick = useCallback(
(event: React.MouseEvent<HTMLAnchorElement>, hit: Hit, position: number): void => {
if (hit.id === undefined) {
return;
}

trackSearchResultClick(hit, position);

// edge case when the clicked search result is the currently visited paged
if (relativizeUrl(hit.url) === pathname) {
// do not navigate to the search result page in this case
event.preventDefault();

// sanitize the title to remove any html tags
const title = hit?.title && removeTags(hit.title);

if (!title) {
return;
}

// check for heading with the same text as the title
const headings =
document
.querySelector('main > div.prose')
?.querySelectorAll('h1, h2, h3, h4, h5, h6') ?? [];
const foundHeading = Array.from(headings).find(heading =>
heading.textContent?.toLowerCase().includes(title.toLowerCase())
);

// close the search results and scroll to the heading if it exists
setInputFocus(false);
if (foundHeading) {
foundHeading.scrollIntoView({
behavior: 'smooth',
});
}
}
},
[pathname, removeTags, trackSearchResultClick]
);

return (
<div className={styles.search} ref={ref}>
<div className={styles['search-bar']}>
Expand Down Expand Up @@ -271,15 +318,15 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
focused?.id === hit.id ? styles['sgs-hit-focused'] : ''
}`}
ref={
// Scroll to eleemnt on focus
// Scroll to element on focus
hit.id === focused?.id
? el => el?.scrollIntoView({block: 'nearest'})
: undefined
}
>
<Link
href={relativizeUrl(hit.url)}
onClick={() => trackSearchResultClick(hit, index)}
onClick={e => handleSearchResultClick(e, hit, index)}
>
{hit.title && (
<h6>
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11110,10 +11110,10 @@ scheduler@^0.23.0:
dependencies:
loose-envify "^1.1.0"

search-insights@^2.2.3:
version "2.13.0"
resolved "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz"
integrity sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==
search-insights@^2.17.2:
version "2.17.2"
resolved "https://registry.yarnpkg.com/search-insights/-/search-insights-2.17.2.tgz#d13b2cabd44e15ade8f85f1c3b65c8c02138629a"
integrity sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==

section-matter@^1.0.0:
version "1.0.0"
Expand Down

0 comments on commit 5af5d29

Please sign in to comment.