Skip to content

Commit

Permalink
improve typescript documentation: enums & aliases! (#1982)
Browse files Browse the repository at this point in the history
* tag renderers are just SFCs!
remove page arg from renderBlock

* add context to Documentation component to expose renderers and docs data to children

* rewrite all tag renderers to be simple SFCs and use context for rendering

* typescript tag supports enums and type aliases too!!

* type IDocsData requires markdown, optional typescript & kss data.

* ApiHeader component now includes kind, inheritance, package/source link

* don't use ApiLink yet (next PR)

* createDefaultRenderers()

* documentalist 1.0.0-beta.2, add docs-data script to root, fix dev:docs

* minor code cleanup

* tsconfigPath option fixes interface inheritance!!!

* context docs

* documentalist beta.4, sourceUrl, react class

* markdownCode util

* Documentation renderViewSourceLinkText prop

* remove unnecessary dependency

* DeprecatedTag SFC

* class CssExample

* tag renderers can be SFCs or classes
  • Loading branch information
giladgray authored Jan 25, 2018
1 parent 6b32365 commit 02c4a98
Show file tree
Hide file tree
Showing 28 changed files with 536 additions and 255 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Each library has its own dev script which you can run to watch changes to that p

Much of Blueprint's documentation lives inside source code as JSDoc comments in `.tsx` files and KSS markup in `.scss` files. This documentation is extracted and converted into static JSON data using [documentalist](https://github.com/palantir/documentalist/).

If you are updating documentation sources (_not_ the docs UI code which lives in `packages/docs-app` or the docs theme in `packages/docs-theme`), you'll need to run `yarn compile` from `packages/docs-data` to see changes reflected in the application.
If you are updating documentation sources (_not_ the docs UI code which lives in `packages/docs-app` or the docs theme in `packages/docs-theme`), you'll need to run `yarn compile` from `packages/docs-data` to see changes reflected in the application. For simplicity, an alias script `yarn docs-data` exists in the root to minimize directory hopping.

### Updating icons

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"deploy": "gh-pages -d docs -b master",
"dev:all": "lerna run dev --parallel --scope '!@blueprintjs/{landing-app,table-dev-app}'",
"dev:core": "lerna run dev --parallel --scope '@blueprintjs/{core,docs-app}'",
"dev:docs": "lerna run dev --parallel --scope '@blueprintjs/{docs,docs-app}'",
"dev:docs": "lerna run dev --parallel --scope '@blueprintjs/{docs-app,docs-theme}'",
"dev:datetime": "lerna run dev --parallel --scope '@blueprintjs/{core,datetime,docs-app}'",
"dev:labs": "lerna run dev --parallel --scope '@blueprintjs/{core,labs,select,docs-app}'",
"dev:landing": "lerna run dev --parallel --scope '@blueprintjs/{core,landing-app}'",
Expand All @@ -26,6 +26,7 @@
"dist:libs": "lerna run dist --parallel --scope '@blueprintjs/{core,datetime,docs-theme,icons,labs,select,table,timezone}'",
"dist:apps": "lerna run dist --parallel --scope '@blueprintjs/{docs-app,landing-app,table-dev-app}'",
"dist:docs": "run-s clean-docs copy-docs-app copy-landing-app",
"docs-data": "lerna run compile --scope '@blueprintjs/docs-data'",
"lint": "lerna run --parallel lint",
"lint-fix": "lerna run --parallel lint-fix",
"test": "lerna run --parallel test",
Expand Down
6 changes: 6 additions & 0 deletions packages/docs-app/src/components/blueprintDocs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { Menu, MenuItem, Popover, Position, setHotkeysDialogProps } from "@blueprintjs/core";
import { IPackageInfo } from "@blueprintjs/docs-data";
import { Documentation, IDocumentationProps } from "@blueprintjs/docs-theme";
import { ITsDocBase } from "documentalist/dist/client";
import * as React from "react";
import { NavbarActions } from "./navbarActions";

Expand Down Expand Up @@ -59,6 +60,7 @@ export class BlueprintDocs extends React.Component<IBlueprintDocsProps, { themeN
navbarLeft={navbarLeft}
navbarRight={navbarRight}
onComponentUpdate={this.handleComponentUpdate}
renderViewSourceLinkText={this.renderViewSourceLinkText}
/>
</>
);
Expand Down Expand Up @@ -89,6 +91,10 @@ export class BlueprintDocs extends React.Component<IBlueprintDocsProps, { themeN
);
}

