Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DM] core/components/button.javascript-api #882

Merged
merged 7 commits into from
Mar 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 8 additions & 35 deletions gulp/docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <parent>.<child> and remove old <child> 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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 3 additions & 2 deletions packages/docs/src/common/resolveDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
}
Expand All @@ -29,4 +30,4 @@ export function resolveDocs(componentName: string, key: React.Key) {
throw new Error(`Unknown @reactDocs component: ${componentName}`);
}
return React.createElement(docsComponent, { key });
}
};
5 changes: 3 additions & 2 deletions packages/docs/src/common/resolveExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -67,7 +68,7 @@ const Example: React.SFC<IExampleProps> = (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;
}
Expand All @@ -83,4 +84,4 @@ export function resolveExample(exampleName: string, key: React.Key) {
name={exampleName}
sourceUrl={[SRC_HREF_BASE, packageName, "examples", fileName].join("/")}
/>;
}
};
14 changes: 7 additions & 7 deletions packages/docs/src/components/navMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ export const NavMenuItem: React.SFC<INavMenuItemProps & { children?: React.React
const classes = classNames(
"docs-menu-item",
`docs-menu-item-${isPageNode(item) ? "page" : "heading"}`,
`depth-${item.depth}`,
`depth-${item.level}`,
props.className,
);
const itemClasses = classNames(Classes.MENU_ITEM, {
[Classes.ACTIVE]: props.isActive,
[Classes.INTENT_PRIMARY]: props.isActive,
});
const handleClick = () => props.onClick(item.reference);
const handleClick = () => props.onClick(item.route);
const title = props.children ? <strong>{item.title}</strong> : item.title;
return (
<li className={classes} key={item.reference}>
<a className={itemClasses} href={"#" + item.reference} onClick={handleClick}>
<li className={classes} key={item.route}>
<a className={itemClasses} href={"#" + item.route} onClick={handleClick}>
{title}
</a>
{props.children}
Expand All @@ -52,16 +52,16 @@ NavMenuItem.displayName = "Docs.NavMenuItem";

export const NavMenu: React.SFC<INavMenuProps> = (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)
? <NavMenu {...props} className={menuClasses} items={section.children} />
: undefined;
return (
<NavMenuItem
key={section.reference}
key={section.route}
item={section}
isActive={isActive}
onClick={props.onItemClick}
Expand Down
12 changes: 6 additions & 6 deletions packages/docs/src/components/navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { handleStringChange } from "@blueprintjs/core/examples/common/baseExample";

import * as classNames from "classnames";
import { IHeadingNode, IPageNode, isPageNode } from "documentalist/dist/client";
import { IHeadingNode, IPageNode } from "documentalist/dist/client";
import { filter } from "fuzzaldrin-plus";
import * as PureRender from "pure-render-decorator";
import * as React from "react";
Expand All @@ -41,7 +41,7 @@ export interface INavigatorState {
interface INavigationSection {
filterKey: string;
path: string[];
reference: string;
route: string;
title: string;
}

Expand Down Expand Up @@ -114,10 +114,10 @@ export class Navigator extends React.Component<INavigatorProps, INavigatorState>
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 });
});
}

