Skip to content

Commit

Permalink
fix: Fix anchor links to dynamic content (e.g. Markdown headings)
Browse files Browse the repository at this point in the history
A new hook, `useScrollToLocation()`, automatically detects anchor links and scrolls them into view when the target element appears.  This solves the problem of opening a deeplink to a specific anchor in a Markdown document, which is rendered asynchronously.

The hook has been added to the FileViewer, so it applies to any Insights, Markdown help page, etc.
  • Loading branch information
baumandm committed Mar 23, 2022
1 parent cc6934f commit a30d862
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/frontend/src/components/file-viewer/file-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Crumbs } from '../../components/crumbs/crumbs';
import { iconFactory, iconFactoryAs } from '../../shared/icon-factory';
import { getMimeTypeDefinition, MIME_VIEWER } from '../../shared/mime-utils';
import { getCompletePath } from '../../shared/url-utils';
import { useScrollToLocation } from '../../shared/use-scroll-to-location';
import { useFetch } from '../../shared/useFetch';
import { MarkdownContainer } from '../markdown-container/markdown-container';
import { CodeRenderer } from '../renderers/code-renderer/code-renderer';
Expand Down Expand Up @@ -132,6 +133,9 @@ export const FileViewer = ({
transformAssetUri,
...props
}: FileViewerProps & BoxProps) => {
// Scroll to anchor location if needed
useScrollToLocation();

const fileUrl = determineFileUrl(path, baseAssetUrl, transformAssetUri);

const downloadUrl = getCompletePath(baseAssetUrl, downloadPath || path, true);
Expand Down
56 changes: 56 additions & 0 deletions packages/frontend/src/shared/use-scroll-to-location.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2022 Expedia, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { useRef, useEffect } from 'react';
import { useLocation } from 'react-router-dom';

/**
* React Hook to scroll to an asynchronously-loaded anchor element.
*
* It uses the location hash directly (if set).
*
* This is required for dynamic content e.g. rendered Markdown
*
*/
export const useScrollToLocation = () => {
// Get hash from URL (if any)
const { hash } = useLocation();

// Track whether or not we've already scrolled to the location
const scrolledRef = useRef(false);

// Track when the hash changes
const hashRef = useRef(hash);

useEffect(() => {
if (hash) {
// Reset if hash changes
if (hashRef.current !== hash) {
hashRef.current = hash;
scrolledRef.current = false;
}

// Ensure we haven't already scrolled
if (!scrolledRef.current) {
const element = document.querySelector(hash);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
scrolledRef.current = true;
}
}
}
});
};

0 comments on commit a30d862

Please # to comment.