private renderViewSourceLinkText(entry: ITsDocBase) {
return `@blueprintjs/${entry.fileName.split("/", 2)[1]}`;
}

// This function is called whenever the documentation page changes and should be used to
// run non-React code on the newly rendered sections.
private handleComponentUpdate = () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const reactDocs = new ReactDocsTagRenderer(ReactDocs as any);
const reactExample = new ReactExampleTagRenderer(reactExamples);

const tagRenderers = {
...createDefaultRenderers(docs),
...createDefaultRenderers(),
reactDocs: reactDocs.render,
reactExample: reactExample.render,
};
Expand Down
3 changes: 2 additions & 1 deletion packages/docs-data/compile-docs-data
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ function generateDocumentalistData() {
new dm.TypescriptPlugin({
excludeNames: [/I.+State$/],
excludePaths: ["node_modules/", "-app/", "test-commons/"],
tsconfigPath: path.resolve(__dirname, "../../config/tsconfig.base.json"),
}),
)
.use(".scss", new dm.KssPlugin())
.documentGlobs("../*/src/**/*.{ts,tsx,scss,md}")
.documentGlobs("../*/src/**/*.{scss,md}", "../*/src/index.{ts,tsx}")
.then(docs => JSON.stringify(docs, transformDocumentalistData, 2))
.then(content => fs.writeFileSync(path.join(GENERATED_SRC_DIR, DOCS_DATA_FILENAME), content));
}
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"@blueprintjs/docs-theme": "^2.0.0-beta.1",
"documentalist": "^1.0.0-beta.1",
"documentalist": "^1.0.0-beta.4",
"glob": "^7.1.2",
"highlights": "^3.1.1",
"marked": "^0.3.6",
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-theme/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"dependencies": {
"@blueprintjs/core": "^2.0.0-beta.1",
"classnames": "^2.2",
"documentalist": "^1.0.0-beta.1",
"documentalist": "^1.0.0-beta.4",
"fuzzaldrin-plus": "^0.5.0",
"tslib": "^1.5.0"
},
Expand Down
77 changes: 77 additions & 0 deletions packages/docs-theme/src/common/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2017 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import { Utils } from "@blueprintjs/core";
import {
IBlock,
IKssPluginData,
IMarkdownPluginData,
ITsDocBase,
ITypescriptPluginData,
} from "documentalist/dist/client";

/** This docs theme requires Markdown data and optionally supports Typescript and KSS data. */
export type IDocsData = IMarkdownPluginData & (ITypescriptPluginData | {}) & (IKssPluginData | {});

export function hasTypescriptData(docs: IDocsData): docs is IMarkdownPluginData & ITypescriptPluginData {
return docs != null && (docs as ITypescriptPluginData).typescript != null;
}

export function hasKssData(docs: IDocsData): docs is IMarkdownPluginData & IKssPluginData {
return docs != null && (docs as IKssPluginData).css != null;
}

/**
* Use React context to transparently provide helpful functions to children.
* This is basically the pauper's Redux store connector: some central state from the root
* `Documentation` component is exposed to its children so those in the know can speak
* directly to their parent.
*/
export interface IDocumentationContext {
/**
* Get the Documentalist data.
* Use the `hasTypescriptData` and `hasKssData` typeguards before accessing those plugins' data.
*/
getDocsData(): IDocsData;

/** Render a block of Documentalist documentation to a React node. */
renderBlock(block: IBlock): React.ReactNode;

/** Render a Documentalist Typescript type string to a React node. */
renderType(type: string): React.ReactNode;

/** Render the text of a "View source" link. */
renderViewSourceLinkText(entry: ITsDocBase): React.ReactNode;
}

/**
* To enable context access in a React component, assign `static contextTypes` and declare `context` type:
*
* ```tsx
* export class ContextComponent extends React.PureComponent<IApiLinkProps> {
* public static contextTypes = DocumentationContextTypes;
* public context: IDocumentationContext;
*
* public render() {
* return this.context.renderBlock(this.props.block);
* }
* }
* ```
*/
export const DocumentationContextTypes: React.ValidationMap<IDocumentationContext> = {
getDocsData: assertFunctionProp,
renderBlock: assertFunctionProp,
renderType: assertFunctionProp,
renderViewSourceLinkText: assertFunctionProp,
};

