Skip to content

Commit 1c916ef

Browse files
committed
feat(site): clean up api reference header hierarchy
I'm ok with H4s now
1 parent a04bedc commit 1c916ef

File tree

6 files changed

+496
-115
lines changed

6 files changed

+496
-115
lines changed

site/src/components/docs/TableOfContents/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { MarkdownHeading } from 'astro';
22
import { TableOfContentsDesktop } from './TableOfContents.desktop';
33
import { TableOfContentsMobile } from './TableOfContents.mobile';
4-
import { filterHeadingsByDepth, navigateToHeading, useActiveHeading } from './utils';
4+
import { filterHeadingsForToc, navigateToHeading, useActiveHeading } from './utils';
55

66
interface TableOfContentsProps {
77
headings: MarkdownHeading[];
88
}
99

1010
export function TableOfContents({ headings }: TableOfContentsProps) {
11-
const filteredHeadings = filterHeadingsByDepth(headings, 2, 3);
11+
const filteredHeadings = filterHeadingsForToc(headings);
1212
const activeId = useActiveHeading(filteredHeadings);
1313

1414
if (filteredHeadings.length === 0) {

site/src/components/docs/TableOfContents/utils.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import debounce from 'just-debounce-it';
33
import throttle from 'just-throttle';
44
import type { RefObject } from 'react';
55
import { useEffect, useState } from 'react';
6+
import { API_REFERENCE_SUBSECTION_TITLES } from '@/utils/apiReferenceModel';
67

78
/**
89
* Find the first scrollable ancestor of an element
@@ -39,14 +40,28 @@ export function isElementOffscreen(element: HTMLElement, container: HTMLElement)
3940
}
4041

4142
/**
42-
* Filter headings by depth range
43+
* Include headings for docs TOC, including API-reference subsection H4s only.
4344
*/
44-
export function filterHeadingsByDepth(
45-
headings: MarkdownHeading[],
46-
minDepth: number,
47-
maxDepth: number
48-
): MarkdownHeading[] {
49-
return headings.filter((h) => h.depth >= minDepth && h.depth <= maxDepth);
45+
export function filterHeadingsForToc(headings: MarkdownHeading[]): MarkdownHeading[] {
46+
const apiReferenceSubsectionTitles = new Set(API_REFERENCE_SUBSECTION_TITLES);
47+
const isTocHeadingDepth = (depth: number): boolean => depth === 2 || depth === 3;
48+
const isApiReferenceSubsectionHeading = (heading: MarkdownHeading): boolean => {
49+
const tocKind = (heading as MarkdownHeading & { tocKind?: string }).tocKind;
50+
51+
return tocKind === 'api-reference-subsection' && apiReferenceSubsectionTitles.has(heading.text);
52+
};
53+
54+
return headings.filter((heading) => {
55+
if (isTocHeadingDepth(heading.depth)) {
56+
return true;
57+
}
58+
59+
if (heading.depth === 4) {
60+
return isApiReferenceSubsectionHeading(heading);
61+
}
62+
63+
return false;
64+
});
5065
}
5166

5267
/**

site/src/components/docs/api-reference/ApiReference.astro

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { kebabCase } from 'es-toolkit/string';
44
import ContentWidth from '@/components/frames/ContentWidth.astro';
55
import H2 from '@/components/typography/H2Markdown.astro';
66
import H3 from '@/components/typography/H3Markdown.astro';
7+
import H4 from '@/components/typography/H4Markdown.astro';
78
import MarkdownCode from '@/components/typography/MarkdownCode.astro';
89
import P from '@/components/typography/P.astro';
910
import type { ComponentApiReference } from '@/types/api-reference';
1011
import { isValidFramework } from '@/types/docs';
12+
import { createApiReferenceModel } from '@/utils/apiReferenceModel';
1113
import FrameworkCase from '../FrameworkCase.astro';
1214
import ApiDataAttrsTable from './ApiDataAttrsTable.astro';
1315
import ApiPropsTable from './ApiPropsTable.astro';
@@ -28,47 +30,50 @@ const entry = await getEntry('apiReference', kebabCase(component));
2830
const apiRef: ComponentApiReference | null = entry?.data ?? null;
2931
if (!apiRef) return;
3032
31-
const hasParts = apiRef.parts && Object.keys(apiRef.parts).length > 0;
33+
const apiReferenceModel = createApiReferenceModel(component, apiRef);
34+
if (!apiReferenceModel) return;
3235
33-
const hasProps = Object.keys(apiRef.props).length > 0;
34-
const hasState = Object.keys(apiRef.state).length > 0;
35-
const hasDataAttrs = Object.keys(apiRef.dataAttributes).length > 0;
36+
const singlePropsSection = !apiReferenceModel.hasParts
37+
? apiReferenceModel.sections.find((section) => section.key === 'props')
38+
: null;
39+
const singleStateSection = !apiReferenceModel.hasParts
40+
? apiReferenceModel.sections.find((section) => section.key === 'state')
41+
: null;
42+
const singleDataAttributesSection = !apiReferenceModel.hasParts
43+
? apiReferenceModel.sections.find((section) => section.key === 'dataAttributes')
44+
: null;
3645
---
3746

38-
{hasParts ? (
39-
<ContentWidth>
40-
{Object.entries(apiRef.parts!).map(([partKebab, part]) => {
41-
const tagName = part.platforms?.html?.tagName;
42-
const partHasProps = Object.keys(part.props).length > 0;
43-
const partHasState = Object.keys(part.state).length > 0;
44-
const partHasDataAttrs = Object.keys(part.dataAttributes).length > 0;
45-
const componentName = `${component}.${part.name}`;
47+
<ContentWidth>
48+
<H2 id={apiReferenceModel.heading.id}>{apiReferenceModel.heading.text}</H2>
49+
50+
{apiReferenceModel.hasParts ? (
51+
apiReferenceModel.parts.map((part) => {
52+
const partPropsSection = part.sections.find((section) => section.key === 'props');
53+
const partStateSection = part.sections.find((section) => section.key === 'state');
54+
const partDataAttributesSection = part.sections.find((section) => section.key === 'dataAttributes');
4655

4756
return (
4857
<>
49-
<H2 id={partKebab}>
50-
<FrameworkCase frameworks={["react"]}>
51-
<MarkdownCode>{`<${component}.${part.name} />`}</MarkdownCode> reference
52-
</FrameworkCase>
53-
<FrameworkCase frameworks={["html"]}>
54-
<MarkdownCode>{tagName ? `<${tagName}>` : part.name}</MarkdownCode> reference
55-
</FrameworkCase>
56-
</H2>
58+
<H3 id={part.id}>
59+
<FrameworkCase frameworks={["react"]}>{part.labelByFramework.react}</FrameworkCase>
60+
<FrameworkCase frameworks={["html"]}>{part.labelByFramework.html}</FrameworkCase>
61+
</H3>
5762

5863
{part.description && (
5964
<P><InlineMarkdown content={part.description} /></P>
6065
)}
6166

62-
{partHasProps && (
67+
{partPropsSection && (
6368
<>
64-
<H3 id={`${partKebab}-props`}>Props</H3>
65-
<ApiPropsTable props={part.props} componentName={componentName} />
69+
<H4 id={partPropsSection.id}>{partPropsSection.title}</H4>
70+
<ApiPropsTable props={part.data.props} componentName={part.componentName} />
6671
</>
6772
)}
6873

69-
{partHasState && (
74+
{partStateSection && (
7075
<>
71-
<H3 id={`${partKebab}-state`}>State</H3>
76+
<H4 id={partStateSection.id}>{partStateSection.title}</H4>
7277
<P>
7378
<FrameworkCase frameworks={["react"]}>
7479
State is accessible via the{" "}
@@ -80,54 +85,52 @@ const hasDataAttrs = Object.keys(apiRef.dataAttributes).length > 0;
8085
State is reflected as data attributes for CSS styling.
8186
</FrameworkCase>
8287
</P>
83-
<ApiStateTable state={part.state} componentName={componentName} />
88+
<ApiStateTable state={part.data.state} componentName={part.componentName} />
8489
</>
8590
)}
8691

87-
{partHasDataAttrs && (
92+
{partDataAttributesSection && (
8893
<>
89-
<H3 id={`${partKebab}-data-attributes`}>Data attributes</H3>
90-
<ApiDataAttrsTable dataAttributes={part.dataAttributes} />
94+
<H4 id={partDataAttributesSection.id}>{partDataAttributesSection.title}</H4>
95+
<ApiDataAttrsTable dataAttributes={part.data.dataAttributes} />
9196
</>
9297
)}
9398
</>
9499
);
95-
})}
96-
</ContentWidth>
97-
) : (
98-
<ContentWidth>
99-
<H2 id="api-reference">API reference</H2>
100-
101-
{hasProps && (
102-
<>
103-
<H3 id="props">Props</H3>
104-
<ApiPropsTable props={apiRef.props} componentName={component} />
105-
</>
106-
)}
100+
})
101+
) : (
102+
<>
103+
{singlePropsSection && (
104+
<>
105+
<H3 id={singlePropsSection.id}>{singlePropsSection.title}</H3>
106+
<ApiPropsTable props={apiReferenceModel.data.props} componentName={component} />
107+
</>
108+
)}
107109

108-
{hasState && (
109-
<>
110-
<H3 id="state">State</H3>
111-
<P>
112-
<FrameworkCase frameworks={["react"]}>
113-
State is accessible via the{" "}
114-
<MarkdownCode>render</MarkdownCode>,{" "}
115-
<MarkdownCode>className</MarkdownCode>, and{" "}
116-
<MarkdownCode>style</MarkdownCode> props.
117-
</FrameworkCase>
118-
<FrameworkCase frameworks={["html"]}>
119-
State is reflected as data attributes for CSS styling.
120-
</FrameworkCase>
121-
</P>
122-
<ApiStateTable state={apiRef.state} componentName={component} />
123-
</>
124-
)}
110+
{singleStateSection && (
111+
<>
112+
<H3 id={singleStateSection.id}>{singleStateSection.title}</H3>
113+
<P>
114+
<FrameworkCase frameworks={["react"]}>
115+
State is accessible via the{" "}
116+
<MarkdownCode>render</MarkdownCode>,{" "}
117+
<MarkdownCode>className</MarkdownCode>, and{" "}
118+
<MarkdownCode>style</MarkdownCode> props.
119+
</FrameworkCase>
120+
<FrameworkCase frameworks={["html"]}>
121+
State is reflected as data attributes for CSS styling.
122+
</FrameworkCase>
123+
</P>
124+
<ApiStateTable state={apiReferenceModel.data.state} componentName={component} />
125+
</>
126+
)}
125127

126-
{hasDataAttrs && (
127-
<>
128-
<H3 id="data-attributes">Data attributes</H3>
129-
<ApiDataAttrsTable dataAttributes={apiRef.dataAttributes} />
130-
</>
131-
)}
132-
</ContentWidth>
133-
)}
128+
{singleDataAttributesSection && (
129+
<>
130+
<H3 id={singleDataAttributesSection.id}>{singleDataAttributesSection.title}</H3>
131+
<ApiDataAttrsTable dataAttributes={apiReferenceModel.data.dataAttributes} />
132+
</>
133+
)}
134+
</>
135+
)}
136+
</ContentWidth>

0 commit comments

Comments
 (0)