Expand All @@ -142,8 +142,8 @@ export class Navigator extends React.Component<INavigatorProps, INavigatorState>
return (
<a
className={classes}
href={"#" + section.reference}
key={section.reference}
href={"#" + section.route}
key={section.route}
onMouseEnter={this.handleResultHover}
>
<small className="docs-result-path pt-text-muted" dangerouslySetInnerHTML={pathHtml} />
Expand Down
6 changes: 3 additions & 3 deletions packages/docs/src/components/page.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,7 +21,7 @@ export const Page: React.SFC<IPageProps> = ({ 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 <h3><code>{ex.message}</code></h3>;
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/components/propsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>((a, b) => typeof b === "string" ? a + b : a, "");
const html = documentation.contents.reduce<string>((a, b) => typeof b === "string" ? a + b : a, "");

return (
<tr key={name}>
Expand Down
23 changes: 12 additions & 11 deletions packages/docs/src/components/styleguide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ export interface IStyleguideState {
@HotkeysTarget
@PureRender
export class Styleguide extends React.Component<IStyleguideProps, IStyleguideState> {
/** 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;
Expand All @@ -95,10 +95,10 @@ export class Styleguide extends React.Component<IStyleguideProps, IStyleguideSta
};

// build up static map of all references to their page, for navigation / routing
this.referenceToPage = {};
eachLayoutNode(this.props.layout, (node, [parent]) => {
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;
});
}

Expand Down Expand Up @@ -187,7 +187,7 @@ export class Styleguide extends React.Component<IStyleguideProps, IStyleguideSta

private handleNavigation = (activeSectionId: string) => {
// 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 });
}
Expand All @@ -210,10 +210,11 @@ export class Styleguide extends React.Component<IStyleguideProps, IStyleguideSta
}

private maybeScrollToActivePageMenuItem() {
const { activePageId } = this.state;
const { activeSectionId } = this.state;
// only scroll nav menu if active item is not visible in viewport.
// using activePageId so you can see the page title in nav (may not be visible in document).
const navMenuElement = this.navElement.query(`a[href="#${activePageId}"]`);
// using activeSectionId so you can see the page title in nav (may not be visible in document).
const navMenuElement = this.navElement
.query(`a[href="#${activeSectionId}"]`).closest(".docs-menu-item-page");
const innerBounds = navMenuElement.getBoundingClientRect();
const outerBounds = this.navElement.getBoundingClientRect();
if (innerBounds.top < outerBounds.top || innerBounds.bottom > outerBounds.bottom) {
Expand All @@ -230,7 +231,7 @@ export class Styleguide extends React.Component<IStyleguideProps, IStyleguideSta
// active section cannot actually be selected in the nav (often a short one at the end).
const currentSectionId = location.hash.slice(1);
// this map is built by an in-order traversal so the keys are actually sorted correctly!
const sections = Object.keys(this.referenceToPage);
const sections = Object.keys(this.routeToPage);
const index = sections.indexOf(currentSectionId);
const newIndex = index === -1 ? 0 : (index + direction + sections.length) % sections.length;
// updating hash triggers event listener which sets new state.
Expand Down
50 changes: 21 additions & 29 deletions packages/docs/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import { CssExample } from "./common/cssExample";
import { PropsStore } from "./common/propsStore";
import { resolveDocs } from "./common/resolveDocs";
import { resolveExample } from "./common/resolveExample";
import { TagRenderer } from "./components/page";
import { PropsTable } from "./components/propsTable";
import { IPackageInfo, Styleguide } from "./components/styleguide";

import { IPageData, IPageNode, slugify } from "documentalist/dist/client";
import { IHeadingTag, IPageNode } from "documentalist/dist/client";
import { IKssPluginData, IMarkdownPluginData, ITypescriptPluginData } from "documentalist/dist/plugins";

interface IDocsData extends IKssPluginData, IMarkdownPluginData, ITypescriptPluginData {
Expand All @@ -40,46 +41,37 @@ const versions = require<string[]>("./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 <CssExample {...example} key={key} />;
}
};

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 <PropsTable key={key} name={name} props={props} />;
}

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" },
<a className="docs-anchor" key="anchor" name={reference} />,
<a className="docs-anchor-link" href={"#" + reference} key="link">
<span className="pt-icon-standard pt-icon-link" />
</a>,
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));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🎆 no need to re-derive references since we've got route now

return <Heading depth={depth} header={heading} key={key} reference={ref} />;
};
}
const Heading: React.SFC<IHeadingTag> = ({ level, route, value }) => (
// use createElement so we can dynamically choose tag based on depth
React.createElement(`h${level}`, { className: "docs-title" },
<a className="docs-anchor" key="anchor" name={route} />,
<a className="docs-anchor-link" href={"#" + route} key="link">
<span className="pt-icon-standard pt-icon-link" />
</a>,
value,
)
);
Heading.displayName = "Docs.Heading";
const renderHeading: TagRenderer = (heading: IHeadingTag, key) => <Heading key={key} {...heading} />;

// 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,
Expand All @@ -98,9 +90,9 @@ const updateExamples = () => {
ReactDOM.render(
<Styleguide
defaultPageId="styleguide"
layout={docs.layout}
layout={docs.nav}
onUpdate={updateExamples}
pages={docs.docs}
pages={docs.pages}
releases={releases}
tagRenderers={TAGS}
versions={versions}
Expand Down