11'use client' ;
22
33import { Fragment , useCallback , useEffect , useRef , useState } from 'react' ;
4+ import { captureException } from '@sentry/nextjs' ;
45import {
56 Hit ,
67 Result ,
@@ -9,7 +10,7 @@ import {
910} from '@sentry-internal/global-search' ;
1011import DOMPurify from 'dompurify' ;
1112import Link from 'next/link' ;
12- import { useRouter } from 'next/navigation' ;
13+ import { usePathname , useRouter } from 'next/navigation' ;
1314import algoliaInsights from 'search-insights' ;
1415
1516import { useOnClickOutside } from 'sentry-docs/clientUtils' ;
@@ -90,6 +91,7 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
9091 const [ showOffsiteResults , setShowOffsiteResults ] = useState ( false ) ;
9192 const [ loading , setLoading ] = useState ( true ) ;
9293 const router = useRouter ( ) ;
94+ const pathname = usePathname ( ) ;
9395
9496 const handleClickOutside = useCallback ( ( ev : MouseEvent ) => {
9597 // don't close the search results if the user is clicking the expand button
@@ -196,21 +198,66 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
196198 } ) ;
197199
198200 const trackSearchResultClick = useCallback ( ( hit : Hit , position : number ) : void => {
199- if ( hit . id === undefined ) {
200- return ;
201+ try {
202+ algoliaInsights ( 'clickedObjectIDsAfterSearch' , {
203+ eventName : 'documentation_search_result_click' ,
204+ userToken : randomUserToken ,
205+ index : hit . index ,
206+ objectIDs : [ hit . id ] ,
207+ // Positions in Algolia are 1 indexed
208+ queryID : hit . queryID ?? '' ,
209+ positions : [ position + 1 ] ,
210+ } ) ;
211+ } catch ( error ) {
212+ captureException ( error ) ;
201213 }
214+ } , [ ] ) ;
202215
203- algoliaInsights ( 'clickedObjectIDsAfterSearch' , {
204- eventName : 'documentation_search_result_click' ,
205- userToken : randomUserToken ,
206- index : hit . index ,
207- objectIDs : [ hit . id ] ,
208- // Positions in Algolia are 1 indexed
209- queryID : hit . queryID ?? '' ,
210- positions : [ position + 1 ] ,
211- } ) ;
216+ const removeTags = useCallback ( ( str : string ) => {
217+ return str . replace ( / < \/ ? [ ^ > ] + ( > | $ ) / g, '' ) ;
212218 } , [ ] ) ;
213219
220+ const handleSearchResultClick = useCallback (
221+ ( event : React . MouseEvent < HTMLAnchorElement > , hit : Hit , position : number ) : void => {
222+ if ( hit . id === undefined ) {
223+ return ;
224+ }
225+
226+ trackSearchResultClick ( hit , position ) ;
227+
228+ // edge case when the clicked search result is the currently visited paged
229+ if ( relativizeUrl ( hit . url ) === pathname ) {
230+ // do not navigate to the search result page in this case
231+ event . preventDefault ( ) ;
232+
233+ // sanitize the title to remove any html tags
234+ const title = hit ?. title && removeTags ( hit . title ) ;
235+
236+ if ( ! title ) {
237+ return ;
238+ }
239+
240+ // check for heading with the same text as the title
241+ const headings =
242+ document
243+ . querySelector ( 'main > div.prose' )
244+ ?. querySelectorAll ( 'h1, h2, h3, h4, h5, h6' ) ?? [ ] ;
245+ const foundHeading = Array . from ( headings ) . find ( heading =>
246+ heading . textContent ?. toLowerCase ( ) . includes ( title . toLowerCase ( ) )
247+ ) ;
248+
249+ // close the search results and scroll to the heading if it exists
250+ setInputFocus ( false ) ;
251+ if ( foundHeading ) {
252+ foundHeading . scrollIntoView ( {
253+ behavior : 'smooth' ,
254+ } ) ;
255+ }
256+ }
257+ } ,
258+ [ pathname , removeTags , trackSearchResultClick ]
259+ ) ;
260+
214261 return (
215262 < div className = { styles . search } ref = { ref } >
216263 < div className = { styles [ 'search-bar' ] } >
@@ -271,15 +318,15 @@ export function Search({path, autoFocus, searchPlatforms = [], showChatBot}: Pro
271318 focused ?. id === hit . id ? styles [ 'sgs-hit-focused' ] : ''
272319 } `}
273320 ref = {
274- // Scroll to eleemnt on focus
321+ // Scroll to element on focus
275322 hit . id === focused ?. id
276323 ? el => el ?. scrollIntoView ( { block : 'nearest' } )
277324 : undefined
278325 }
279326 >
280327 < Link
281328 href = { relativizeUrl ( hit . url ) }
282- onClick = { ( ) => trackSearchResultClick ( hit , index ) }
329+ onClick = { e => handleSearchResultClick ( e , hit , index ) }
283330 >
284331 { hit . title && (
285332 < h6 >
0 commit comments