// simple alternative to prop-types dependency
function assertFunctionProp<T>(obj: T, key: keyof T) {
if (obj[key] != null && Utils.isFunction(obj[key])) {
return undefined;
}
return new Error(`[Blueprint] Documentation context ${key} must be function.`);
}
10 changes: 5 additions & 5 deletions packages/docs-theme/src/components/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
* Licensed under the terms of the LICENSE file distributed with this project.
*/

import { IBlock, IPageData } from "documentalist/dist/client";
import { IBlock } from "documentalist/dist/client";
import * as React from "react";

import { ITagRendererMap, TagElement } from "../tags";
import { ITagRendererMap } from "../tags";

export function renderBlock(block: IBlock | undefined, tagRenderers: ITagRendererMap, page?: IPageData): TagElement[] {
export function renderBlock(block: IBlock | undefined, tagRenderers: ITagRendererMap): JSX.Element[] {
if (block === undefined) {
return [];
}
Expand All @@ -22,11 +22,11 @@ export function renderBlock(block: IBlock | undefined, tagRenderers: ITagRendere
if (renderer === undefined) {
throw new Error(`Unknown @tag: ${node.tag}`);
}
return renderer(node, i, tagRenderers, page);
return React.createElement(renderer, { ...node, key: i });
} catch (ex) {
console.error(ex.message);
return (
<h3>
<h3 key={`__error-${i}`}>
<code>{ex.message}</code>
</h3>
);
Expand Down
35 changes: 30 additions & 5 deletions packages/docs-theme/src/components/documentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
*/

import * as classNames from "classnames";
import { IMarkdownPluginData, isPageNode } from "documentalist/dist/client";
import { isPageNode, ITsDocBase, linkify } from "documentalist/dist/client";
import * as React from "react";

import { FocusStyleManager, Hotkey, Hotkeys, HotkeysTarget, IProps, Utils } from "@blueprintjs/core";

import { DocumentationContextTypes, hasTypescriptData, IDocsData, IDocumentationContext } from "../common/context";
import { eachLayoutNode } from "../common/utils";
import { TagRenderer } from "../tags";
import { ITagRendererMap } from "../tags";
import { renderBlock } from "./block";
import { Navigator } from "./navigator";
import { NavMenu } from "./navMenu";
import { Page } from "./page";
Expand All @@ -24,9 +26,9 @@ export interface IDocumentationProps extends IProps {

/**
* All the docs data from Documentalist.
* Must include at least `{ nav, pages }` from the MarkdownPlugin.
* This theme requires the Markdown plugin, and optionally supports Typescript and KSS data.
*/
docs: IMarkdownPluginData;
docs: IDocsData;

/**
* Callback invoked whenever the component props or state change (specifically,
Expand All @@ -35,8 +37,15 @@ export interface IDocumentationProps extends IProps {
*/
onComponentUpdate?: (pageId: string) => void;

/**
* Callback invoked to render "View source" links in Typescript interfaces.
* The `href` of the link will be `entry.sourceUrl`.
* @default "View source"
*/
renderViewSourceLinkText?: (entry: ITsDocBase) => React.ReactNode;

/** Tag renderer functions. Unknown tags will log console errors. */
tagRenderers: { [tag: string]: TagRenderer };
tagRenderers: ITagRendererMap;

/**
* Elements to render on the left side of the navbar, typically logo and title.
Expand All @@ -59,6 +68,8 @@ export interface IDocumentationState {

@HotkeysTarget
export class Documentation extends React.PureComponent<IDocumentationProps, IDocumentationState> {
public static childContextTypes = DocumentationContextTypes;

public static defaultProps = {
navbarLeft: "Documentation",
};
Expand Down Expand Up @@ -88,6 +99,20 @@ export class Documentation extends React.PureComponent<IDocumentationProps, IDoc
});
}

public getChildContext(): IDocumentationContext {
const { docs, renderViewSourceLinkText } = this.props;
return {
getDocsData: () => docs,
renderBlock: block => renderBlock(block, this.props.tagRenderers),
renderType: hasTypescriptData(docs)
? type => linkify(type, docs.typescript, name => <u key={name}>{name}</u>)
: type => type,
renderViewSourceLinkText: Utils.isFunction(renderViewSourceLinkText)
? renderViewSourceLinkText
: () => "View source",
};
}

public render() {
const { activePageId, activeSectionId } = this.state;
const { nav, pages } = this.props.docs;
Expand Down
Loading

1 comment on commit 02c4a98

@blueprint-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

improve typescript documentation: enums & aliases! (#1982)

Preview: documentation | landing | table

Please sign in to comment.