Skip to content

Commit

Permalink
fix: render top-level API summary page (#632)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Apr 10, 2024
1 parent 9d67ec8 commit b88e345
Show file tree
Hide file tree
Showing 19 changed files with 130 additions and 47 deletions.
8 changes: 4 additions & 4 deletions packages/commons/fdr-utils/src/flattenApiDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,16 +272,16 @@ function flattenPackage(
const pages =
order?.items
?.filter((item): item is DocsV1Read.ApiNavigationConfigItem.Page => item.type === "page")
.map((item): FlattenedPageMetadata => {
return {
.map(
(item): FlattenedPageMetadata => ({
type: "page",
id: item.id,
slug: item.fullSlug ?? [...parentSlugs, item.urlSlug],
title: item.title,
icon: item.icon,
hidden: item.hidden ?? false,
};
}) ?? [];
}),
) ?? [];

const items: FlattenedApiDefinitionPackageItem[] = [
...endpoints,
Expand Down
6 changes: 1 addition & 5 deletions packages/commons/fdr-utils/src/getNavigationRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,7 @@ function resolveRedirect(node: SidebarNodeRaw.VisitableNode | undefined, from: s
}

if (node.type === "apiSection") {
if (node.hasSummaryPage) {
// go to the summary page directly (no need to redirect)
return undefined;
}
const firstChild = node.items[0] ?? node.changelog;
const firstChild = node.summaryPage ?? node.items[0] ?? node.changelog;
return resolveRedirect(firstChild, from);
}

Expand Down
32 changes: 30 additions & 2 deletions packages/commons/fdr-utils/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,21 @@ function resolveSidebarNodeRawApiSection(
description: undefined, // TODO: add description here
icon: undefined,
hidden: false,
hasSummaryPage: subpackage.summaryPageId != null,
summaryPage:
subpackage.summaryPageId != null
? {
type: "page",
id: subpackage.summaryPageId,
slug: subpackage.slug,
title,
description: undefined,
icon: undefined,
hidden: false,
apiType: "summary",
api,
}
: undefined,
flattenedApiDefinition: undefined, // only the top-level api section should have this
};
}

Expand Down Expand Up @@ -271,7 +285,21 @@ export function resolveSidebarNodes(
description: undefined, // TODO: add description here
icon: api.icon,
hidden: api.hidden ?? false,
hasSummaryPage: flattened.summaryPageId != null,
summaryPage:
flattened.summaryPageId != null
? {
type: "page",
id: flattened.summaryPageId,
slug: definitionSlug,
title: api.title,
description: undefined,
icon: api.icon,
hidden: api.hidden ?? false,
apiType: "summary",
api: api.api,
}
: undefined,
flattenedApiDefinition: flattened,
});
}
},
Expand Down
12 changes: 11 additions & 1 deletion packages/commons/fdr-utils/src/traverser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ function visitNode(
return traverseState;
},
apiSection: (apiSection) => {
if (apiSection.summaryPage != null) {
traverseState = visitPage(apiSection.summaryPage, currentNode, traverseState, sectionTitleBreadcrumbs);
if (traverseState.next != null) {
return traverseState;
}
}

const apiSectionBreadcrumbs = [...sectionTitleBreadcrumbs, apiSection.title];

if (apiSection.changelog != null) {
Expand Down Expand Up @@ -154,7 +161,10 @@ export function traverseSidebarNodes(
return traverseState;
}

export function findApiSection(api: string, sidebarNodes: SidebarNode[]): SidebarNode.ApiSection | undefined {
export function findApiSection(
api: string,
sidebarNodes: readonly SidebarNodeRaw[],
): SidebarNodeRaw.ApiSection | undefined {
for (const node of sidebarNodes) {
if (node.type === "apiSection") {
if (node.id === api) {
Expand Down
25 changes: 21 additions & 4 deletions packages/commons/fdr-utils/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { APIV1Read, DocsV1Read, FdrAPI } from "@fern-api/fdr-sdk";
import type { MDXRemoteSerializeResult } from "next-mdx-remote";
import { FlattenedApiDefinition } from "./flattenApiDefinition";

export interface ColorsConfig {
light: DocsV1Read.ThemeConfig | undefined;
Expand Down Expand Up @@ -84,7 +85,8 @@ export declare namespace SidebarNodeRaw {
description: string | undefined;
icon: string | undefined;
hidden: boolean;
hasSummaryPage: boolean;
summaryPage: ApiSummaryPage | undefined;
flattenedApiDefinition: FlattenedApiDefinition | undefined;
}

export interface Section {
Expand Down Expand Up @@ -130,11 +132,16 @@ export declare namespace SidebarNodeRaw {
stream?: boolean;
}

export interface ApiSummaryPage extends Page {
api: FdrAPI.ApiId;
apiType: "summary";
}

export interface SubpackageSection extends ApiSection {
apiType: "subpackage";
}

export type ApiPage = WebSocketPage | WebhookPage | EndpointPage;
export type ApiPage = WebSocketPage | WebhookPage | EndpointPage | ApiSummaryPage;

export type ApiPageOrSubpackage = ApiPage | SubpackageSection | Page;
}
Expand Down Expand Up @@ -162,9 +169,14 @@ export declare namespace SidebarNode {

export interface ChangelogPage extends Page, Omit<SidebarNodeRaw.ChangelogPage, keyof Page> {}

export interface ApiSection extends Omit<SidebarNodeRaw.ApiSection, "items" | "changelog" | "description"> {
export interface ApiSection
extends Omit<
SidebarNodeRaw.ApiSection,
"items" | "changelog" | "description" | "flattenedApiDefinition" | "summaryPage"
> {
items: ApiPageOrSubpackage[];
changelog: ChangelogPage | undefined;
summaryPage: ApiSummaryPage | undefined;
description: MDXRemoteSerializeResult | string | undefined;
}

Expand Down Expand Up @@ -193,11 +205,16 @@ export declare namespace SidebarNode {
stream?: boolean;
}

export interface ApiSummaryPage extends Page {
api: FdrAPI.ApiId;
apiType: "summary";
}

export interface SubpackageSection extends ApiSection {
apiType: "subpackage";
}

export type ApiPage = WebSocketPage | WebhookPage | EndpointPage;
export type ApiPage = WebSocketPage | WebhookPage | EndpointPage | ApiSummaryPage;

export type ApiPageOrSubpackage = ApiPage | SubpackageSection | Page;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/commons/fdr-utils/src/visitSidebarNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export function visitSidebarNode(
return false;
}
}
if (apiSection.summaryPage != null) {
const flag = visitSidebarNode(apiSection.summaryPage, visit, [...parentNodes, apiSection]);
if (flag === false) {
return false;
}
}
return true;
},
section: (section) => {
Expand Down
6 changes: 6 additions & 0 deletions packages/commons/fdr-utils/src/visitSidebarNodeRaw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export function visitSidebarNodeRaw(
return false;
}
}
if (apiSection.summaryPage != null) {
const flag = visitSidebarNodeRaw(apiSection.summaryPage, visit, [...parentNodes, apiSection]);
if (flag === false) {
return false;
}
}
return true;
},
section: (section) => {
Expand Down
22 changes: 12 additions & 10 deletions packages/fdr-sdk/src/converters/db/convertDocsDefinitionToDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
visitUnversionedWriteNavigationConfig,
visitWriteNavigationConfig,
} from "../../client";
import { type WithoutQuestionMarks } from "../utils/WithoutQuestionMarks";
import { assertNever } from "../utils/assertNever";
import { DEFAULT_DARK_MODE_ACCENT_PRIMARY, DEFAULT_LIGHT_MODE_ACCENT_PRIMARY } from "../utils/colors";
import { type WithoutQuestionMarks } from "../utils/WithoutQuestionMarks";
const { kebabCase } = lodash;

export interface S3FileInfo {
Expand Down Expand Up @@ -419,19 +419,21 @@ function transformApiSectionNavigationForDb(
}
return {
items: transformItems(writeShape.items),
summaryPageId: writeShape.summaryPageId,
};
}

function transformItems(items: DocsV1Write.ApiNavigationConfigItem[]) {
return items.map((item): DocsV1Read.ApiNavigationConfigItem => {
return item.type === "subpackage"
? {
type: "subpackage",
subpackageId: item.subpackageId,
items: transformItems(item.items),
}
: item.type === "page"
? transformPageNavigationItemForDb(item)
: item;
if (item.type === "subpackage") {
return {
type: "subpackage",
subpackageId: item.subpackageId,
items: transformItems(item.items),
};
} else if (item.type === "page") {
return transformPageNavigationItemForDb(item);
}
return item;
});
}
2 changes: 1 addition & 1 deletion packages/ui/app/src/api-page/ApiSectionMarkdownPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const ApiSectionMarkdownPage = ({
return (
<div
className={clsx("scroll-mt-header-height-padded", {
"border-default border-b mb-px pb-20": !hideBottomSeparator,
"border-default border-b mb-px": !hideBottomSeparator,
})}
ref={setTargetRef}
data-route={`/${slug}`}
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/mdx/plugins/rehypeLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function rehypeFernLayout(props?: PageHeaderProps): (tree: Root, vfile: V
h(
"div",
{
class: "md:max-h-vh-minus-header scroll-mt-header-height md:top-header-height md:sticky md:-my-8 md:py-8",
class: "scroll-mt-header-height md:top-header-height md:sticky md:-my-8 md:py-8",
},
aside,
),
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/app/src/next-app/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,11 @@
.fern-sidebar-link-container {
@apply items-stretch relative flex min-h-[36px];
@apply data-[state=active]:t-accent-aaa;
@apply data-[state=inactive]:hover:t-accent-aaa data-[state=inactive]:t-muted;
@apply data-[state=inactive]:hover:t-accent-aaa;

&:not(.top-level) {
@apply data-[state=inactive]:t-muted;
}
}

.fern-sidebar-link-indent {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/sidebar/SidebarApiSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export const ExpandableSidebarApiSection: React.FC<ExpandableSidebarApiSectionPr
showIndicator={selectedSlug != null && checkSlugStartsWith(selectedSlug, slug) && !expanded}
icon={apiSection.icon}
hidden={apiSection.hidden}
slug={apiSection.hasSummaryPage ? slug : undefined}
slug={apiSection.summaryPage != null ? slug : undefined}
>
{children}
</SidebarSlugLink>
Expand Down
11 changes: 8 additions & 3 deletions packages/ui/app/src/sidebar/SidebarLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { range } from "lodash-es";
import { Url } from "next/dist/shared/lib/router/router";
import Link from "next/link";
import {
forwardRef,
HTMLAttributeAnchorTarget,
memo,
JSX,
JSXElementConstructor,
PropsWithChildren,
ReactElement,
ReactNode,
createElement,
forwardRef,
memo,
useEffect,
useRef,
} from "react";
Expand All @@ -37,6 +40,7 @@ interface SidebarSlugLinkProps {
tooltipContent?: ReactNode;
hidden: boolean;
scrollOnShallow?: boolean;
as?: keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
}

type SidebarLinkProps = PropsWithChildren<
Expand Down Expand Up @@ -72,6 +76,7 @@ const SidebarLinkInternal = forwardRef<HTMLButtonElement, SidebarLinkProps>((pro
rel,
hidden,
scrollOnShallow,
as = "span",
} = props;

if (hidden && !expanded && !selected) {
Expand Down Expand Up @@ -164,7 +169,7 @@ const SidebarLinkInternal = forwardRef<HTMLButtonElement, SidebarLinkProps>((pro
{typeof icon === "string" ? <RemoteFontAwesomeIcon icon={icon} /> : icon}
</span>
)}
<span className="fern-sidebar-link-text">{title}</span>
{createElement(as, { className: "fern-sidebar-link-text" }, title)}
{rightElement}
</span>
{expandButton}
Expand Down
14 changes: 8 additions & 6 deletions packages/ui/app/src/sidebar/SidebarSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,17 +187,19 @@ export const SidebarSection = memo<SidebarSectionProps>(function SidebarSection(
apiSection: (apiSection) =>
depth === 0 ? (
<li key={apiSection.id}>
{apiSection.hasSummaryPage ? (
{apiSection.summaryPage != null ? (
<SidebarSlugLink
className={cn({
className={cn("top-level", {
"mt-6": topLevel,
})}
depth={depth}
title={apiSection.title}
slug={apiSection.slug}
icon={apiSection.icon}
hidden={apiSection.hidden}
title={apiSection.summaryPage.title}
as={"h6"}
slug={apiSection.summaryPage.slug}
icon={apiSection.summaryPage.icon}
hidden={apiSection.summaryPage.hidden}
registerScrolledToPathListener={registerScrolledToPathListener}
selected={isEqual(selectedSlug, apiSection.summaryPage.slug)}
>
<SidebarApiSection
slug={apiSection.slug}
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/app/src/sidebar/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function serializeSidebarNodeDescriptionMdx(
}

async function serializeApiSectionDescriptionMdx(
apiSection: SidebarNodeRaw.ApiSection,
{ flattenedApiDefinition, ...apiSection }: SidebarNodeRaw.ApiSection,
options?: FernSerializeMdxOptions,
): Promise<SidebarNode.ApiSection> {
return {
Expand All @@ -40,6 +40,9 @@ async function serializeApiSectionDescriptionMdx(
changelog: apiSection.changelog
? await serializePageDescriptionMdx<SidebarNode.ChangelogPage>(apiSection.changelog, options)
: undefined,
summaryPage: apiSection.summaryPage
? await serializePageDescriptionMdx<SidebarNode.ApiSummaryPage>(apiSection.summaryPage, options)
: undefined,
};
}

Expand Down
Loading

0 comments on commit b88e345

Please sign in to comment.