diff --git a/web/src/components/MarkdownViewer/MarkdownViewer.tsx b/web/src/components/MarkdownViewer/MarkdownViewer.tsx index b1e16dbd6..36409b484 100644 --- a/web/src/components/MarkdownViewer/MarkdownViewer.tsx +++ b/web/src/components/MarkdownViewer/MarkdownViewer.tsx @@ -1,5 +1,5 @@ import { useHistory } from 'react-router-dom' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import { Container } from '@harness/uicore' import cx from 'classnames' import MarkdownPreview from '@uiw/react-markdown-preview' @@ -19,9 +19,8 @@ export function MarkdownViewer({ source, className, maxHeight }: MarkdownViewerP const [isOpen, setIsOpen] = useState(false) const history = useHistory() const [zoomLevel, setZoomLevel] = useState(INITIAL_ZOOM_LEVEL) - const [imgEvent, setImageEvent] = useState([]) - + const refRootHref = useMemo(() => document.getElementById('repository-ref-root')?.getAttribute('href'), []) const interceptClickEventOnViewerContainer = useCallback( event => { const { target } = event @@ -31,19 +30,26 @@ export function MarkdownViewer({ source, className, maxHeight }: MarkdownViewerP const imageSrc = string.split('![image]')[1] return imageSrc.slice(1, imageSrc.length - 1) }) + setImageEvent(imageStringArray) + if (target?.tagName?.toLowerCase() === 'a') { - const { href } = target + const href = target.getAttribute('href') // Intercept click event on internal links and navigate to pages to avoid full page reload - if (href && !href.startsWith('#')) { + if (href) { try { - const url = new URL(href) + const url = new URL(target.href) if (url.origin === window.location.origin) { event.stopPropagation() event.preventDefault() - history.push(url.pathname) + + if (href.startsWith('#')) { + document.getElementById(href.slice(1))?.scrollIntoView() + } else { + history.push(url.pathname) + } } } catch (e) { // eslint-disable-next-line no-console @@ -71,6 +77,43 @@ export function MarkdownViewer({ source, className, maxHeight }: MarkdownViewerP if (parent && /^h(1|2|3|4|5|6)/.test((parent as unknown as HTMLDivElement).tagName)) { parent.children = parent.children.slice(1) } + + // Rewrite a.href to point to the correct location for relative links to files inside repository. + // Relative links are defined as links that do not start with /, #, https:, http:, mailto:, + // tel:, data:, javascript:, sms:, or http(s): + if (refRootHref) { + const { properties } = node as unknown as { properties: { href: string } } + let href: string = properties.href + + if ( + href && + !href.startsWith('/') && + !href.startsWith('#') && + !href.startsWith('https:') && + !href.startsWith('http:') && + !href.startsWith('mailto:') && + !href.startsWith('tel:') && + !href.startsWith('data:') && + !href.startsWith('javascript:') && + !href.startsWith('sms:') && + !/^http(s)?:/.test(href) + ) { + try { + // Some relative links are prefixed by `./`, normalize them + if (href.startsWith('./')) { + href = properties.href = properties.href.replace('./', '') + } + + // Test if the link is relative to the current page. + // If true, rewrite it to point to the correct location + if (new URL(window.location.href + '/' + href).origin === window.location.origin) { + properties.href = (refRootHref + '/~/' + href).replace(/^\/ng\//, '/') + } + } catch (_exception) { + // eslint-disable-line no-empty + } + } + } } }} rehypePlugins={[ diff --git a/web/src/pages/Repository/RepositoryContent/ContentHeader/ContentHeader.tsx b/web/src/pages/Repository/RepositoryContent/ContentHeader/ContentHeader.tsx index 9d235af26..8048ff7ec 100644 --- a/web/src/pages/Repository/RepositoryContent/ContentHeader/ContentHeader.tsx +++ b/web/src/pages/Repository/RepositoryContent/ContentHeader/ContentHeader.tsx @@ -68,7 +68,9 @@ export function ContentHeader({ /> - + / diff --git a/web/src/pages/Repository/RepositoryContent/FileContent/FileContent.module.scss b/web/src/pages/Repository/RepositoryContent/FileContent/FileContent.module.scss index b89a46787..5ee73659b 100644 --- a/web/src/pages/Repository/RepositoryContent/FileContent/FileContent.module.scss +++ b/web/src/pages/Repository/RepositoryContent/FileContent/FileContent.module.scss @@ -8,8 +8,6 @@ .heading { box-shadow: 0px 0px 1px rgba(40, 41, 61, 0.08), 0px 0.5px 2px rgba(96, 97, 112, 0.16); - // border-top: 1px solid var(--grey-200); - // border-bottom: 1px solid var(--grey-200); align-items: center; padding-left: var(--spacing-large) !important; background-color: var(--grey-100); diff --git a/web/src/pages/Repository/RepositoryContent/RepositoryContent.tsx b/web/src/pages/Repository/RepositoryContent/RepositoryContent.tsx index f983c84db..568bf08a4 100644 --- a/web/src/pages/Repository/RepositoryContent/RepositoryContent.tsx +++ b/web/src/pages/Repository/RepositoryContent/RepositoryContent.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect } from 'react' import { Container } from '@harness/uicore' import { GitInfoProps, isDir } from 'utils/GitUtils' import { ContentHeader } from './ContentHeader/ContentHeader' @@ -12,6 +12,10 @@ export function RepositoryContent({ resourcePath, resourceContent }: Pick) { + useEffect(() => { + window.scroll({ top: 0 }) + }, [gitRef, resourcePath]) + return (