Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stash: api tree v2 #1954

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-deprecation": "^2.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-tailwindcss": "^3.13.1",
"eslint-plugin-vitest": "^0.3.26",
"execa": "^5.1.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"eslint-plugin-deprecation": "^2.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-vitest": "^0.3.26",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-tailwindcss": "^3.13.1"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,23 @@
"@fern-ui/components": "workspace:*",
"@fern-ui/fdr-utils": "workspace:*",
"@fern-ui/fern-docs-mdx": "workspace:*",
"@fern-ui/fern-docs-search-ui": "workspace:*",
"@fern-ui/fern-docs-server": "workspace:*",
"@fern-ui/fern-docs-syntax-highlighter": "workspace:*",
"@fern-ui/fern-docs-utils": "workspace:*",
"@fern-ui/loadable": "workspace:*",
"@fern-ui/next-seo": "workspace:*",
"@fern-ui/react-commons": "workspace:*",
"@fern-ui/search-utils": "workspace:*",
"@fern-ui/fern-docs-search-ui": "workspace:*",
"@inkeep/widgets": "^0.2.288",
"@next/third-parties": "14.2.9",
"@radix-ui/colors": "^3.0.0",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-collapsible": "^1.1.1",
"@radix-ui/react-compose-refs": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-primitive": "^2.0.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
Expand Down
33 changes: 33 additions & 0 deletions packages/ui/app/src/api-reference/endpoints/AnchorIdParts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { FernNavigation } from "@fern-api/fdr-sdk";
import { useDeepCompareMemoize } from "@fern-ui/react-commons";
import { PropsWithChildren, ReactElement, createContext, useContext } from "react";
import { getAnchorId } from "../../util/anchor";

const SlugContext = createContext<FernNavigation.Slug>(FernNavigation.Slug(""));
const AnchorIdPartsContext = createContext<string[]>([]);

export function SlugProvider({ slug, children }: PropsWithChildren<{ slug: FernNavigation.Slug }>): ReactElement {
return <SlugContext.Provider value={slug}>{children}</SlugContext.Provider>;
}

export function AnchorProvider({
children,
parts,
}: PropsWithChildren<{ parts: string | readonly string[] }>): ReactElement {
const parentParts = useContext(AnchorIdPartsContext);
const childParts = Array.isArray(parts) ? parts : [parts];
return (
<AnchorIdPartsContext.Provider value={useDeepCompareMemoize([...parentParts, ...childParts])}>
{children}
</AnchorIdPartsContext.Provider>
);
}

export function useSlug(): FernNavigation.Slug {
return useContext(SlugContext);
}

export function useAnchorId(): string {
const parts = useContext(AnchorIdPartsContext);
return getAnchorId(parts);
}
17 changes: 10 additions & 7 deletions packages/ui/app/src/api-reference/endpoints/Endpoint.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createEndpointContext, type ApiDefinition } from "@fern-api/fdr-sdk/api-definition";
import type * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { memo, useMemo } from "react";
import { SlugProvider } from "./AnchorIdParts";
import { EndpointContent } from "./EndpointContent";

