From 9deb01ce519f2cd106bd87fd330d0b38612e25ab Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Tue, 28 Mar 2017 14:08:34 -0700 Subject: [PATCH] [DM] core/components/button.javascript-api (#882) * nav layout code moves to MarkdownPlugin but must configure all plugins manually now * support IHeadingTag with single "heading" renderer TagRenderer receives ITag as first argument to support this polymorphism. use type in renderer definition to avoid redeclaring each argument. deconstruct and rename `value` as needed for minimal churn and maximal clarity. * refactor to support `route` property * IBlock field rename * documentalist@0.0.5 --- gulp/docs.js | 43 ++++-------------- package.json | 2 +- packages/docs/src/common/resolveDocs.ts | 5 ++- packages/docs/src/common/resolveExample.tsx | 5 ++- packages/docs/src/components/navMenu.tsx | 14 +++--- packages/docs/src/components/navigator.tsx | 12 ++--- packages/docs/src/components/page.tsx | 6 +-- packages/docs/src/components/propsTable.tsx | 2 +- packages/docs/src/components/styleguide.tsx | 23 +++++----- packages/docs/src/index.tsx | 50 +++++++++------------ 10 files changed, 65 insertions(+), 97 deletions(-) diff --git a/gulp/docs.js b/gulp/docs.js index 326d596b1b..e1089d9ff8 100644 --- a/gulp/docs.js +++ b/gulp/docs.js @@ -26,43 +26,16 @@ module.exports = (blueprint, gulp, plugins) => { }; gulp.task("docs-json", () => { - return dm.Documentalist.create({ renderer: text.renderer }) + return new dm.Documentalist([], { renderer: text.renderer }) + .use(".md", new dm.MarkdownPlugin({ navPage: config.navPage })) + .use(/\.tsx?$/, new dm.TypescriptPlugin()) .use(".scss", new dm.KssPlugin()) .documentGlobs("packages/*/src/**/*") - .then((contents) => { - function nestChildPage(child, parent) { - const originalRef = child.reference; - - // update entry reference to include parent reference - const nestedRef = dm.slugify(parent.reference, originalRef); - child.reference = nestedRef; - - if (dm.isPageNode(child)) { - // rename nested pages to be . and remove old entry - contents.docs[nestedRef] = contents.docs[originalRef]; - contents.docs[nestedRef].reference = nestedRef; - delete contents.docs[originalRef]; - // recurse through page children - child.children.forEach((innerchild) => nestChildPage(innerchild, child)); - } - } - - // navPage is used to construct the sidebar menu - const roots = dm.createNavigableTree(contents.docs, contents.docs[config.navPage]).children; - // nav page is not a real docs page so we can remove it from output - delete contents.docs[config.navPage]; - roots.forEach((page) => { - if (dm.isPageNode(page)) { - page.children.forEach((child) => nestChildPage(child, page)); - } - }); - - // add a new field to data file with pre-processed layout tree - contents.layout = roots; - - return text.fileStream(filenames.data, JSON.stringify(contents, null, 2)) - .pipe(gulp.dest(config.data)); - }); + .then((docs) => JSON.stringify(docs, null, 2)) + .then((content) => ( + text.fileStream(filenames.data, content) + .pipe(gulp.dest(config.data)) + )); }); // create a JSON file containing latest released version of each project diff --git a/package.json b/package.json index a39ca768e4..8fb990ab26 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "better-handlebars": "github:wmeldon/better-handlebars", "chai": "3.5.0", "del": "2.2.2", - "documentalist": "0.0.4", + "documentalist": "0.0.5", "enzyme": "2.6.0", "gulp": "3.9.1", "gulp-concat": "2.6.0", diff --git a/packages/docs/src/common/resolveDocs.ts b/packages/docs/src/common/resolveDocs.ts index b4f7675816..b7df97931e 100644 --- a/packages/docs/src/common/resolveDocs.ts +++ b/packages/docs/src/common/resolveDocs.ts @@ -8,6 +8,7 @@ import * as React from "react"; // this is the default map, containing docs components defined locally. +import { TagRenderer } from "../components/page"; import * as ReactDocs from "../components/reactDocs"; export interface IDocsMap { @@ -19,7 +20,7 @@ export interface IDocsMap { * it to an actual component class in the given map, or in the default map which contains * valid docs components from this package. Provide a custom map to inject your own components. */ -export function resolveDocs(componentName: string, key: React.Key) { +export const resolveDocs: TagRenderer = ({ value: componentName }, key) => { if (componentName == null) { return undefined; } @@ -29,4 +30,4 @@ export function resolveDocs(componentName: string, key: React.Key) { throw new Error(`Unknown @reactDocs component: ${componentName}`); } return React.createElement(docsComponent, { key }); -} +}; diff --git a/packages/docs/src/common/resolveExample.tsx b/packages/docs/src/common/resolveExample.tsx index e2596ea1cd..16c28be248 100644 --- a/packages/docs/src/common/resolveExample.tsx +++ b/packages/docs/src/common/resolveExample.tsx @@ -11,6 +11,7 @@ import * as CoreExamples from "@blueprintjs/core/examples"; import * as DateExamples from "@blueprintjs/datetime/examples"; import * as TableExamples from "@blueprintjs/table/examples"; +import { TagRenderer } from "src/components/page"; import { getTheme } from "./theme"; // tslint:disable-next-line no-empty-interface @@ -67,7 +68,7 @@ const Example: React.SFC = (props) => ( * it to an actual example component exported by one of the packages. Also returns * the URL of the source code on GitHub. */ -export function resolveExample(exampleName: string, key: React.Key) { +export const resolveExample: TagRenderer = ({ value: exampleName }, key) => { if (exampleName == null) { return undefined; } @@ -83,4 +84,4 @@ export function resolveExample(exampleName: string, key: React.Key) { name={exampleName} sourceUrl={[SRC_HREF_BASE, packageName, "examples", fileName].join("/")} />; -} +}; diff --git a/packages/docs/src/components/navMenu.tsx b/packages/docs/src/components/navMenu.tsx index 74b3e8ab19..7b0f82deca 100644 --- a/packages/docs/src/components/navMenu.tsx +++ b/packages/docs/src/components/navMenu.tsx @@ -30,18 +30,18 @@ export const NavMenuItem: React.SFC props.onClick(item.reference); + const handleClick = () => props.onClick(item.route); const title = props.children ? {item.title} : item.title; return ( -
  • - +
  • + {title} {props.children} @@ -52,8 +52,8 @@ NavMenuItem.displayName = "Docs.NavMenuItem"; export const NavMenu: React.SFC = (props) => { const menu = props.items.map((section) => { - const isActive = props.activeSectionId === section.reference; - const isExpanded = isActive || props.activePageId === section.reference; + const isActive = props.activeSectionId === section.route; + const isExpanded = isActive || props.activePageId === (section as IPageNode).reference; // active section gets selected styles, expanded section shows its children const menuClasses = classNames({ "docs-nav-expanded": isExpanded }); const childrenMenu = isPageNode(section) @@ -61,7 +61,7 @@ export const NavMenu: React.SFC = (props) => { : undefined; return ( public componentDidMount() { this.sections = []; eachLayoutNode(this.props.items, (node, parents) => { - const { reference, title } = node; + const { route, title } = node; const path = parents.map((p) => p.title).reverse(); const filterKey = [...path, title].join("/"); - this.sections.push({ filterKey, path, reference, title }); + this.sections.push({ filterKey, path, route, title }); }); } @@ -142,8 +142,8 @@ export class Navigator extends React.Component return ( diff --git a/packages/docs/src/components/page.tsx b/packages/docs/src/components/page.tsx index 9dd5146262..34dbe52616 100644 --- a/packages/docs/src/components/page.tsx +++ b/packages/docs/src/components/page.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import { IPageData } from "documentalist/dist/client"; +import { IPageData, ITag } from "documentalist/dist/client"; -export type TagRenderer = (value: string, key: React.Key, page: IPageData) => JSX.Element | undefined; +export type TagRenderer = (tag: ITag, key: React.Key, page: IPageData) => JSX.Element | undefined; export interface IPageProps { page: IPageData; @@ -21,7 +21,7 @@ export const Page: React.SFC = ({ tagRenderers, page }) => { if (renderer === undefined) { throw new Error(`Unknown @tag: ${node.tag}`); } - return renderer(node.value, i, page); + return renderer(node, i, page); } catch (ex) { console.error(ex.message); return

    {ex.message}

    ; diff --git a/packages/docs/src/components/propsTable.tsx b/packages/docs/src/components/propsTable.tsx index a75134b05d..d64b52255b 100644 --- a/packages/docs/src/components/propsTable.tsx +++ b/packages/docs/src/components/propsTable.tsx @@ -58,7 +58,7 @@ const renderPropRow = (prop: IInheritedPropertyEntry) => { // TODO: this ignores tags in prop docs, but that's kind of OK cuz they all get processed // into prop.tags by the TS compiler. - const html = documentation.renderedContent.reduce((a, b) => typeof b === "string" ? a + b : a, ""); + const html = documentation.contents.reduce((a, b) => typeof b === "string" ? a + b : a, ""); return ( diff --git a/packages/docs/src/components/styleguide.tsx b/packages/docs/src/components/styleguide.tsx index 5a5111eaae..59c261f8b5 100644 --- a/packages/docs/src/components/styleguide.tsx +++ b/packages/docs/src/components/styleguide.tsx @@ -76,8 +76,8 @@ export interface IStyleguideState { @HotkeysTarget @PureRender export class Styleguide extends React.Component { - /** Map of section reference to containing page reference. */ - private referenceToPage: { [reference: string]: string }; + /** Map of section route to containing page reference. */ + private routeToPage: { [route: string]: string }; private contentElement: HTMLElement; private navElement: HTMLElement; @@ -95,10 +95,10 @@ export class Styleguide extends React.Component { - const { reference } = isPageNode(node) ? node : parent; - this.referenceToPage[node.reference] = reference; + this.routeToPage = {}; + eachLayoutNode(this.props.layout, (node, parents) => { + const { reference } = isPageNode(node) ? node : parents[0]; + this.routeToPage[node.route] = reference; }); } @@ -187,7 +187,7 @@ export class Styleguide extends React.Component { // only update state if this section reference is valid - const activePageId = this.referenceToPage[activeSectionId]; + const activePageId = this.routeToPage[activeSectionId]; if (activeSectionId !== undefined && activePageId !== undefined) { this.setState({ activePageId, activeSectionId }); } @@ -210,10 +210,11 @@ export class Styleguide extends React.Component outerBounds.bottom) { @@ -230,7 +231,7 @@ export class Styleguide extends React.Component("./generated/versions.json") } as IPackageInfo)); /* tslint:enable:no-var-requires */ -function resolveCssExample(reference: string, key: React.Key) { +const resolveCssExample: TagRenderer = ({ value: reference }, key) => { const example = docs.css[reference]; if (example === undefined || example.reference === undefined) { throw new Error(`Unknown @css reference: ${reference}`); } return ; -} +}; const propsStore = new PropsStore(docs.ts); -function resolveInterface(name: string, key: React.Key) { +const resolveInterface: TagRenderer = ({ value: name }, key) => { const props = propsStore.getProps(name); return ; -} - -const Heading: React.SFC<{ depth: number, header: string, reference: string }> = - ({ depth, header, reference }) => ( - // use createElement so we can dynamically choose tag based on depth - React.createElement(`h${depth}`, { className: "docs-title" }, -
    , - - - , - header, - ) - ); +}; -function renderHeading(depth: number) { - return (heading: string, key: React.Key, page: IPageData): JSX.Element => { - const ref = (depth === 1 ? page.reference : slugify(page.reference, heading)); - return ; - }; -} +const Heading: React.SFC = ({ level, route, value }) => ( + // use createElement so we can dynamically choose tag based on depth + React.createElement(`h${level}`, { className: "docs-title" }, + , + + + , + value, + ) +); +Heading.displayName = "Docs.Heading"; +const renderHeading: TagRenderer = (heading: IHeadingTag, key) => ; // tslint:disable:object-literal-key-quotes const TAGS = { - "#": renderHeading(1), - "##": renderHeading(2), - "###": renderHeading(3), - "####": renderHeading(4), css: resolveCssExample, + heading: renderHeading, interface: resolveInterface, page: () => undefined as JSX.Element, reactDocs: resolveDocs, @@ -98,9 +90,9 @@ const updateExamples = () => { ReactDOM.render(