Skip to content

Commit

Permalink
improvement: use local storage to keep track of the selected fern-cli…
Browse files Browse the repository at this point in the history
…ent (#274)

* add local storage hook

* fern-client

* event listener

* using jotai to keep track of client

* remove console log

* remove additional lazy loading
  • Loading branch information
abvthecity authored Dec 2, 2023
1 parent 2b76032 commit 1e1434a
Show file tree
Hide file tree
Showing 19 changed files with 88 additions and 74 deletions.
23 changes: 23 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
8 changes: 4 additions & 4 deletions packages/commons/react/theme/src/useTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { useEffect, useMemo } from "react";
import { type Theme } from "./theme";

interface UseThemeReturnType {
theme: Theme | undefined;
theme: Theme;
setTheme: (theme: Theme) => void;
}

export function useTheme(colorConfigType: "dark" | "light" | "darkAndLight"): UseThemeReturnType {
const { setTheme, theme } = _useTheme();
const { setTheme, theme, resolvedTheme } = _useTheme();

useEffect(() => {
if (colorConfigType !== "darkAndLight") {
Expand All @@ -21,6 +21,6 @@ export function useTheme(colorConfigType: "dark" | "light" | "darkAndLight"): Us
return { theme: colorConfigType, setTheme };
}

return { theme, setTheme } as UseThemeReturnType;
}, [colorConfigType, setTheme, theme]);
return { theme: theme ?? resolvedTheme ?? "dark", setTheme } as UseThemeReturnType;
}, [colorConfigType, resolvedTheme, setTheme, theme]);
}
1 change: 1 addition & 0 deletions packages/ui/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"algoliasearch": "^4.18.0",
"classnames": "^2.3.1",
"instantsearch.js": "^4.56.3",
"jotai": "^2.6.0",
"lodash-es": "^4.17.21",
"lru-cache": "^10.0.1",
"marked": "^5.0.4",
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/app/src/api-page/artifacts/ApiArtifacts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { DocsV1Read } from "@fern-api/fdr-sdk";
import { joinUrlSlugs } from "@fern-ui/app-utils";
import { useApiDefinitionContext } from "../../api-context/useApiDefinitionContext";
import { API_ARTIFACTS_TITLE } from "../../config";
import { HEADER_HEIGHT } from "../../constants";
import { ApiPageMargins } from "../page-margins/ApiPageMargins";
import { useApiPageCenterElement } from "../useApiPageCenterElement";
import { DotNetLogo } from "./sdk-logos/DotNetLogo";
Expand All @@ -28,7 +27,7 @@ export const ApiArtifacts: React.FC<ApiArtifacts.Props> = ({ apiArtifacts }) =>

return (
<ApiPageMargins>
<div ref={setTargetRef} data-route={`/${slug}`} style={{ scrollMarginTop: HEADER_HEIGHT }}>
<div ref={setTargetRef} data-route={`/${slug}`} className="scroll-mt-16">
<H2 className="pt-20">{API_ARTIFACTS_TITLE}</H2>
<div className="t-muted mt-5 text-lg">
Official open-source client libraries for your favorite platforms.
Expand Down
25 changes: 20 additions & 5 deletions packages/ui/app/src/api-page/endpoints/EndpointContent.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { APIV1Read } from "@fern-api/fdr-sdk";
import classNames from "classnames";
import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import React, { useCallback, useMemo, useState } from "react";
import { useInView } from "react-intersection-observer";
import { useApiDefinitionContext } from "../../api-context/useApiDefinitionContext";
import { HEADER_HEIGHT } from "../../constants";
import { useDocsContext } from "../../docs-context/useDocsContext";
import { useNavigationContext } from "../../navigation-context";
import { useViewportContext } from "../../viewport-context/useViewportContext";
import { type CodeExampleClient } from "../examples/code-example";
import { getCurlLines } from "../examples/curl-example/curlUtils";
Expand Down Expand Up @@ -69,6 +72,8 @@ function getAvailableExampleClients(example: APIV1Read.ExampleEndpointCall): Cod
return clients;
}

const fernClientAtom = atomWithStorage<CodeExampleClient>("fern-client", DEFAULT_CLIENT);

