feat(theme-common): JSDoc for all APIs (#6974)

* feat(theme-common): JSDoc for all APIs

* fix tests
This commit is contained in:
Joshua Chen 2022-03-23 21:39:19 +08:00 committed by GitHub
parent 4103fef11e
commit b456a64f61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 871 additions and 679 deletions

View file

@ -9,8 +9,14 @@ import {useState, useCallback, useRef} from 'react';
import {useLocationChange} from '../utils/useLocationChange';
import {useScrollPosition} from '../utils/scrollUtils';
/**
* Wires the imperative logic of a hideable navbar.
* @param hideOnScroll If `false`, this hook is basically a no-op.
*/
export function useHideableNavbar(hideOnScroll: boolean): {
/** A ref to the navbar component. Plug this into the actual element. */
readonly navbarRef: (node: HTMLElement | null) => void;
/** If `false`, the navbar component should not be rendered. */
readonly isNavbarVisible: boolean;
} {
const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll);
@ -29,7 +35,8 @@ export function useHideableNavbar(hideOnScroll: boolean): {
const scrollTop = currentPosition.scrollY;
// It needed for mostly to handle rubber band scrolling
// Needed mostly for handling rubber band scrolling.
// See https://github.com/facebook/docusaurus/pull/5721
if (scrollTop < navbarHeight.current) {
setIsNavbarVisible(true);
return;
@ -66,8 +73,5 @@ export function useHideableNavbar(hideOnScroll: boolean): {
setIsNavbarVisible(true);
});
return {
navbarRef,
isNavbarVisible,
};
return {navbarRef, isNavbarVisible};
}

View file

@ -12,7 +12,13 @@ import './styles.css';
export const keyboardFocusedClassName = 'navigation-with-keyboard';
/**
* Detect keyboard focus indicator to not show outline for mouse users
* Side-effect that adds the `keyboardFocusedClassName` to the body element when
* the keyboard has been pressed, or removes it when the mouse is clicked.
*
* The presence of this class name signals that the user may be using keyboard
* for navigation, and the theme **must** add focus outline when this class name
* is present. (And optionally not if it's absent, for design purposes)
*
* Inspired by https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
*/
export function useKeyboardNavigation(): void {

View file

@ -7,10 +7,13 @@
import {useEffect} from 'react';
/**
* Side-effect that locks the document body's scroll throughout the lifetime of
* the containing component. e.g. when the mobile sidebar is expanded.
*/
export function useLockBodyScroll(lock: boolean = true): void {
useEffect(() => {
document.body.style.overflow = lock ? 'hidden' : 'visible';
return () => {
document.body.style.overflow = 'visible';
};

View file

@ -9,6 +9,10 @@ import defaultTheme from 'prism-react-renderer/themes/palenight';
import {useColorMode} from '../contexts/colorMode';
import {useThemeConfig} from '../utils/useThemeConfig';
/**
* Returns a color-mode-dependent Prism theme: whatever the user specified in
* the config. Falls back to `palenight`.
*/
export function usePrismTheme(): typeof defaultTheme {
const {prism} = useThemeConfig();
const {colorMode} = useColorMode();

View file

@ -11,9 +11,22 @@ import {useCallback, useEffect, useState} from 'react';
const SEARCH_PARAM_QUERY = 'q';
/** Some utility functions around search queries. */
export function useSearchPage(): {
/**
* Works hand-in-hand with `setSearchQuery`; whatever the user has inputted
* into the search box.
*/
searchQuery: string;
/**
* Set a new search query. In addition to updating `searchQuery`, this handle
* also mutates the location and appends the query.
*/
setSearchQuery: (newSearchQuery: string) => void;
/**
* Given a query, this handle generates the corresponding search page link,
* with base URL prepended.
*/
generateSearchPageLink: (targetSearchQuery: string) => string;
} {
const history = useHistory();
@ -52,7 +65,9 @@ export function useSearchPage(): {
const generateSearchPageLink = useCallback(
(targetSearchQuery: string) =>
// Refer to https://github.com/facebook/docusaurus/pull/2838
`${baseUrl}search?q=${encodeURIComponent(targetSearchQuery)}`,
`${baseUrl}search?${SEARCH_PARAM_QUERY}=${encodeURIComponent(
targetSearchQuery,
)}`,
[baseUrl],
);

View file

@ -11,8 +11,10 @@ import {useThemeConfig} from '../utils/useThemeConfig';
// TODO make the hardcoded theme-classic classnames configurable (or add them
// to ThemeClassNames?)
// If the anchor has no height and is just a "marker" in the dom; we'll use the
// parent (normally the link text) rect boundaries instead
/**
* If the anchor has no height and is just a "marker" in the DOM; we'll use the
* parent (normally the link text) rect boundaries instead
*/
function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
const rect = element.getBoundingClientRect();
const hasNoHeight = rect.top === rect.bottom;
@ -24,7 +26,7 @@ function getVisibleBoundingClientRect(element: HTMLElement): DOMRect {
/**
* Considering we divide viewport into 2 zones of each 50vh, this returns true
* if an element is in the first zone (ie, appear in viewport, near the top)
* if an element is in the first zone (i.e., appear in viewport, near the top)
*/
function isInViewportTopHalf(boundingRect: DOMRect) {
return boundingRect.top > 0 && boundingRect.bottom < window.innerHeight / 2;
@ -114,12 +116,23 @@ function useAnchorTopOffsetRef() {
}
export type TOCHighlightConfig = {
/** A class name that all TOC links share. */
linkClassName: string;
/** The class name applied to the active (highlighted) link. */
linkActiveClassName: string;
/**
* The minimum heading level that the TOC includes. Only headings that are in
* this range will be eligible as "active heading".
*/
minHeadingLevel: number;
/** @see {@link TOCHighlightConfig.minHeadingLevel} */
maxHeadingLevel: number;
};
/**
* Side-effect that applies the active class name to the TOC heading that the
* user is currently viewing. Disabled when `config` is undefined.
*/
export function useTOCHighlight(config: TOCHighlightConfig | undefined): void {
const lastActiveLinkRef = useRef<HTMLAnchorElement | undefined>(undefined);

View file

@ -12,12 +12,6 @@ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
const windowSizes = {
desktop: 'desktop',
mobile: 'mobile',
// This "ssr" value is very important to handle hydration FOUC / layout shifts
// You have to handle server-rendering explicitly on the call-site
// On the server, you may need to render BOTH the mobile/desktop elements (and
// hide one of them with mediaquery)
// We don't return "undefined" on purpose, to make it more explicit
ssr: 'ssr',
} as const;
@ -34,13 +28,21 @@ function getWindowSize() {
: windowSizes.mobile;
}
// Simulate the SSR window size in dev, so that potential hydration FOUC/layout
// shift problems can be seen in dev too!
const DevSimulateSSR = process.env.NODE_ENV === 'development' && true;
// This hook returns an enum value on purpose!
// We don't want it to return the actual width value, for resize perf reasons
// We only want to re-render once a breakpoint is crossed
/**
* Gets the current window size as an enum value. We don't want it to return the
* actual width value, so that it only re-renders once a breakpoint is crossed.
*
* It may return `"ssr"`, which is very important to handle hydration FOUC or
* layout shifts. You have to handle it explicitly upfront. On the server, you
* may need to render BOTH the mobile/desktop elements (and hide one of them
* with mediaquery). We don't return `undefined` on purpose, to make it more
* explicit.
*
* In development mode, this hook will still return `"ssr"` for one second, to
* catch potential layout shifts, similar to strict mode calling effects twice.
*/
export function useWindowSize(): WindowSize {
const [windowSize, setWindowSize] = useState<WindowSize>(() => {
if (DevSimulateSSR) {