@@ -11,6 +11,7 @@ import useQueryString from "~/hooks/useQueryString.ts"
1111import { RefObject } from "preact"
1212import SectionWrapper from "~/components/SectionWrapper/SectionWrapper.tsx"
1313import button from "@chainlink/design-system/button.module.css"
14+ import { updateTableOfContents } from "~/components/TableOfContents/tocStore.ts"
1415
1516export type DataFeedType = "default" | "smartdata" | "rates" | "streamsCrypto" | "streamsRwa"
1617export const FeedList = ( {
@@ -136,23 +137,119 @@ export const FeedList = ({
136137 const wrapperRef = useRef ( null )
137138 const [ showOnlySVR , setShowOnlySVR ] = useState ( false )
138139
139- // Scroll restoration handler - re- scroll to hash target after content loads
140+ // scroll handler
140141 useEffect ( ( ) => {
141- if ( typeof window === "undefined" || ! window . location . hash ) return
142-
143- // Check if chainMetadata has loaded (content is ready)
144142 if ( ! chainMetadata . loading && chainMetadata . processedData ) {
145- // Wait for any DOM updates to finish
143+ if ( typeof window === "undefined" ) return
144+
145+ // Get the anchor from URL if present
146+ const hash = window . location . hash . substring ( 1 ) // Remove the # character
147+
148+ // Force a delay to ensure DOM elements are rendered before updating
146149 setTimeout ( ( ) => {
147- const targetId = window . location . hash . substring ( 1 )
148- const targetElement = document . getElementById ( targetId )
149- if ( targetElement ) {
150- // Use scrollIntoView with behavior: auto to ensure proper positioning
151- targetElement . scrollIntoView ( { behavior : "auto" } )
150+ let hasUpdatedAnyId = false
151+
152+ // Find all section elements that need their IDs updated
153+ chainMetadata . processedData ?. networks . forEach ( ( network ) => {
154+ const sectionId = network . name . toLowerCase ( ) . replace ( / \s + / g, "-" )
155+ const existingSection = document . getElementById ( sectionId )
156+
157+ // If section exists with correct ID, no need to update
158+ if ( existingSection ) return
159+
160+ // Find section with network name title and update its ID
161+ document . querySelectorAll ( "h3" ) . forEach ( ( heading ) => {
162+ if ( heading . textContent === network . name ) {
163+ const section = heading . closest ( "section" )
164+ if ( section ) {
165+ const oldId = section . id
166+ section . id = sectionId
167+ heading . id = sectionId
168+ hasUpdatedAnyId = true
169+
170+ // Update anchor links inside the heading
171+ const anchor = heading . querySelector ( "a" )
172+ if ( anchor ) {
173+ anchor . href = `#${ sectionId } `
174+ }
175+
176+ // If we're updating the ID that matches our hash, we need to scroll to it
177+ if ( hash && ( hash === oldId || hash === sectionId ) ) {
178+ setTimeout ( ( ) => section . scrollIntoView ( { behavior : "auto" } ) , 100 )
179+ }
180+ }
181+ }
182+ } )
183+ } )
184+
185+ // Also update testnet section if it exists
186+ if ( chainMetadata . processedData ?. testnetNetwork ) {
187+ const testnetId =
188+ chainMetadata . processedData . testnetNetwork . name . toLowerCase ( ) . replace ( / \s + / g, "-" ) || "testnet-feeds"
189+ document . querySelectorAll ( "h2" ) . forEach ( ( heading ) => {
190+ if ( heading . textContent === "Testnet Feeds" || heading . textContent ?. includes ( "Testnet" ) ) {
191+ const section = heading . closest ( "section" )
192+ if ( section ) {
193+ const oldId = section . id
194+ section . id = testnetId
195+ heading . id = testnetId
196+ hasUpdatedAnyId = true
197+
198+ // Update anchor links inside the heading
199+ const anchor = heading . querySelector ( "a" )
200+ if ( anchor ) {
201+ anchor . href = `#${ testnetId } `
202+ }
203+
204+ // If we're updating the ID that matches our hash, we need to scroll to it
205+ if ( hash && ( hash === oldId || hash === testnetId ) ) {
206+ setTimeout ( ( ) => section . scrollIntoView ( { behavior : "auto" } ) , 100 )
207+ }
208+ }
209+ }
210+ } )
211+ }
212+
213+ // If we have a hash but haven't scrolled yet, try to find the element with that ID
214+ if ( hash && hasUpdatedAnyId ) {
215+ const targetElement = document . getElementById ( hash )
216+ if ( targetElement ) {
217+ setTimeout ( ( ) => targetElement . scrollIntoView ( { behavior : "auto" } ) , 100 )
218+ }
219+ } else if ( hash ) {
220+ // Basic fallback if we didnt update any IDs but still have a hash
221+ const targetElement = document . getElementById ( hash )
222+ if ( targetElement ) {
223+ setTimeout ( ( ) => targetElement . scrollIntoView ( { behavior : "auto" } ) , 200 )
224+ }
152225 }
153- } , 200 )
226+
227+ // Update TOC links if we made any ID changes
228+ if ( hasUpdatedAnyId ) {
229+ // Find the TOC container and update its links
230+ const tocLinks = document . querySelectorAll ( ".toc-item a" )
231+ tocLinks . forEach ( ( link ) => {
232+ const href = link . getAttribute ( "href" )
233+ if ( href ) {
234+ const currentHash = href . split ( "#" ) [ 1 ]
235+ if ( currentHash ) {
236+ // Try to find element with this ID
237+ const targetHeading = document . getElementById ( currentHash )
238+ if ( targetHeading ) {
239+ // Update the TOC link to point to the correct ID
240+ const updatedHref = window . location . pathname + window . location . search + "#" + currentHash
241+ link . setAttribute ( "href" , updatedHref )
242+ }
243+ }
244+ }
245+ } )
246+
247+ // Trigger a TOC update
248+ updateTableOfContents ( )
249+ }
250+ } , 300 )
154251 }
155- } , [ chainMetadata . loading , chainMetadata . processedData ] )
252+ } , [ chainMetadata . loading , chainMetadata . processedData , currentNetwork ] )
156253
157254 // Network selection handler
158255 function handleNetworkSelect ( chain : Chain ) {
@@ -304,7 +401,11 @@ export const FeedList = ({
304401 < StreamsNetworkAddressesTable />
305402 </ SectionWrapper >
306403
307- < SectionWrapper title = { streamsMainnetSectionTitle } depth = { 2 } >
404+ < SectionWrapper
405+ title = { streamsMainnetSectionTitle }
406+ depth = { 2 }
407+ idOverride = { streamsMainnetSectionTitle . toLowerCase ( ) . replace ( / \s + / g, "-" ) }
408+ >
308409 < div className = { feedList . tableFilters } >
309410 < form class = { feedList . filterDropdown_search } >
310411 < input
@@ -363,7 +464,11 @@ export const FeedList = ({
363464 ) }
364465 </ SectionWrapper >
365466
366- < SectionWrapper title = { streamsTestnetSectionTitle } depth = { 2 } >
467+ < SectionWrapper
468+ title = { streamsTestnetSectionTitle }
469+ depth = { 2 }
470+ idOverride = { streamsTestnetSectionTitle . toLowerCase ( ) . replace ( / \s + / g, "-" ) }
471+ >
367472 < div className = { feedList . tableFilters } >
368473 < form class = { feedList . filterDropdown_search } >
369474 < input
@@ -493,7 +598,12 @@ export const FeedList = ({
493598 . map ( ( network : ChainNetwork ) => {
494599 return (
495600 < >
496- < SectionWrapper title = { network . name } depth = { 3 } key = { network . name } >
601+ < SectionWrapper
602+ title = { network . name }
603+ depth = { 3 }
604+ key = { network . name }
605+ idOverride = { network . name . toLowerCase ( ) . replace ( / \s + / g, "-" ) }
606+ >
497607 { network . networkType === "mainnet" ? (
498608 < >
499609 { ! isStreams && chain . l2SequencerFeed && (
@@ -780,7 +890,14 @@ export const FeedList = ({
780890 { ! isDeprecating &&
781891 chainMetadata . processedData ?. testnetProcessedData &&
782892 chainMetadata . processedData ?. testnetProcessedData . length > 0 && (
783- < SectionWrapper title = { isStreams ? streamsTestnetSectionTitle : "Testnet Feeds" } depth = { 2 } updateTOC = { true } >
893+ < SectionWrapper
894+ title = { isStreams ? streamsTestnetSectionTitle : "Testnet Feeds" }
895+ depth = { 2 }
896+ updateTOC = { true }
897+ idOverride = {
898+ chainMetadata . processedData . testnetNetwork ?. name ?. toLowerCase ( ) . replace ( / \s + / g, "-" ) || "testnet-feeds"
899+ }
900+ >
784901 < div className = { feedList . tableFilters } >
785902 < form class = { feedList . filterDropdown_search } >
786903 < input
0 commit comments