mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs(docs-infra): API doc content rendering fixes (#60116)
The PR introduces a few doc content rendering fixes: - Fix highlighted section heading styles (regression from #59965). - Convert JSDoc links within 'Usage Notes' sections to HTML and render them. - Add IDs to doc content headings. This, by itself, makes these headings available in the page ToC. PR Close #60116
This commit is contained in:
parent
ffb19e64f1
commit
2b114e784d
9 changed files with 52 additions and 37 deletions
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import {Renderer, Tokens} from 'marked';
|
||||
import {codeToHtml} from '../shiki/shiki';
|
||||
import {SECTION_HEADING, SECTION_SUB_HEADING} from '../styling/css-classes';
|
||||
|
||||
/**
|
||||
* Custom renderer for marked that will be used to transform markdown files to HTML
|
||||
|
|
@ -55,21 +56,39 @@ export const renderer: Partial<Renderer> = {
|
|||
<div class="docs-table docs-scroll-track-transparent">
|
||||
<table>
|
||||
<thead>
|
||||
${this.tablerow({
|
||||
text: header.map((cell) => this.tablecell(cell)).join(''),
|
||||
})}
|
||||
${this.tablerow({text: header.map((cell) => this.tablecell(cell)).join('')})}
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows
|
||||
.map((row) =>
|
||||
this.tablerow({
|
||||
text: row.map((cell) => this.tablecell(cell)).join(''),
|
||||
}),
|
||||
)
|
||||
.map((row) => this.tablerow({text: row.map((cell) => this.tablecell(cell)).join('')}))
|
||||
.join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
heading(this: Renderer, {text, depth, tokens}: Tokens.Heading) {
|
||||
const id = text
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-')
|
||||
.replace(/[^a-z0-9-]/g, '');
|
||||
|
||||
// Since we have a code transformer `addApiLinksToHtml` which adds anchors
|
||||
// to code blocks of known symbols, we add an additional `data-skip-anchor`
|
||||
// attribute that prevents the transformation. This is needed since nested
|
||||
// anchor tags are illegal and break the HTML.
|
||||
const textRenderer = new Renderer();
|
||||
textRenderer.codespan = ({text}) => `<code data-skip-anchor>${text}</code>`;
|
||||
const parsedText = this.parser.parseInline(tokens, textRenderer);
|
||||
|
||||
// The template matches templates/section-heading.tsx
|
||||
return `
|
||||
<h${depth} id="${id}" class="${SECTION_HEADING} ${SECTION_SUB_HEADING}">
|
||||
<a href="#${id}" aria-label="Link to ${text} section" tabIndex="-1">
|
||||
${parsedText}
|
||||
</a>
|
||||
</h${depth}>
|
||||
`;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,3 +27,4 @@ export const HEADER_ENTRY_LABEL = 'docs-api-item-label';
|
|||
|
||||
export const SECTION_CONTAINER = 'docs-reference-section';
|
||||
export const SECTION_HEADING = 'docs-reference-section-heading';
|
||||
export const SECTION_SUB_HEADING = 'docs-reference-section-heading--sub';
|
||||
|
|
|
|||
|
|
@ -476,10 +476,11 @@ function appendPrefixAndSuffix(entry: DocEntry, codeTocData: CodeTableOfContents
|
|||
*/
|
||||
export function addApiLinksToHtml(htmlString: string): string {
|
||||
const result = htmlString.replace(
|
||||
// This regex looks for span/code blocks not wrapped by an anchor block.
|
||||
// This regex looks for span/code blocks not wrapped by an anchor block
|
||||
// or the tag doesn't contain `data-skip-anchor` attribute.
|
||||
// Their content are then replaced with a link if the symbol is known
|
||||
// The captured content ==> vvvvvvvv
|
||||
/(?<!<a[^>]*>)(<(?:(?:span)|(?:code))[^>]*>\s*)([^<]*?)(\s*<\/(?:span|code)>)/g,
|
||||
// The captured content ==> vvvvvvvv
|
||||
/(?<!<a[^>]*>)(<(?:(?:span)|(?:code))(?!\sdata-skip-anchor)[^>]*>\s*)([^<]*?)(\s*<\/(?:span|code)>)/g,
|
||||
(type: string, span1: string, potentialSymbolName: string, span2: string) => {
|
||||
let [symbol, subSymbol] = potentialSymbolName.split(/(?:#|\.)/) as [string, string?];
|
||||
|
||||
|
|
|
|||
|
|
@ -58,16 +58,10 @@ export function addHtmlDescription<T extends HasDescription & HasModuleName>(
|
|||
|
||||
const description = !!entry.description ? entry.description : jsDocDescription;
|
||||
const shortTextMatch = description.match(firstParagraphRule);
|
||||
const htmlDescription = getHtmlForJsDocText(description, entry).trim();
|
||||
const shortHtmlDescription = getHtmlForJsDocText(
|
||||
shortTextMatch ? shortTextMatch[0] : '',
|
||||
entry,
|
||||
).trim();
|
||||
return {
|
||||
...entry,
|
||||
htmlDescription,
|
||||
shortHtmlDescription,
|
||||
};
|
||||
const htmlDescription = getHtmlForJsDocText(description).trim();
|
||||
const shortHtmlDescription = getHtmlForJsDocText(shortTextMatch ? shortTextMatch[0] : '').trim();
|
||||
|
||||
return {...entry, htmlDescription, shortHtmlDescription};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +75,7 @@ export function addHtmlJsDocTagComments<T extends HasJsDocTags & HasModuleName>(
|
|||
...entry,
|
||||
jsdocTags: entry.jsdocTags.map((tag) => ({
|
||||
...tag,
|
||||
htmlComment: getHtmlForJsDocText(tag.comment, entry),
|
||||
htmlComment: getHtmlForJsDocText(tag.comment),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
|
@ -100,20 +94,16 @@ export function addHtmlUsageNotes<T extends HasJsDocTags>(entry: T): T & HasHtml
|
|||
const usageNotesTag = entry.jsdocTags.find(
|
||||
({name}) => name === JS_DOC_USAGE_NOTES_TAG || name === JS_DOC_REMARKS_TAG,
|
||||
);
|
||||
const htmlUsageNotes = usageNotesTag
|
||||
? (marked.parse(wrapExampleHtmlElementsWithCode(usageNotesTag.comment)) as string)
|
||||
: '';
|
||||
|
||||
const transformedHtml = addApiLinksToHtml(htmlUsageNotes);
|
||||
const htmlUsageNotes = usageNotesTag ? getHtmlForJsDocText(usageNotesTag.comment) : '';
|
||||
|
||||
return {
|
||||
...entry,
|
||||
htmlUsageNotes: transformedHtml,
|
||||
htmlUsageNotes,
|
||||
};
|
||||
}
|
||||
|
||||
/** Given a markdown JsDoc text, gets the rendered HTML. */
|
||||
function getHtmlForJsDocText<T extends HasModuleName>(text: string, entry: T): string {
|
||||
function getHtmlForJsDocText(text: string): string {
|
||||
const parsed = marked.parse(convertLinks(wrapExampleHtmlElementsWithCode(text))) as string;
|
||||
return addApiLinksToHtml(parsed);
|
||||
}
|
||||
|
|
@ -126,7 +116,7 @@ export function setEntryFlags<T extends HasJsDocTags & HasModuleName>(
|
|||
...entry,
|
||||
isDeprecated: isDeprecatedEntry(entry),
|
||||
deprecationMessage: deprecationMessage
|
||||
? getHtmlForJsDocText(deprecationMessage, entry)
|
||||
? getHtmlForJsDocText(deprecationMessage)
|
||||
: deprecationMessage,
|
||||
isDeveloperPreview: isDeveloperPreview(entry),
|
||||
isExperimental: isExperimental(entry),
|
||||
|
|
|
|||
|
|
@ -66,7 +66,11 @@
|
|||
.docs-reference-section-heading {
|
||||
padding-block-start: 3rem;
|
||||
|
||||
a {
|
||||
&--sub {
|
||||
padding-block-start: 1rem;
|
||||
}
|
||||
|
||||
& > a {
|
||||
@include anchor.docs-anchor();
|
||||
color: inherit;
|
||||
}
|
||||
|
|
@ -98,7 +102,7 @@
|
|||
z-index: 0;
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
&.docs-highlighted-card {
|
||||
box-shadow: 10px 4px 40px 0 rgba(0, 0, 0, 0.01);
|
||||
|
||||
&::before {
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@
|
|||
}
|
||||
|
||||
p > a,
|
||||
p > em > a,
|
||||
td > a,
|
||||
div > a:not(.docs-card),
|
||||
code > a,
|
||||
|
|
@ -93,6 +94,7 @@
|
|||
}
|
||||
|
||||
p > a,
|
||||
p > em > a,
|
||||
.docs-list a,
|
||||
.docs-card a {
|
||||
margin-block: 0;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {DOCUMENT} from '@angular/common';
|
|||
import {ReferenceScrollHandler} from '../services/reference-scroll-handler.service';
|
||||
import {API_SECTION_CLASS_NAME} from '../constants/api-reference-prerender.constants';
|
||||
|
||||
const HIGHLIGHTED_CARD_CLASS = 'highlighted';
|
||||
const HIGHLIGHTED_CARD_CLASS = 'docs-highlighted-card';
|
||||
|
||||
@Component({
|
||||
selector: 'adev-reference-page',
|
||||
|
|
|
|||
|
|
@ -188,7 +188,6 @@ export function optionsReducer<T extends Object>(dst: T, objs: T | T[]): T {
|
|||
* A reference to an Angular application running on a page.
|
||||
*
|
||||
* @usageNotes
|
||||
* {@a is-stable-examples}
|
||||
* ### isStable examples and caveats
|
||||
*
|
||||
* Note two important points about `isStable`, demonstrated in the examples below:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {NgAdapterInjector} from './util';
|
|||
* {@link UpgradeModule#upgrading-an-angular-1-service Upgrading an AngularJS service} below.
|
||||
* 4. Creation of an AngularJS service that wraps and exposes an Angular injectable
|
||||
* so that it can be injected into an AngularJS context. See `downgradeInjectable`.
|
||||
* 3. Bootstrapping of a hybrid Angular application which contains both of the frameworks
|
||||
* 5. Bootstrapping of a hybrid Angular application which contains both of the frameworks
|
||||
* coexisting in a single application.
|
||||
*
|
||||
* @usageNotes
|
||||
|
|
@ -102,7 +102,7 @@ import {NgAdapterInjector} from './util';
|
|||
*
|
||||
* ### Examples
|
||||
*
|
||||
* Import the `UpgradeModule` into your top level {@link NgModule Angular `NgModule`}.
|
||||
* Import the `UpgradeModule` into your top level Angular {@link NgModule NgModule}.
|
||||
*
|
||||
* {@example upgrade/static/ts/full/module.ts region='ng2-module'}
|
||||
*
|
||||
|
|
@ -116,7 +116,6 @@ import {NgAdapterInjector} from './util';
|
|||
*
|
||||
* {@example upgrade/static/ts/full/module.ts region='bootstrap-ng2'}
|
||||
*
|
||||
* {@a upgrading-an-angular-1-service}
|
||||
* ### Upgrading an AngularJS service
|
||||
*
|
||||
* There is no specific API for upgrading an AngularJS service. Instead you should just follow the
|
||||
|
|
|
|||
Loading…
Reference in a new issue