diff --git a/package.json b/package.json index e962b312d6..abe50a47fd 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/eslint/package.json b/packages/eslint/package.json index ea4fd43161..bde0a3252d 100644 --- a/packages/eslint/package.json +++ b/packages/eslint/package.json @@ -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": { diff --git a/packages/ui/app/package.json b/packages/ui/app/package.json index 56a5be6c10..976080b8bb 100644 --- a/packages/ui/app/package.json +++ b/packages/ui/app/package.json @@ -42,6 +42,7 @@ "@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:*", @@ -49,14 +50,15 @@ "@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", diff --git a/packages/ui/app/src/api-reference/endpoints/AnchorIdParts.tsx b/packages/ui/app/src/api-reference/endpoints/AnchorIdParts.tsx new file mode 100644 index 0000000000..1dd39d02d4 --- /dev/null +++ b/packages/ui/app/src/api-reference/endpoints/AnchorIdParts.tsx @@ -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("")); +const AnchorIdPartsContext = createContext([]); + +export function SlugProvider({ slug, children }: PropsWithChildren<{ slug: FernNavigation.Slug }>): ReactElement { + return {children}; +} + +export function AnchorProvider({ + children, + parts, +}: PropsWithChildren<{ parts: string | readonly string[] }>): ReactElement { + const parentParts = useContext(AnchorIdPartsContext); + const childParts = Array.isArray(parts) ? parts : [parts]; + return ( + + {children} + + ); +} + +export function useSlug(): FernNavigation.Slug { + return useContext(SlugContext); +} + +export function useAnchorId(): string { + const parts = useContext(AnchorIdPartsContext); + return getAnchorId(parts); +} diff --git a/packages/ui/app/src/api-reference/endpoints/Endpoint.tsx b/packages/ui/app/src/api-reference/endpoints/Endpoint.tsx index 7133c624a6..8cd2ff1256 100644 --- a/packages/ui/app/src/api-reference/endpoints/Endpoint.tsx +++ b/packages/ui/app/src/api-reference/endpoints/Endpoint.tsx @@ -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 { @@ -31,13 +32,15 @@ const UnmemoizedEndpoint: React.FC = ({ } return ( - + + + ); }; diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointContentLeft.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointContentLeft.tsx index 70583e0119..733ebc0c88 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointContentLeft.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointContentLeft.tsx @@ -8,11 +8,12 @@ import { useFeatureFlags } from "../../atoms"; import { Markdown } from "../../mdx/Markdown"; import { JsonPropertyPath } from "../examples/JsonPropertyPath"; import { TypeComponentSeparator } from "../types/TypeComponentSeparator"; +import { AnchorProvider } from "./AnchorIdParts"; import { EndpointError } from "./EndpointError"; import { EndpointParameter } from "./EndpointParameter"; import { EndpointRequestSection } from "./EndpointRequestSection"; import { EndpointResponseSection } from "./EndpointResponseSection"; -import { EndpointSection } from "./EndpointSection"; +import { EndpointSection, EndpointSectionTitle } from "./EndpointSection"; export interface HoveringProps { isHovering: boolean; @@ -32,17 +33,17 @@ export declare namespace EndpointContentLeft { } } -const REQUEST = ["request"]; -const RESPONSE = ["response"]; -const REQUEST_PATH = ["request", "path"]; -const REQUEST_QUERY = ["request", "query"]; -const REQUEST_HEADER = ["request", "header"]; -const REQUEST_BODY = ["request", "body"]; -const RESPONSE_BODY = ["response", "body"]; -const RESPONSE_ERROR = ["response", "error"]; +// const REQUEST = ["request"]; +// const RESPONSE = ["response"]; +// const REQUEST_PATH = ["request", "path"]; +// const REQUEST_QUERY = ["request", "query"]; +// const REQUEST_HEADER = ["request", "header"]; +// const REQUEST_BODY = ["request", "body"]; +// const RESPONSE_BODY = ["response", "body"]; +// const RESPONSE_ERROR = ["response", "error"]; const UnmemoizedEndpointContentLeft: React.FC = ({ - context: { node, endpoint, types, auth, globalHeaders }, + context: { endpoint, types, auth, globalHeaders }, example, showErrors, onHoverRequestProperty, @@ -157,55 +158,84 @@ const UnmemoizedEndpointContentLeft: React.FC = ({
{endpoint.pathParameters && endpoint.pathParameters.length > 0 && ( - -
- {endpoint.pathParameters.map((parameter) => ( -
- - -
- ))} -
-
+ + + Path parameters +
+ {endpoint.pathParameters.map((parameter) => ( + + + + + ))} +
+
+
)} {headers.length > 0 && ( - -
- {headers.map((parameter) => { - let isAuth = false; - if ( - (auth?.type === "header" && parameter.key === auth?.headerWireValue) || - parameter.key === "Authorization" - ) { - isAuth = true; - } + + + Headers +
+ {headers.map((parameter) => { + let isAuth = false; + if ( + (auth?.type === "header" && parameter.key === auth?.headerWireValue) || + parameter.key === "Authorization" + ) { + isAuth = true; + } - return ( -
- {isAuth && ( -
-
- Auth -
+ return ( + +
+ {isAuth && ( +
+
+ Auth +
+
+ )} + +
- )} +
+ ); + })} +
+ + + )} + {endpoint.queryParameters && endpoint.queryParameters.length > 0 && ( + + + Query parameters +
+ {endpoint.queryParameters.map((parameter) => ( + = ({ availability={parameter.availability} types={types} /> -
- ); - })} -
- - )} - {endpoint.queryParameters && endpoint.queryParameters.length > 0 && ( - -
- {endpoint.queryParameters.map((parameter) => ( -
- - -
- ))} -
-
+ + ))} +
+
+
)} {/* {endpoint.requestBody.length > 1 && ( @@ -270,10 +277,10 @@ const UnmemoizedEndpointContentLeft: React.FC = ({ + Request = ({ )} */} {endpoint.request && ( - - - + + + Request + + + + + )} {endpoint.response && ( - - - + + + Response + + + + + )} {showErrors && endpoint.errors && endpoint.errors.length > 0 && ( - -
- {sortBy(endpoint.errors, [(e) => e.statusCode, (e) => e.name]).map((error, idx) => { - return ( - { - event.stopPropagation(); - setSelectedError(error); - }} - onHoverProperty={onHoverResponseProperty} - anchorIdParts={[ - ...RESPONSE_ERROR, - `${convertNameToAnchorPart(error.name) ?? error.statusCode}`, - ]} - slug={node.slug} - availability={error.availability} - types={types} - /> - ); - })} -
-
+ + + Errors +
+ {sortBy(endpoint.errors, [(e) => e.statusCode, (e) => e.name]).map((error, idx) => { + return ( + + { + event.stopPropagation(); + setSelectedError(error); + }} + onHoverProperty={onHoverResponseProperty} + availability={error.availability} + types={types} + /> + + ); + })} +
+
+
)}
); diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointError.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointError.tsx index 94f306b637..9b0b36c877 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointError.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointError.tsx @@ -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"; @@ -19,8 +18,6 @@ export declare namespace EndpointError { isSelected: boolean; onClick: MouseEventHandler; onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; availability: APIV1Read.Availability | null | undefined; types: Record; } @@ -33,8 +30,6 @@ export const EndpointError = memo(function EndpointErrorUnm isSelected, onHoverProperty, onClick, - anchorIdParts, - slug, availability, types, }) { @@ -77,8 +72,6 @@ export const EndpointError = memo(function EndpointErrorUnm applyErrorStyles shape={error.shape} onHoverProperty={onHoverProperty} - anchorIdParts={anchorIdParts} - slug={slug} types={types} isResponse={true} /> diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointParameter.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointParameter.tsx index 5383cec85f..744e2e5461 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointParameter.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointParameter.tsx @@ -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; @@ -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( - ({ name, description, additionalDescriptions, anchorIdParts, slug, shape, availability, types }) => ( - - - - ), - (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 => ( + + + ); -EndpointParameter.displayName = "EndpointParameter"; - export const EndpointParameterContent: FC> = ({ name, - anchorIdParts, - slug, availability, description, additionalDescriptions = EMPTY_ARRAY, typeShorthand, children, }) => { - const anchorId = getAnchorId(anchorIdParts); + const slug = useSlug(); + const anchorId = useAnchorId(); + const ref = useRef(null); const [isActive, setIsActive] = useState(false); @@ -97,15 +82,13 @@ export const EndpointParameterContent: FC { 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, }); @@ -116,14 +99,24 @@ export const EndpointParameterContent: FC - + - {name} + + + {name} + + {typeShorthand} {availability != null && } diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointRequestSection.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointRequestSection.tsx index 944246f640..114e9e83dc 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointRequestSection.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointRequestSection.tsx @@ -1,129 +1,133 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; -import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; -import cn from "clsx"; +import { cn } from "@fern-ui/components"; import { Fragment, ReactNode } from "react"; import { Markdown } from "../../mdx/Markdown"; import { renderTypeShorthand } from "../../type-shorthand"; import { JsonPropertyPath } from "../examples/JsonPropertyPath"; import { TypeComponentSeparator } from "../types/TypeComponentSeparator"; import { TypeReferenceDefinitions } from "../types/type-reference/TypeReferenceDefinitions"; +import { AnchorProvider } from "./AnchorIdParts"; import { EndpointParameter, EndpointParameterContent } from "./EndpointParameter"; -export declare namespace EndpointRequestSection { - export interface Props { - request: ApiDefinition.HttpRequest; - onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; - types: Record; - } -} - -export const EndpointRequestSection: React.FC = ({ +const EndpointRequestSection = ({ request, onHoverProperty, - anchorIdParts, - slug, types, -}) => { +}: { + request: ApiDefinition.HttpRequest; + onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; + types: Record; +}): ReactNode => { return ( -
+ <> ({ - formData: (formData) => { - const fileArrays = formData.fields.filter( - (p): p is ApiDefinition.FormDataField.Files => p.type === "files", - ); - const files = formData.fields.filter( - (p): p is ApiDefinition.FormDataField.File_ => p.type === "file", - ); - return `a multipart form${fileArrays.length > 0 || files.length > 1 ? " with multiple files" : files[0] != null ? ` containing ${files[0].isOptional ? "an optional" : "a"} file` : ""}`; - }, - bytes: (bytes) => `binary data${bytes.contentType != null ? ` of type ${bytes.contentType}` : ""}`, - object: (obj) => renderTypeShorthand(obj, { withArticle: true }, types), - alias: (alias) => renderTypeShorthand(alias, { withArticle: true }, types), - })}.`} + fallback={createRequestBodyDescriptionFallback(request.body, types)} /> - {visitDiscriminatedUnion(request.body)._visit({ - formData: (formData) => - formData.fields.map((p) => ( - - - {visitDiscriminatedUnion(p, "type")._visit({ - file: (file) => ( - - ), - files: (files) => ( - - ), - property: (property) => ( - - ), - _other: () => null, - })} - - )), - bytes: () => null, - object: (obj) => ( - - ), - alias: (alias) => ( - - ), - })} -
+ + ); }; +const EndpointRequestBody = ({ + body, + types, + onHoverProperty, +}: { + body: ApiDefinition.HttpRequestBodyShape; + types: Record; + onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; +}): ReactNode => { + switch (body.type) { + case "formData": + return ; + case "bytes": + return false; + case "alias": + case "object": + return ( + + ); + default: + return false; + } +}; + +const EndpointRequestBodyFormData = ({ + body, + types, +}: { + body: ApiDefinition.HttpRequestBodyShape.FormData; + types: Record; +}) => { + return ( + <> + {body.fields.map((field) => ( + + + + + + + ))} + + ); +}; + +const EndpointRequestBodyFormDataField = ({ + field, + types, +}: { + field: ApiDefinition.FormDataField; + types: Record; +}) => { + switch (field.type) { + case "file": + return ( + + ); + case "files": + return ( + + ); + case "property": + return ( + + ); + default: + return null; + } +}; + function renderTypeShorthandFormDataField( property: Exclude, ): ReactNode { @@ -134,3 +138,25 @@ function renderTypeShorthandFormDataField( ); } + +function createRequestBodyDescriptionFallback( + body: ApiDefinition.HttpRequestBodyShape, + types: Record, +): ReactNode { + switch (body.type) { + case "formData": { + const fileArrays = body.fields.filter((p): p is ApiDefinition.FormDataField.Files => p.type === "files"); + const files = body.fields.filter((p): p is ApiDefinition.FormDataField.File_ => p.type === "file"); + return `This endpoint expects a multipart form${fileArrays.length > 0 || files.length > 1 ? " with multiple files" : files[0] != null ? ` containing ${files[0].isOptional ? "an optional" : "a"} file` : ""}.`; + } + case "bytes": + return `This endpoint expects binary data${body.contentType != null ? ` of type ${body.contentType}` : ""}.`; + case "object": + case "alias": + return `This endpoint expects ${renderTypeShorthand(body, { withArticle: true }, types)}.`; + default: + return "This endpoint expects an unknown type."; + } +} + +export { EndpointRequestSection }; diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointResponseSection.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointResponseSection.tsx index f38b11c203..25a2e56b8d 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointResponseSection.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointResponseSection.tsx @@ -1,34 +1,26 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; +import { ReactNode } from "react"; import { useFeatureFlags } from "../../atoms"; import { Markdown } from "../../mdx/Markdown"; import { renderTypeShorthand } from "../../type-shorthand"; import { JsonPropertyPath } from "../examples/JsonPropertyPath"; import { TypeReferenceDefinitions } from "../types/type-reference/TypeReferenceDefinitions"; -export declare namespace EndpointResponseSection { - export interface Props { - response: ApiDefinition.HttpResponse; - exampleResponseBody: ApiDefinition.ExampleEndpointResponse | undefined; - onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; - types: Record; - } -} - -export const EndpointResponseSection: React.FC = ({ +const EndpointResponseSection = ({ response, exampleResponseBody, onHoverProperty, - anchorIdParts, - slug, types, -}) => { +}: { + response: ApiDefinition.HttpResponse; + exampleResponseBody: ApiDefinition.ExampleEndpointResponse | undefined; + onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; + types: Record; +}): ReactNode => { const { isAudioFileDownloadSpanSummary } = useFeatureFlags(); return ( -
+ <> = isAudioFileDownloadSpanSummary, })} /> - -
+ + ); }; interface EndpointResponseSectionContentProps { body: ApiDefinition.HttpResponseBodyShape; onHoverProperty: ((path: JsonPropertyPath, opts: { isHovering: boolean }) => void) | undefined; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; types: Record; } -function EndpointResponseSectionContent({ - body, - onHoverProperty, - anchorIdParts, - slug, - types, -}: EndpointResponseSectionContentProps) { +function EndpointResponseSectionContent({ body, onHoverProperty, types }: EndpointResponseSectionContentProps) { switch (body.type) { case "fileDownload": case "streamingText": - return null; + return false; case "stream": return ( import("../../mdx/Markdown").then(({ Markdown }) => Markdown), { ssr: true, }); -export declare namespace EndpointSection { - export type Props = React.PropsWithChildren<{ - headerType?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; - title: ReactNode; - description?: FernDocs.MarkdownText | undefined; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; - }>; -} - -export const EndpointSection: React.FC = ({ - headerType = "h3", - title, - description, - anchorIdParts, - slug, - children, -}) => { - const ref = useRef(null); - const anchorId = getAnchorId(anchorIdParts); - const href = useHref(slug, anchorId); - return ( +const EndpointSection = forwardRef>( + ({ children, ...props }, forwardedRef) => ( -
- - {createElement(headerType, { className: "relative mt-0 flex items-center mb-3" }, title)} - - +
{children} -
+
+ ), +); + +EndpointSection.displayName = "EndpointSection"; + +const EndpointSectionTitle = forwardRef< + HTMLHeadingElement, + ComponentPropsWithoutRef<"h1"> & { + level?: 1 | 2 | 3 | 4 | 5 | 6; + } +>(({ level = 3, children, ...props }, forwardRef) => { + const anchorId = useAnchorId(); + return ( + + {createElement( + `h${level}`, + { ...props, className: cn("relative mt-0 flex items-center mb-3", props.className), ref: forwardRef }, + children, + )} + ); -}; +}); + +EndpointSectionTitle.displayName = "EndpointSectionTitle"; + +const EndpointSectionDescription = forwardRef< + HTMLDivElement, + Omit, "children"> & { + children: FernDocs.MarkdownText; + } +>(({ children, ...props }, forwardRef) => { + return ; +}); + +EndpointSectionDescription.displayName = "EndpointSectionDescription"; + +export { EndpointSection, EndpointSectionDescription, EndpointSectionTitle }; diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx index 7e2a3c0fc1..f3f18c88dc 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx @@ -99,39 +99,35 @@ export const EndpointUrl = React.forwardRef - {(onClick) => ( - - )} + {showEnvironment && ( + + + + )} + {!showEnvironment && environmentBasepath && environmentBasepath !== "/" && ( + {environmentBasepath} + )} + {pathParts} + +
diff --git a/packages/ui/app/src/api-reference/types/discriminated-union/DiscriminatedUnionVariant.tsx b/packages/ui/app/src/api-reference/types/discriminated-union/DiscriminatedUnionVariant.tsx index 1393077637..8b009799ec 100644 --- a/packages/ui/app/src/api-reference/types/discriminated-union/DiscriminatedUnionVariant.tsx +++ b/packages/ui/app/src/api-reference/types/discriminated-union/DiscriminatedUnionVariant.tsx @@ -1,6 +1,5 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; import type * as FernDocs from "@fern-api/fdr-sdk/docs"; -import type * as FernNavigation from "@fern-api/fdr-sdk/navigation"; import titleCase from "@fern-api/ui-core-utils/titleCase"; import { AvailabilityBadge } from "@fern-ui/components/badges"; import cn from "clsx"; @@ -11,7 +10,7 @@ import { useIsApiReferencePaginated, useRouteListener } from "../../../atoms"; import { FernAnchor } from "../../../components/FernAnchor"; import { useHref } from "../../../hooks/useHref"; import { Markdown } from "../../../mdx/Markdown"; -import { getAnchorId } from "../../../util/anchor"; +import { useAnchorId, useSlug } from "../../endpoints/AnchorIdParts"; import { TypeDefinitionContext, TypeDefinitionContextValue, @@ -23,8 +22,6 @@ export declare namespace DiscriminatedUnionVariant { export interface Props { discriminant: ApiDefinition.PropertyKey; unionVariant: ApiDefinition.DiscriminatedUnionVariant; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; types: Record; } } @@ -32,13 +29,12 @@ export declare namespace DiscriminatedUnionVariant { export const DiscriminatedUnionVariant: React.FC = ({ discriminant, unionVariant, - anchorIdParts, - slug, types, }) => { const { isRootTypeDefinition } = useTypeDefinitionContext(); - const anchorId = getAnchorId(anchorIdParts); + const slug = useSlug(); + const anchorId = useAnchorId(); const ref = useRef(null); const [isActive, setIsActive] = useState(false); @@ -88,10 +84,8 @@ export const DiscriminatedUnionVariant: React.FC { if (descriptions.length > 0) { capturePosthogEvent("api_reference_multiple_descriptions", { - slug, - anchorIdParts, - discriminant, - discriminantValue: unionVariant.discriminantValue, + name, + href: String(new URL(`/${slug}#${anchorId}`, window.location.href)), count: descriptions.length, descriptions, }); @@ -109,7 +103,7 @@ export const DiscriminatedUnionVariant: React.FC
- + {unionVariant.displayName ?? titleCase(unionVariant.discriminantValue)} @@ -122,13 +116,7 @@ export const DiscriminatedUnionVariant: React.FC - +
diff --git a/packages/ui/app/src/api-reference/types/object/ObjectProperty.tsx b/packages/ui/app/src/api-reference/types/object/ObjectProperty.tsx index 72aa4904b5..e8919c5c51 100644 --- a/packages/ui/app/src/api-reference/types/object/ObjectProperty.tsx +++ b/packages/ui/app/src/api-reference/types/object/ObjectProperty.tsx @@ -1,6 +1,6 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; -import { AvailabilityBadge } from "@fern-ui/components/badges"; +import { CopyToClipboardButton } from "@fern-ui/components"; +import { AvailabilityBadge, Badge } from "@fern-ui/components/badges"; import cn from "clsx"; import { compact } from "es-toolkit/array"; import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; @@ -11,7 +11,7 @@ import { FernErrorBoundary } from "../../../components/FernErrorBoundary"; import { useHref } from "../../../hooks/useHref"; import { Markdown } from "../../../mdx/Markdown"; import { renderTypeShorthandRoot } from "../../../type-shorthand"; -import { getAnchorId } from "../../../util/anchor"; +import { useAnchorId, useSlug } from "../../endpoints/AnchorIdParts"; import { JsonPropertyPath } from "../../examples/JsonPropertyPath"; import { TypeDefinitionContext, @@ -27,16 +27,14 @@ import { export declare namespace ObjectProperty { export interface Props { property: ApiDefinition.ObjectProperty; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; applyErrorStyles: boolean; types: Record; } } export const ObjectProperty: React.FC = (props) => { - const { slug, anchorIdParts } = props; - const anchorId = getAnchorId(anchorIdParts); + const slug = useSlug(); + const anchorId = useAnchorId(); const ref = useRef(null); const [isActive, setIsActive] = useState(false); @@ -51,16 +49,19 @@ export const ObjectProperty: React.FC = (props) => { } }); - return ; + return ; }; interface ObjectPropertyInternalProps extends ObjectProperty.Props { - anchorId: string; isActive: boolean; } const UnmemoizedObjectPropertyInternal = forwardRef((props, ref) => { - const { slug, property, applyErrorStyles, types, anchorIdParts, anchorId, isActive } = props; + const { property, applyErrorStyles, types, isActive } = props; + + const slug = useSlug(); + const anchorId = useAnchorId(); + const contextValue = useTypeDefinitionContext(); const jsonPropertyPath = useMemo( (): JsonPropertyPath => [ @@ -110,9 +111,8 @@ const UnmemoizedObjectPropertyInternal = forwardRef { if (descriptions.length > 0) { capturePosthogEvent("api_reference_multiple_descriptions", { - name: property.key, - slug, - anchorIdParts, + name, + href: String(new URL(`/${slug}#${anchorId}`, window.location.href)), count: descriptions.length, descriptions, }); @@ -130,14 +130,20 @@ const UnmemoizedObjectPropertyInternal = forwardRef
- - - {property.key} - + + + + {property.key} + + {renderTypeShorthandRoot(property.valueShape, types, contextValue.isResponse)} {property.availability != null && ( @@ -151,8 +157,6 @@ const UnmemoizedObjectPropertyInternal = forwardRef @@ -166,8 +170,6 @@ const UnmemoizedObjectPropertyInternal = forwardRef diff --git a/packages/ui/app/src/api-reference/types/type-definition/InternalTypeDefinition.tsx b/packages/ui/app/src/api-reference/types/type-definition/InternalTypeDefinition.tsx index c2ed8cca98..720995155d 100644 --- a/packages/ui/app/src/api-reference/types/type-definition/InternalTypeDefinition.tsx +++ b/packages/ui/app/src/api-reference/types/type-definition/InternalTypeDefinition.tsx @@ -1,12 +1,11 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; import { FernTooltipProvider } from "@fern-ui/components"; import { useBooleanState, useIsHovering } from "@fern-ui/react-commons"; import cn from "clsx"; import { memo, useCallback, useMemo } from "react"; import { useRouteListener } from "../../../atoms"; import { FernErrorBoundary } from "../../../components/FernErrorBoundary"; -import { getAnchorId } from "../../../util/anchor"; +import { useAnchorId, useSlug } from "../../endpoints/AnchorIdParts"; import { TypeDefinitionContext, TypeDefinitionContextValue, @@ -21,8 +20,6 @@ export declare namespace InternalTypeDefinition { export interface Props { shape: ApiDefinition.TypeShapeOrReference; isCollapsible: boolean; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; types: Record; } } @@ -30,20 +27,16 @@ export declare namespace InternalTypeDefinition { export const InternalTypeDefinition = memo(function InternalTypeDefinition({ shape, isCollapsible, - anchorIdParts, - slug, types, }) { - const collapsableContent = useMemo( - () => createCollapsibleContent(shape, types, anchorIdParts, slug), - [shape, types, anchorIdParts, slug], - ); + const slug = useSlug(); + const anchorId = useAnchorId(); + const collapsableContent = useMemo(() => createCollapsibleContent(shape, types), [shape, types]); - const anchorIdSoFar = getAnchorId(anchorIdParts); const { value: isCollapsed, toggleValue: toggleIsCollapsed, setValue: setCollapsed } = useBooleanState(true); useRouteListener(slug, (anchor) => { - const isActive = anchor?.startsWith(anchorIdSoFar + ".") ?? false; + const isActive = anchor?.startsWith(anchorId + ".") ?? false; if (isActive) { setCollapsed(false); } @@ -90,45 +83,41 @@ export const InternalTypeDefinition = memo(functio ? `Hide ${collapsableContent.elementNameSingular}` : `Hide ${collapsableContent.elements.length} ${collapsableContent.elementNamePlural}`; - const renderContent = () => ( -
- {collapsableContent.elementNameSingular !== "enum value" ? ( - collapsableContent.elements.length === 0 ? null : ( - - - - - - ) - ) : ( - - )} -
- ); - return ( - {renderContent()} +
+ {collapsableContent.elementNameSingular !== "enum value" ? ( + collapsableContent.elements.length === 0 ? null : ( + + + + + + ) + ) : ( + + )} +
); }); diff --git a/packages/ui/app/src/api-reference/types/type-definition/TypeDefinitionDetails.tsx b/packages/ui/app/src/api-reference/types/type-definition/TypeDefinitionDetails.tsx index 9d78e29588..9ed7543d5e 100644 --- a/packages/ui/app/src/api-reference/types/type-definition/TypeDefinitionDetails.tsx +++ b/packages/ui/app/src/api-reference/types/type-definition/TypeDefinitionDetails.tsx @@ -16,7 +16,7 @@ export const TypeDefinitionDetails: React.FC = ({ e separatorText != null ? (
-
{separatorText}
+
{separatorText}
) : ( diff --git a/packages/ui/app/src/api-reference/types/type-definition/createCollapsibleContent.tsx b/packages/ui/app/src/api-reference/types/type-definition/createCollapsibleContent.tsx index 284ed649fb..6a8479afd9 100644 --- a/packages/ui/app/src/api-reference/types/type-definition/createCollapsibleContent.tsx +++ b/packages/ui/app/src/api-reference/types/type-definition/createCollapsibleContent.tsx @@ -5,9 +5,9 @@ import { unwrapObjectType, unwrapReference, } from "@fern-api/fdr-sdk/api-definition"; -import { Slug } from "@fern-api/fdr-sdk/navigation"; import { ReactElement } from "react"; import { Chip } from "../../../components/Chip"; +import { AnchorProvider } from "../../endpoints/AnchorIdParts"; import { DiscriminatedUnionVariant } from "../discriminated-union/DiscriminatedUnionVariant"; import { ObjectProperty } from "../object/ObjectProperty"; import { UndiscriminatedUnionVariant } from "../undiscriminated-union/UndiscriminatedUnionVariant"; @@ -22,8 +22,8 @@ interface CollapsibleContent { export function createCollapsibleContent( shape: TypeShapeOrReference, types: Record, - anchorIdParts: readonly string[], - slug: Slug, + // anchorIdParts: readonly string[], + // slug: Slug, ): CollapsibleContent | undefined { const unwrapped = unwrapReference(shape, types); @@ -32,14 +32,13 @@ export function createCollapsibleContent( const union = unwrapped.shape; return { elements: union.variants.map((variant) => ( - + + + )), elementNameSingular: "variant", elementNamePlural: "variants", @@ -60,14 +59,9 @@ export function createCollapsibleContent( const { properties } = unwrapObjectType(unwrapped.shape, types); return { elements: properties.map((property) => ( - + + + )), elementNameSingular: "property", elementNamePlural: "properties", @@ -76,15 +70,14 @@ export function createCollapsibleContent( case "undiscriminatedUnion": { return { elements: unwrapped.shape.variants.map((variant, variantIdx) => ( - + + + )), elementNameSingular: "variant", elementNamePlural: "variants", diff --git a/packages/ui/app/src/api-reference/types/type-reference/InternalTypeReferenceDefinitions.tsx b/packages/ui/app/src/api-reference/types/type-reference/InternalTypeReferenceDefinitions.tsx index 40c3144635..1a1fdac37a 100644 --- a/packages/ui/app/src/api-reference/types/type-reference/InternalTypeReferenceDefinitions.tsx +++ b/packages/ui/app/src/api-reference/types/type-reference/InternalTypeReferenceDefinitions.tsx @@ -1,5 +1,4 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; import React from "react"; import { UnreachableCaseError } from "ts-essentials"; @@ -12,8 +11,6 @@ export declare namespace InternalTypeReferenceDefinitions { applyErrorStyles: boolean; isCollapsible: boolean; className?: string; - anchorIdParts: readonly string[]; - slug: FernNavigation.Slug; shape: ApiDefinition.TypeShapeOrReference; types: Record; isResponse?: boolean; @@ -66,62 +63,19 @@ export const InternalTypeReferenceDefinitions: React.FC { const unwrapped = ApiDefinition.unwrapReference(shape, types); switch (unwrapped.shape.type) { - case "object": { - if (unwrapped.shape.extraProperties != null) { - // TODO: (rohin) Refactor this - return ( - - ); - } - return ( - - ); - } + case "object": case "enum": case "primitive": - case "undiscriminatedUnion": { - return ( - - ); - } - case "discriminatedUnion": { - const union = unwrapped.shape; - return ( - - ); - } + case "undiscriminatedUnion": + case "discriminatedUnion": + return ; + case "list": - case "set": { + case "set": return ( ); - } - case "map": { + + case "map": return ( ); - } + case "literal": case "unknown": - return null; + return false; default: throw new UnreachableCaseError(unwrapped.shape); } diff --git a/packages/ui/app/src/api-reference/types/type-reference/TypeReferenceDefinitions.tsx b/packages/ui/app/src/api-reference/types/type-reference/TypeReferenceDefinitions.tsx index 89449f0dd7..bca052bc44 100644 --- a/packages/ui/app/src/api-reference/types/type-reference/TypeReferenceDefinitions.tsx +++ b/packages/ui/app/src/api-reference/types/type-reference/TypeReferenceDefinitions.tsx @@ -1,5 +1,4 @@ import type * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; import { memo } from "react"; import { FernErrorBoundary } from "../../../components/FernErrorBoundary"; import { JsonPropertyPath } from "../../examples/JsonPropertyPath"; @@ -11,9 +10,7 @@ export declare namespace TypeReferenceDefinitions { applyErrorStyles: boolean; isCollapsible: boolean; onHoverProperty?: (path: JsonPropertyPath, opts: { isHovering: boolean }) => void; - anchorIdParts: readonly string[]; className?: string; - slug: FernNavigation.Slug; shape: ApiDefinition.TypeShapeOrReference; types: Record; isResponse?: boolean; @@ -25,9 +22,7 @@ export const TypeReferenceDefinitions = memo(fun isCollapsible, applyErrorStyles, onHoverProperty, - anchorIdParts, className, - slug, types, isResponse, }) { @@ -39,8 +34,6 @@ export const TypeReferenceDefinitions = memo(fun isCollapsible={isCollapsible} applyErrorStyles={applyErrorStyles} className={className} - anchorIdParts={anchorIdParts} - slug={slug} types={types} /> diff --git a/packages/ui/app/src/api-reference/types/undiscriminated-union/UndiscriminatedUnionVariant.tsx b/packages/ui/app/src/api-reference/types/undiscriminated-union/UndiscriminatedUnionVariant.tsx index c5f4b91d3c..7723df5189 100644 --- a/packages/ui/app/src/api-reference/types/undiscriminated-union/UndiscriminatedUnionVariant.tsx +++ b/packages/ui/app/src/api-reference/types/undiscriminated-union/UndiscriminatedUnionVariant.tsx @@ -1,5 +1,4 @@ import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition"; -import * as FernNavigation from "@fern-api/fdr-sdk/navigation"; import { visitDiscriminatedUnion } from "@fern-api/ui-core-utils"; import { AvailabilityBadge } from "@fern-ui/components/badges"; import cn from "clsx"; @@ -74,9 +73,7 @@ function getIconForTypeReference( export declare namespace UndiscriminatedUnionVariant { export interface Props { unionVariant: ApiDefinition.UndiscriminatedUnionVariant; - anchorIdParts: readonly string[]; applyErrorStyles: boolean; - slug: FernNavigation.Slug; idx: number; types: Record; } @@ -84,9 +81,7 @@ export declare namespace UndiscriminatedUnionVariant { export const UndiscriminatedUnionVariant: React.FC = ({ unionVariant, - anchorIdParts, applyErrorStyles, - slug, types, }) => { const { isRootTypeDefinition } = useTypeDefinitionContext(); @@ -122,10 +117,8 @@ export const UndiscriminatedUnionVariant: React.FC diff --git a/packages/ui/app/src/api-reference/web-socket/WebSocket.tsx b/packages/ui/app/src/api-reference/web-socket/WebSocket.tsx index 4bae775058..6a9566ac6f 100644 --- a/packages/ui/app/src/api-reference/web-socket/WebSocket.tsx +++ b/packages/ui/app/src/api-reference/web-socket/WebSocket.tsx @@ -15,7 +15,7 @@ import { PlaygroundButton } from "../../playground/PlaygroundButton"; import { usePlaygroundBaseUrl } from "../../playground/utils/select-environment"; import { getSlugFromChildren } from "../../util/getSlugFromText"; import { EndpointParameter } from "../endpoints/EndpointParameter"; -import { EndpointSection } from "../endpoints/EndpointSection"; +import { EndpointSection, EndpointSectionTitle } from "../endpoints/EndpointSection"; import { EndpointUrlWithOverflow } from "../endpoints/EndpointUrlWithOverflow"; import { TitledExample } from "../examples/TitledExample"; import { TypeComponentSeparator } from "../types/TypeComponentSeparator"; @@ -158,11 +158,8 @@ const WebhookContent: FC = ({ context, breadcrumb, last }) } > {headers && headers.length > 0 && ( - + + Headers
{headers.map((parameter) => (
@@ -186,11 +183,8 @@ const WebhookContent: FC = ({ context, breadcrumb, last }) )} {channel.pathParameters && channel.pathParameters.length > 0 && ( - + + Path parameters
{channel.pathParameters.map((parameter) => (
@@ -214,11 +208,8 @@ const WebhookContent: FC = ({ context, breadcrumb, last }) )} {channel.queryParameters && channel.queryParameters.length > 0 && ( - + + Query parameters
{channel.queryParameters.map((parameter) => { return ( @@ -248,19 +239,15 @@ const WebhookContent: FC = ({ context, breadcrumb, last }) {publishMessages.length > 0 && ( - + {"Send"} - } - anchorIdParts={["send"]} - slug={node.slug} - headerType="h2" - > + {/* = ({ context, breadcrumb, last }) )} {subscribeMessages.length > 0 && ( - + {"Receive"} - } - anchorIdParts={["receive"]} - slug={node.slug} - headerType="h2" - > + {/*
- +

{title}

{headingElement} diff --git a/packages/ui/app/src/api-reference/webhooks/WebhookContent.tsx b/packages/ui/app/src/api-reference/webhooks/WebhookContent.tsx index 068523955f..00e94a0a1c 100644 --- a/packages/ui/app/src/api-reference/webhooks/WebhookContent.tsx +++ b/packages/ui/app/src/api-reference/webhooks/WebhookContent.tsx @@ -6,7 +6,7 @@ import { FernBreadcrumbs } from "../../components/FernBreadcrumbs"; import { useHref } from "../../hooks/useHref"; import { Markdown } from "../../mdx/Markdown"; import { EndpointParameter } from "../endpoints/EndpointParameter"; -import { EndpointSection } from "../endpoints/EndpointSection"; +import { EndpointSection, EndpointSectionTitle } from "../endpoints/EndpointSection"; import { JsonPropertyPath } from "../examples/JsonPropertyPath"; import { TypeComponentSeparator } from "../types/TypeComponentSeparator"; import { useApiPageCenterElement } from "../useApiPageCenterElement"; @@ -63,7 +63,8 @@ export const WebhookContent = memo((props) => { {webhook.headers && webhook.headers.length > 0 && (
- + + Headers
{webhook.headers.map((parameter) => (
@@ -92,7 +93,8 @@ export const WebhookContent = memo((props) => { {webhook.payload && (
- + + Payload ((props) => {
- + + Response
diff --git a/packages/ui/app/src/components/ApiReferenceButton.tsx b/packages/ui/app/src/components/ApiReferenceButton.tsx index be51ba793e..ab253e3fcb 100644 --- a/packages/ui/app/src/components/ApiReferenceButton.tsx +++ b/packages/ui/app/src/components/ApiReferenceButton.tsx @@ -8,7 +8,7 @@ export const ApiReferenceButton: React.FC<{ slug: FernNavigation.Slug }> = ({ sl const href = useHref(slug); return ( - + ) : undefined } + asChild > ): ReactElement { - const { copyToClipboard, wasJustCopied } = useCopyToClipboard(() => new URL(href, window.location.href).toString()); +export function FernAnchor({ + href, + sideOffset = 12, + children, + asChild, +}: PropsWithChildren): ReactElement { + const { copyToClipboard, wasJustCopied } = useCopyToClipboard(() => String(new URL(href, window.location.href))); const [forceMount, setIsMounted] = useState(wasJustCopied ? true : undefined); useEffect(() => { @@ -27,7 +33,7 @@ export function FernAnchor({ href, sideOffset = 12, children }: PropsWithChildre return ( - {children} + {children} & { + title?: ReactNode; - // mdx: FernDocs.MarkdownText | FernDocs.MarkdownText[] | undefined; - mdx: FernDocs.MarkdownText | undefined; - className?: string; - size?: "xs" | "sm" | "lg"; + // mdx: FernDocs.MarkdownText | FernDocs.MarkdownText[] | undefined; + mdx: FernDocs.MarkdownText | undefined; + size?: "xs" | "sm" | "lg"; - /** - * Fallback content to render if the MDX is empty - */ - fallback?: ReactNode; - } -} + /** + * Fallback content to render if the MDX is empty + */ + fallback?: ReactNode; + } + >(({ title, mdx, size, fallback, ...props }, ref) => { + // If the MDX is empty, return null + if ( + !fallback && + (mdx == null || (typeof mdx === "string" ? mdx.trim().length === 0 : mdx.code.trim().length === 0)) + ) { + return null; + } -export const Markdown = memo(({ title, mdx, className, size, fallback }) => { - // If the MDX is empty, return null - if ( - !fallback && - (mdx == null || (typeof mdx === "string" ? mdx.trim().length === 0 : mdx.code.trim().length === 0)) - ) { - return null; - } - - return ( -
- {title} - -
- ); -}); + return ( +
+ {title} + +
+ ); + }), +); Markdown.displayName = "Markdown"; diff --git a/packages/ui/app/src/mdx/components/html/index.tsx b/packages/ui/app/src/mdx/components/html/index.tsx index e1d25304b9..36f80c0f80 100644 --- a/packages/ui/app/src/mdx/components/html/index.tsx +++ b/packages/ui/app/src/mdx/components/html/index.tsx @@ -20,7 +20,11 @@ import { FernLink } from "../../../components/FernLink"; import { useFrontmatter } from "../../../contexts/frontmatter"; export const HeadingRenderer = (level: number, props: ComponentProps<"h1">): ReactElement => { - return {createElement(`h${level}`, props)}; + return ( + + {createElement(`h${level}`, props)} + + ); }; export const P: FC<{ variant: "api" | "markdown" } & ComponentProps<"p">> = ({ variant, className, ...rest }) => { diff --git a/packages/ui/app/src/playground/PlaygroundButton.tsx b/packages/ui/app/src/playground/PlaygroundButton.tsx index 0234f8700d..cfe23b3b0f 100644 --- a/packages/ui/app/src/playground/PlaygroundButton.tsx +++ b/packages/ui/app/src/playground/PlaygroundButton.tsx @@ -25,6 +25,7 @@ export const PlaygroundButton: FC<{ Customize and run in API Playground
} + asChild > > = {propertyKey} {description != null && ( - } delayDuration={0}> + } delayDuration={0} asChild> )} diff --git a/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx b/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx index a2f8204683..07a161330c 100644 --- a/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx +++ b/packages/ui/app/src/playground/auth/PlaygroundOAuthForm.tsx @@ -99,7 +99,10 @@ function FoundOAuthReferencedEndpointForm({
} + asChild > diff --git a/packages/ui/components/src/FernRadioGroup.tsx b/packages/ui/components/src/FernRadioGroup.tsx index 66447cdda8..2469e538ce 100644 --- a/packages/ui/components/src/FernRadioGroup.tsx +++ b/packages/ui/components/src/FernRadioGroup.tsx @@ -17,7 +17,7 @@ export const FernRadioGroup = forwardRef( {options.map( (item) => item.type === "value" && ( - +