export const EndpointContent: React.FC<EndpointContent.Props> = ({
endpoint,
package: package_,
Expand All @@ -78,6 +83,7 @@ export const EndpointContent: React.FC<EndpointContent.Props> = ({
route,
}) => {
const { layoutBreakpoint, viewportSize } = useViewportContext();
const { navigateToPath } = useNavigationContext();
const [isInViewport, setIsInViewport] = useState(false);
const { ref: containerRef } = useInView({
onChange: setIsInViewport,
Expand All @@ -100,9 +106,19 @@ export const EndpointContent: React.FC<EndpointContent.Props> = ({
[setHoveredResponsePropertyPath]
);

const [selectedExampleClient, setSelectedExampleClient] = useState<CodeExampleClient>(DEFAULT_CLIENT);
const [storedSelectedExampleClient, setSelectedExampleClient] = useAtom(fernClientAtom);
const [selectedErrorIndex, setSelectedErrorIndex] = useState<number | null>(null);

const selectedExampleClient = storedSelectedExampleClient ?? DEFAULT_CLIENT;

const setSelectedExampleClientAndScrollToTop = useCallback(
(nextClient: CodeExampleClient) => {
setSelectedExampleClient(nextClient);
navigateToPath(route.substring(1));
},
[navigateToPath, route, setSelectedExampleClient]
);

const errors = useMemo(() => {
return [...(endpoint.errorsV2 ?? [])]
.sort((e1, e2) => (e1.name != null && e2.name != null ? e1.name.localeCompare(e2.name) : 0))
Expand Down Expand Up @@ -178,17 +194,16 @@ export const EndpointContent: React.FC<EndpointContent.Props> = ({

return (
<div
className={classNames("pb-20 pl-6 md:pl-12 pr-4", {
className={classNames("pb-20 pl-6 md:pl-12 pr-4 scroll-mt-16", {
"border-border-default-light dark:border-border-default-dark border-b": !hideBottomSeparator,
})}
onClick={() => setSelectedErrorIndex(null)}
ref={containerRef}
>
<div
className="flex min-w-0 flex-1 flex-col justify-between lg:flex-row lg:space-x-[4vw]"
className="flex min-w-0 flex-1 scroll-mt-16 flex-col justify-between lg:flex-row lg:space-x-[4vw]"
ref={setContainerRef}
data-route={route}
style={{ scrollMarginTop: HEADER_HEIGHT }}
>
<div
className="flex min-w-0 max-w-2xl flex-1 flex-col"
Expand Down Expand Up @@ -228,7 +243,7 @@ export const EndpointContent: React.FC<EndpointContent.Props> = ({
example={example}
availableExampleClients={availableExampleClients}
selectedExampleClient={selectedExampleClient}
onClickExampleClient={setSelectedExampleClient}
onClickExampleClient={setSelectedExampleClientAndScrollToTop}
requestCurlLines={curlLines}
responseJsonLines={jsonLines}
hoveredRequestPropertyPath={hoveredRequestPropertyPath}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";
import { APIV1Read } from "@fern-api/fdr-sdk";
import { type Theme } from "@fern-ui/theme";
import dynamic from "next/dynamic";
import { memo } from "react";
import { CodeBlockSkeleton } from "../../commons/CodeBlockSkeleton";
import type { CodeExampleClient } from "../examples/code-example";
import { CurlExample } from "../examples/curl-example/CurlExample";
import { CurlLine, curlLinesToString } from "../examples/curl-example/curlUtils";
Expand All @@ -12,11 +12,6 @@ import { JsonLine } from "../examples/json-example/jsonLineUtils";
import { TitledExample } from "../examples/TitledExample";
import { CodeExampleClientDropdown } from "./CodeExampleClientDropdown";

const CodeBlockSkeleton = dynamic(
() => import("../../commons/CodeBlockSkeleton").then(({ CodeBlockSkeleton }) => CodeBlockSkeleton),
{ ssr: false }
);

export declare namespace EndpointContentCodeSnippets {
export interface Props {
theme?: Theme;
Expand Down
7 changes: 1 addition & 6 deletions packages/ui/app/src/api-page/endpoints/EndpointParameter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { APIV1Read } from "@fern-api/fdr-sdk";
import { AbsolutelyPositionedAnchor } from "../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../commons/monospace/MonospaceText";
import { HEADER_HEIGHT } from "../../constants";
import { getAnchorId } from "../../util/anchor";
import { ApiPageDescription } from "../ApiPageDescription";
import { TypeReferenceDefinitions } from "../types/type-reference/TypeReferenceDefinitions";
Expand All @@ -28,11 +27,7 @@ export const EndpointParameter: React.FC<EndpointParameter.Props> = ({
const anchorId = getAnchorId(anchorIdParts);
const anchorRoute = `${route}#${anchorId}`;
return (
<div
data-route={anchorRoute}
className="group/anchor-container relative flex flex-col gap-2 py-3"
style={{ scrollMarginTop: HEADER_HEIGHT }}
>
<div data-route={anchorRoute} className="group/anchor-container relative flex scroll-mt-16 flex-col gap-2 py-3">
<AbsolutelyPositionedAnchor verticalPosition="default" href={anchorRoute} />
<div className="flex items-baseline gap-1">
<MonospaceText className="text-text-primary-light dark:text-text-primary-dark">{name}</MonospaceText>
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/app/src/api-page/endpoints/EndpointSection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AbsolutelyPositionedAnchor } from "../../commons/AbsolutelyPositionedAnchor";
import { HEADER_HEIGHT } from "../../constants";
import { getAnchorId } from "../../util/anchor";
import { Markdown } from "../markdown/Markdown";

Expand All @@ -22,7 +21,7 @@ export const EndpointSection: React.FC<EndpointSection.Props> = ({
const anchorId = getAnchorId(anchorIdParts);
const anchorRoute = `${route}#${anchorId}`;
return (
<div data-route={anchorRoute} className="flex flex-col" style={{ scrollMarginTop: HEADER_HEIGHT }}>
<div data-route={anchorRoute} className="flex scroll-mt-16 flex-col">
<div className="group/anchor-container relative mb-3 flex items-center">
<AbsolutelyPositionedAnchor href={anchorRoute} verticalPosition="center" />
<div className="text-text-primary-light dark:text-text-primary-dark text-xl font-extrabold">
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/app/src/api-page/subpackages/ApiSubpackage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { APIV1Read } from "@fern-api/fdr-sdk";
import { useApiDefinitionContext } from "../../api-context/useApiDefinitionContext";
import { HEADER_HEIGHT } from "../../constants";
import { ApiPackageContents } from "../ApiPackageContents";
import { ApiPageMargins } from "../page-margins/ApiPageMargins";
import { useApiPageCenterElement } from "../useApiPageCenterElement";
Expand All @@ -26,7 +25,7 @@ export const ApiSubpackage: React.FC<ApiSubpackage.Props> = ({
return (
<>
<ApiPageMargins>
<div ref={setTargetRef} data-route={`/${slug}`} style={{ scrollMarginTop: HEADER_HEIGHT }} />
<div ref={setTargetRef} data-route={`/${slug}`} className="scroll-mt-16" />
</ApiPageMargins>
<ApiPackageContents
key={subpackageId}
Expand Down
4 changes: 1 addition & 3 deletions packages/ui/app/src/api-page/types/object/ObjectProperty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useCallback, useMemo } from "react";
import { useApiDefinitionContext } from "../../../api-context/useApiDefinitionContext";
import { AbsolutelyPositionedAnchor } from "../../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../../commons/monospace/MonospaceText";
import { HEADER_HEIGHT } from "../../../constants";
import { getAnchorId } from "../../../util/anchor";
import { ApiPageDescription } from "../../ApiPageDescription";
import { JsonPropertyPath } from "../../examples/json-example/contexts/JsonPropertyPath";
Expand Down Expand Up @@ -103,10 +102,9 @@ export const ObjectProperty: React.FC<ObjectProperty.Props> = ({
return (
<div
data-route={anchorRoute}
className={classNames("flex relative flex-col py-3", {
className={classNames("flex relative flex-col py-3 scroll-mt-16", {
"px-3": !contextValue.isRootTypeDefinition,
})}
style={{ scrollMarginTop: HEADER_HEIGHT }}
>
<div className="flex items-baseline gap-2">
<div className="group/anchor-container relative">
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/app/src/api-page/webhooks/WebhookContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ export const WebhookContent = React.memo<WebhookContent.Props>(function WebhookC
})}
>
<div
className="flex min-w-0 flex-1 flex-col lg:flex-row lg:space-x-[4vw]"
className="flex min-w-0 flex-1 scroll-mt-16 flex-col lg:flex-row lg:space-x-[4vw]"
ref={setContainerRef}
data-route={route}
style={{ scrollMarginTop: HEADER_HEIGHT }}
>
<div className="flex min-w-0 max-w-2xl flex-1 flex-col">
<div className="py-8">
Expand Down
9 changes: 1 addition & 8 deletions packages/ui/app/src/api-page/webhooks/WebhookSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Url } from "next/dist/shared/lib/router/router";
import { resolveHref } from "next/dist/shared/lib/router/utils/resolve-href";
import { useRouter } from "next/router";
import { AbsolutelyPositionedAnchor } from "../../commons/AbsolutelyPositionedAnchor";
import { HEADER_HEIGHT } from "../../constants";
import { Markdown } from "../markdown/Markdown";

export declare namespace WebhookSection {
Expand All @@ -15,13 +14,7 @@ export declare namespace WebhookSection {

export const WebhookSection: React.FC<WebhookSection.Props> = ({ title, description, href, children }) => {
return (
<div
data-route={resolveHref(useRouter(), href)}
className="flex flex-col"
style={{
scrollMarginTop: HEADER_HEIGHT,
}}
>
<div data-route={resolveHref(useRouter(), href)} className="flex scroll-mt-16 flex-col">
<div className="group/anchor-container relative mb-3 flex items-center">
<AbsolutelyPositionedAnchor href={href} verticalPosition="center" />
<div className="text-text-primary-light dark:text-text-primary-dark text-xl font-extrabold">
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/app/src/contexts.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { ThemeProviderWithLayout } from "@fern-ui/theme";
import { createStore, Provider as JotaiProvider } from "jotai";
import { PropsWithChildren, ReactElement } from "react";
import { CacheContextProvider } from "./cache-context/CacheContextProvider";
import { MobileSidebarContextProvider } from "./mobile-sidebar-context/MobileSidebarContextProvider";
import { SearchContextProvider } from "./search-context/SearchContextProvider";
import { ViewportContextProvider } from "./viewport-context/ViewportContextProvider";

const store = createStore();

export const CONTEXTS = [
({ children }: PropsWithChildren): ReactElement => <JotaiProvider store={store}>{children}</JotaiProvider>,
({ children }: PropsWithChildren): ReactElement => (
<MobileSidebarContextProvider>{children}</MobileSidebarContextProvider>
),
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/docs-context/DocsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export interface DocsContextValue {
resolvePage: (pageId: DocsV1Read.PageId) => DocsV1Read.PageContent;
resolveFile: (fileId: DocsV1Read.FileId) => DocsV1Read.Url;

theme: Theme | undefined;
theme: Theme;
setTheme: (theme: Theme) => void;
}
4 changes: 0 additions & 4 deletions packages/ui/app/src/docs/HeaderLogoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@ export const HeaderLogoSection: React.FC = () => {
const { definitionInfo, activeVersionContext } = useDocsSelectors();
const { logo, logoV2, logoHeight, logoHref } = docsDefinition.config;

if (theme == null) {
return null;
}

const logoForTheme = logoV2 != null ? logoV2[theme] : logo;
const hasMultipleVersions = definitionInfo.type === "versioned";
const activeVersionId =
Expand Down
Loading

1 comment on commit 1e1434a

@vercel
Copy link

@vercel vercel bot commented on 1e1434a Dec 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

fern-dev – ./packages/ui/fe-bundle

fern-dev-buildwithfern.vercel.app
app-dev.buildwithfern.com
devtest.buildwithfern.com
fern-dev-git-main-buildwithfern.vercel.app

Please sign in to comment.