export declare namespace Endpoint {
Expand Down Expand Up @@ -31,13 +32,15 @@ const UnmemoizedEndpoint: React.FC<Endpoint.Props> = ({
}

return (
<EndpointContent
breadcrumb={breadcrumb}
showErrors={showErrors}
context={context}
streamToggle={streamToggle}
last={last}
/>
<SlugProvider slug={node.slug}>
<EndpointContent
breadcrumb={breadcrumb}
showErrors={showErrors}
context={context}
streamToggle={streamToggle}
last={last}
/>
</SlugProvider>
);
};

Expand Down
272 changes: 141 additions & 131 deletions packages/ui/app/src/api-reference/endpoints/EndpointContentLeft.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import type { APIV1Read } from "@fern-api/fdr-sdk/client/types";
import * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils";
import { FernCollapse } from "@fern-ui/components";
import { AvailabilityBadge } from "@fern-ui/components/badges";
Expand All @@ -19,8 +18,6 @@ export declare namespace EndpointError {
isSelected: boolean;
onClick: MouseEventHandler<HTMLButtonElement>;
onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void;
anchorIdParts: readonly string[];
slug: FernNavigation.Slug;
availability: APIV1Read.Availability | null | undefined;
types: Record<string, ApiDefinition.TypeDefinition>;
}
Expand All @@ -33,8 +30,6 @@ export const EndpointError = memo<EndpointError.Props>(function EndpointErrorUnm
isSelected,
onHoverProperty,
onClick,
anchorIdParts,
slug,
availability,
types,
}) {
Expand Down Expand Up @@ -77,8 +72,6 @@ export const EndpointError = memo<EndpointError.Props>(function EndpointErrorUnm
applyErrorStyles
shape={error.shape}
onHoverProperty={onHoverProperty}
anchorIdParts={anchorIdParts}
slug={slug}
types={types}
isResponse={true}
/>
Expand Down
95 changes: 44 additions & 51 deletions packages/ui/app/src/api-reference/endpoints/EndpointParameter.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import type * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import type * as FernDocs from "@fern-api/fdr-sdk/docs";
import * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { EMPTY_ARRAY } from "@fern-api/ui-core-utils";
import { AvailabilityBadge } from "@fern-ui/components/badges";
import cn from "clsx";
import { CopyToClipboardButton, cn } from "@fern-ui/components";
import { AvailabilityBadge, Badge } from "@fern-ui/components/badges";
import { compact } from "es-toolkit/array";
import { FC, PropsWithChildren, ReactNode, memo, useEffect, useRef, useState } from "react";
import { FC, PropsWithChildren, ReactElement, ReactNode, useEffect, useRef, useState } from "react";
import { capturePosthogEvent } from "../../analytics/posthog";
import { useIsApiReferencePaginated, useRouteListener } from "../../atoms";
import { FernAnchor } from "../../components/FernAnchor";
import { useHref } from "../../hooks/useHref";
import { Markdown } from "../../mdx/Markdown";
import { renderTypeShorthandRoot } from "../../type-shorthand";
import { getAnchorId } from "../../util/anchor";
import { TypeReferenceDefinitions } from "../types/type-reference/TypeReferenceDefinitions";
import { useAnchorId, useSlug } from "./AnchorIdParts";

export declare namespace EndpointParameter {
export interface Props {
name: string;
description: FernDocs.MarkdownText | undefined;
additionalDescriptions: FernDocs.MarkdownText[] | undefined;
anchorIdParts: readonly string[];
slug: FernNavigation.Slug;
availability: ApiDefinition.Availability | null | undefined;
shape: ApiDefinition.TypeShape;
types: Record<string, ApiDefinition.TypeDefinition>;
Expand All @@ -32,57 +28,46 @@ export declare namespace EndpointParameter {
description: FernDocs.MarkdownText | undefined;
additionalDescriptions: FernDocs.MarkdownText[] | undefined;
typeShorthand: ReactNode;
anchorIdParts: readonly string[];
slug: FernNavigation.Slug;
availability: ApiDefinition.Availability | null | undefined;
}
}

export const EndpointParameter = memo<EndpointParameter.Props>(
({ name, description, additionalDescriptions, anchorIdParts, slug, shape, availability, types }) => (
<EndpointParameterContent
name={name}
description={description}
additionalDescriptions={additionalDescriptions}
typeShorthand={renderTypeShorthandRoot(shape, types, false)}
anchorIdParts={anchorIdParts}
slug={slug}
availability={availability}
>
<TypeReferenceDefinitions
shape={shape}
isCollapsible={true}
// onHoverProperty={onHoverProperty}
anchorIdParts={anchorIdParts}
slug={slug}
applyErrorStyles={false}
types={types}
/>
</EndpointParameterContent>
),
(prev, next) =>
prev.name === next.name &&
prev.description === next.description &&
prev.additionalDescriptions === next.additionalDescriptions &&
prev.slug === next.slug &&
prev.availability === next.availability &&
prev.shape === next.shape &&
prev.anchorIdParts.join("/") === next.anchorIdParts.join("/"),
export const EndpointParameter = ({
name,
description,
additionalDescriptions,
shape,
availability,
types,
}: EndpointParameter.Props): ReactElement => (
<EndpointParameterContent
name={name}
description={description}
additionalDescriptions={additionalDescriptions}
typeShorthand={renderTypeShorthandRoot(shape, types, false)}
availability={availability}
>
<TypeReferenceDefinitions
shape={shape}
isCollapsible={true}
// onHoverProperty={onHoverProperty}
applyErrorStyles={false}
types={types}
/>
</EndpointParameterContent>
);

EndpointParameter.displayName = "EndpointParameter";

export const EndpointParameterContent: FC<PropsWithChildren<EndpointParameter.ContentProps>> = ({
name,
anchorIdParts,
slug,
availability,
description,
additionalDescriptions = EMPTY_ARRAY,
typeShorthand,
children,
}) => {
const anchorId = getAnchorId(anchorIdParts);
const slug = useSlug();
const anchorId = useAnchorId();

const ref = useRef<HTMLDivElement>(null);

const [isActive, setIsActive] = useState(false);
Expand All @@ -97,15 +82,13 @@ export const EndpointParameterContent: FC<PropsWithChildren<EndpointParameter.Co
}
});

const href = useHref(slug, anchorId);
const descriptions = compact([description, ...additionalDescriptions]);

useEffect(() => {
if (descriptions.length > 0) {
capturePosthogEvent("api_reference_multiple_descriptions", {
name,
slug,
anchorIdParts,
href: String(new URL(`/${slug}#${anchorId}`, window.location.href)),
count: descriptions.length,
descriptions,
});
Expand All @@ -116,14 +99,24 @@ export const EndpointParameterContent: FC<PropsWithChildren<EndpointParameter.Co
return (
<div
ref={ref}
id={href}
id={anchorId}
className={cn("scroll-mt-content-padded relative flex flex-col gap-2 py-3", {
"outline-accent outline-1 outline outline-offset-4 rounded-sm": isActive,
})}
>
<FernAnchor href={href} sideOffset={6}>
<FernAnchor href={`#${anchorId}`} asChild>
<span className="inline-flex items-baseline gap-2">
<span className="fern-api-property-key">{name}</span>
<CopyToClipboardButton content={name} asChild hideIcon>
<Badge
className="fern-api-property-key -ml-2"
variant="ghost"
rounded
interactive
color="accent"
>
{name}
</Badge>
</CopyToClipboardButton>
{typeShorthand}
{availability != null && <AvailabilityBadge availability={availability} size="sm" rounded />}
</span>
Expand Down
Loading
Loading