aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts/pages/documentation
diff options
context:
space:
mode:
Diffstat (limited to 'packages/website/ts/pages/documentation')
-rw-r--r--packages/website/ts/pages/documentation/comment.tsx24
-rw-r--r--packages/website/ts/pages/documentation/custom_enum.tsx31
-rw-r--r--packages/website/ts/pages/documentation/enum.tsx26
-rw-r--r--packages/website/ts/pages/documentation/event_definition.tsx80
-rw-r--r--packages/website/ts/pages/documentation/interface.tsx54
-rw-r--r--packages/website/ts/pages/documentation/method_block.tsx174
-rw-r--r--packages/website/ts/pages/documentation/method_signature.tsx62
-rw-r--r--packages/website/ts/pages/documentation/smart_contracts_documentation.tsx401
-rw-r--r--packages/website/ts/pages/documentation/source_link.tsx27
-rw-r--r--packages/website/ts/pages/documentation/type.tsx187
-rw-r--r--packages/website/ts/pages/documentation/type_definition.tsx135
-rw-r--r--packages/website/ts/pages/documentation/zero_ex_js_documentation.tsx340
12 files changed, 1541 insertions, 0 deletions
diff --git a/packages/website/ts/pages/documentation/comment.tsx b/packages/website/ts/pages/documentation/comment.tsx
new file mode 100644
index 000000000..78bbdc069
--- /dev/null
+++ b/packages/website/ts/pages/documentation/comment.tsx
@@ -0,0 +1,24 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as ReactMarkdown from 'react-markdown';
+import {MarkdownCodeBlock} from 'ts/pages/shared/markdown_code_block';
+
+interface CommentProps {
+ comment: string;
+ className?: string;
+}
+
+const defaultProps = {
+ className: '',
+};
+
+export const Comment: React.SFC<CommentProps> = (props: CommentProps) => {
+ return (
+ <div className={`${props.className} comment`}>
+ <ReactMarkdown
+ source={props.comment}
+ renderers={{CodeBlock: MarkdownCodeBlock}}
+ />
+ </div>
+ );
+};
diff --git a/packages/website/ts/pages/documentation/custom_enum.tsx b/packages/website/ts/pages/documentation/custom_enum.tsx
new file mode 100644
index 000000000..aca8af832
--- /dev/null
+++ b/packages/website/ts/pages/documentation/custom_enum.tsx
@@ -0,0 +1,31 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {utils} from 'ts/utils/utils';
+import {CustomType} from 'ts/types';
+
+const STRING_ENUM_CODE_PREFIX = ' strEnum(';
+
+interface CustomEnumProps {
+ type: CustomType;
+}
+
+// This component renders custom string enums that was a work-around for versions of
+// TypeScript <2.4.0 that did not support them natively. We keep it around to support
+// older versions of 0x.js <0.9.0
+export function CustomEnum(props: CustomEnumProps) {
+ const type = props.type;
+ if (!_.startsWith(type.defaultValue, STRING_ENUM_CODE_PREFIX)) {
+ utils.consoleLog('We do not yet support `Variable` types that are not strEnums');
+ return null;
+ }
+ // Remove the prefix and postfix, leaving only the strEnum values without quotes.
+ const enumValues = type.defaultValue.slice(10, -3).replace(/'/g, '');
+ return (
+ <span>
+ {`{`}
+ {'\t'}{enumValues}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/website/ts/pages/documentation/enum.tsx b/packages/website/ts/pages/documentation/enum.tsx
new file mode 100644
index 000000000..9364a5d31
--- /dev/null
+++ b/packages/website/ts/pages/documentation/enum.tsx
@@ -0,0 +1,26 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {utils} from 'ts/utils/utils';
+import {TypeDocNode, EnumValue} from 'ts/types';
+
+const STRING_ENUM_CODE_PREFIX = ' strEnum(';
+
+interface EnumProps {
+ values: EnumValue[];
+}
+
+export function Enum(props: EnumProps) {
+ const values = _.map(props.values, (value, i) => {
+ const isLast = i === props.values.length - 1;
+ const defaultValueIfAny = !_.isUndefined(value.defaultValue) ? ` = ${value.defaultValue}` : '';
+ return `\n\t${value.name}${defaultValueIfAny},`;
+ });
+ return (
+ <span>
+ {`{`}
+ {values}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/website/ts/pages/documentation/event_definition.tsx b/packages/website/ts/pages/documentation/event_definition.tsx
new file mode 100644
index 000000000..58271e98f
--- /dev/null
+++ b/packages/website/ts/pages/documentation/event_definition.tsx
@@ -0,0 +1,80 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {constants} from 'ts/utils/constants';
+import {utils} from 'ts/utils/utils';
+import {Event, EventArg, HeaderSizes} from 'ts/types';
+import {Type} from 'ts/pages/documentation/type';
+import {AnchorTitle} from 'ts/pages/shared/anchor_title';
+
+const KEYWORD_COLOR = '#a81ca6';
+const CUSTOM_GREEN = 'rgb(77, 162, 75)';
+
+interface EventDefinitionProps {
+ event: Event;
+}
+
+interface EventDefinitionState {
+ shouldShowAnchor: boolean;
+}
+
+export class EventDefinition extends React.Component<EventDefinitionProps, EventDefinitionState> {
+ constructor(props: EventDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const event = this.props.event;
+ return (
+ <div
+ id={event.name}
+ className="pb2"
+ style={{overflow: 'hidden', width: '100%'}}
+ onMouseOver={this.setAnchorVisibility.bind(this, true)}
+ onMouseOut={this.setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`Event ${event.name}`}
+ id={event.name}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{fontSize: 16}}>
+ <pre>
+ <code className="hljs">
+ {this.renderEventCode()}
+ </code>
+ </pre>
+ </div>
+ </div>
+ );
+ }
+ private renderEventCode() {
+ const indexed = <span style={{color: CUSTOM_GREEN}}> indexed</span>;
+ const eventArgs = _.map(this.props.event.eventArgs, (eventArg: EventArg) => {
+ return (
+ <span key={`eventArg-${eventArg.name}`}>
+ {eventArg.name}{eventArg.isIndexed ? indexed : ''}: <Type type={eventArg.type} />,
+ </span>
+ );
+ });
+ const argList = _.reduce(eventArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}{argList}
+ <br />
+ {`}`}
+ </span>
+ );
+ }
+ private setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/website/ts/pages/documentation/interface.tsx b/packages/website/ts/pages/documentation/interface.tsx
new file mode 100644
index 000000000..9e40b8901
--- /dev/null
+++ b/packages/website/ts/pages/documentation/interface.tsx
@@ -0,0 +1,54 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {CustomType, TypeDocTypes} from 'ts/types';
+import {Type} from 'ts/pages/documentation/type';
+import {MethodSignature} from 'ts/pages/documentation/method_signature';
+
+interface InterfaceProps {
+ type: CustomType;
+}
+
+export function Interface(props: InterfaceProps) {
+ const type = props.type;
+ const properties = _.map(type.children, property => {
+ return (
+ <span key={`property-${property.name}-${property.type}-${type.name}`}>
+ {property.name}:{' '}
+ {property.type.typeDocType !== TypeDocTypes.Reflection ?
+ <Type type={property.type} /> :
+ <MethodSignature
+ method={property.type.method}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ />
+ },
+ </span>
+ );
+ });
+ const hasIndexSignature = !_.isUndefined(type.indexSignature);
+ if (hasIndexSignature) {
+ const is = type.indexSignature;
+ const param = (
+ <span key={`indexSigParams-${is.keyName}-${is.keyType}-${type.name}`}>
+ {is.keyName}: <Type type={is.keyType} />
+ </span>
+ );
+ properties.push((
+ <span key={`indexSignature-${type.name}-${is.keyType.name}`}>
+ [{param}]: {is.valueName},
+ </span>
+ ));
+ }
+ const propertyList = _.reduce(properties, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '\n\t', curr];
+ });
+ return (
+ <span>
+ {`{`}
+ <br />
+ {'\t'}{propertyList}
+ <br />
+ {`}`}
+ </span>
+ );
+}
diff --git a/packages/website/ts/pages/documentation/method_block.tsx b/packages/website/ts/pages/documentation/method_block.tsx
new file mode 100644
index 000000000..6fead2f47
--- /dev/null
+++ b/packages/website/ts/pages/documentation/method_block.tsx
@@ -0,0 +1,174 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as ReactMarkdown from 'react-markdown';
+import {Chip} from 'material-ui/Chip';
+import {colors} from 'material-ui/styles';
+import {
+ TypeDocNode,
+ Styles,
+ TypeDefinitionByName,
+ TypescriptMethod,
+ SolidityMethod,
+ Parameter,
+ HeaderSizes,
+} from 'ts/types';
+import {utils} from 'ts/utils/utils';
+import {SourceLink} from 'ts/pages/documentation/source_link';
+import {MethodSignature} from 'ts/pages/documentation/method_signature';
+import {AnchorTitle} from 'ts/pages/shared/anchor_title';
+import {Comment} from 'ts/pages/documentation/comment';
+import {typeDocUtils} from 'ts/utils/typedoc_utils';
+
+interface MethodBlockProps {
+ method: SolidityMethod|TypescriptMethod;
+ libraryVersion: string;
+ typeDefinitionByName: TypeDefinitionByName;
+}
+
+interface MethodBlockState {
+ shouldShowAnchor: boolean;
+}
+
+const styles: Styles = {
+ chip: {
+ fontSize: 13,
+ backgroundColor: colors.lightBlueA700,
+ color: 'white',
+ height: 11,
+ borderRadius: 14,
+ marginTop: 19,
+ lineHeight: 0.8,
+ },
+};
+
+export class MethodBlock extends React.Component<MethodBlockProps, MethodBlockState> {
+ constructor(props: MethodBlockProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const method = this.props.method;
+ if (typeDocUtils.isPrivateOrProtectedProperty(method.name)) {
+ return null;
+ }
+
+ return (
+ <div
+ id={method.name}
+ style={{overflow: 'hidden', width: '100%'}}
+ className="pb4"
+ onMouseOver={this.setAnchorVisibility.bind(this, true)}
+ onMouseOut={this.setAnchorVisibility.bind(this, false)}
+ >
+ {!method.isConstructor &&
+ <div className="flex">
+ {(method as TypescriptMethod).isStatic &&
+ this.renderChip('Static')
+ }
+ {(method as SolidityMethod).isConstant &&
+ this.renderChip('Constant')
+ }
+ {(method as SolidityMethod).isPayable &&
+ this.renderChip('Payable')
+ }
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={method.name}
+ id={method.name}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ </div>
+ }
+ <code className="hljs">
+ <MethodSignature
+ method={method}
+ typeDefinitionByName={this.props.typeDefinitionByName}
+ />
+ </code>
+ {(method as TypescriptMethod).source &&
+ <SourceLink
+ version={this.props.libraryVersion}
+ source={(method as TypescriptMethod).source}
+ />
+ }
+ {method.comment &&
+ <Comment
+ comment={method.comment}
+ className="py2"
+ />
+ }
+ {method.parameters && !_.isEmpty(method.parameters) &&
+ <div>
+ <h4
+ className="pb1 thin"
+ style={{borderBottom: '1px solid #e1e8ed'}}
+ >
+ ARGUMENTS
+ </h4>
+ {this.renderParameterDescriptions(method.parameters)}
+ </div>
+ }
+ {method.returnComment &&
+ <div className="pt1 comment">
+ <h4
+ className="pb1 thin"
+ style={{borderBottom: '1px solid #e1e8ed'}}
+ >
+ RETURNS
+ </h4>
+ <Comment
+ comment={method.returnComment}
+ />
+ </div>
+ }
+ </div>
+ );
+ }
+ private renderChip(text: string) {
+ return (
+ <div
+ className="p1 mr1"
+ style={styles.chip}
+ >
+ {text}
+ </div>
+ );
+ }
+ private renderParameterDescriptions(parameters: Parameter[]) {
+ const descriptions = _.map(parameters, parameter => {
+ const isOptional = parameter.isOptional;
+ return (
+ <div
+ key={`param-description-${parameter.name}`}
+ className="flex pb1 mb2"
+ style={{borderBottom: '1px solid #f0f4f7'}}
+ >
+ <div className="col lg-col-1 md-col-1 sm-hide xs-hide" />
+ <div className="col lg-col-3 md-col-3 sm-col-12 col-12">
+ <div className="bold">
+ {parameter.name}
+ </div>
+ <div className="pt1" style={{color: colors.grey500, fontSize: 14}}>
+ {isOptional && 'optional'}
+ </div>
+ </div>
+ <div className="col lg-col-8 md-col-8 sm-col-12 col-12">
+ {parameter.comment &&
+ <Comment
+ comment={parameter.comment}
+ />
+ }
+ </div>
+ </div>
+ );
+ });
+ return descriptions;
+ }
+ private setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/website/ts/pages/documentation/method_signature.tsx b/packages/website/ts/pages/documentation/method_signature.tsx
new file mode 100644
index 000000000..3b5d2ce78
--- /dev/null
+++ b/packages/website/ts/pages/documentation/method_signature.tsx
@@ -0,0 +1,62 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {TypescriptMethod, SolidityMethod, TypeDefinitionByName, Parameter} from 'ts/types';
+import {Type} from 'ts/pages/documentation/type';
+
+interface MethodSignatureProps {
+ method: TypescriptMethod|SolidityMethod;
+ shouldHideMethodName?: boolean;
+ shouldUseArrowSyntax?: boolean;
+ typeDefinitionByName?: TypeDefinitionByName;
+}
+
+const defaultProps = {
+ shouldHideMethodName: false,
+ shouldUseArrowSyntax: false,
+};
+
+export const MethodSignature: React.SFC<MethodSignatureProps> = (props: MethodSignatureProps) => {
+ const parameters = renderParameters(props.method, props.typeDefinitionByName);
+ const paramString = _.reduce(parameters, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, ', ', curr];
+ });
+ const methodName = props.shouldHideMethodName ? '' : props.method.name;
+ const typeParameterIfExists = _.isUndefined((props.method as TypescriptMethod).typeParameter) ?
+ undefined :
+ renderTypeParameter(props.method, props.typeDefinitionByName);
+ return (
+ <span>
+ {props.method.callPath}{methodName}{typeParameterIfExists}({paramString})
+ {props.shouldUseArrowSyntax ? ' => ' : ': '}
+ {' '}
+ {props.method.returnType &&
+ <Type type={props.method.returnType} typeDefinitionByName={props.typeDefinitionByName}/>
+ }
+ </span>
+ );
+};
+
+function renderParameters(method: TypescriptMethod|SolidityMethod, typeDefinitionByName?: TypeDefinitionByName) {
+ const parameters = method.parameters;
+ const params = _.map(parameters, (p: Parameter) => {
+ const isOptional = p.isOptional;
+ return (
+ <span key={`param-${p.type}-${p.name}`}>
+ {p.name}{isOptional && '?'}: <Type type={p.type} typeDefinitionByName={typeDefinitionByName}/>
+ </span>
+ );
+ });
+ return params;
+}
+
+function renderTypeParameter(method: TypescriptMethod, typeDefinitionByName?: TypeDefinitionByName) {
+ const typeParameter = method.typeParameter;
+ const typeParam = (
+ <span>
+ {`<${typeParameter.name} extends `}
+ <Type type={typeParameter.type} typeDefinitionByName={typeDefinitionByName}/>
+ {`>`}
+ </span>
+ );
+ return typeParam;
+}
diff --git a/packages/website/ts/pages/documentation/smart_contracts_documentation.tsx b/packages/website/ts/pages/documentation/smart_contracts_documentation.tsx
new file mode 100644
index 000000000..3e97829c4
--- /dev/null
+++ b/packages/website/ts/pages/documentation/smart_contracts_documentation.tsx
@@ -0,0 +1,401 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import DocumentTitle = require('react-document-title');
+import findVersions = require('find-versions');
+import semverSort = require('semver-sort');
+import {colors} from 'material-ui/styles';
+import CircularProgress from 'material-ui/CircularProgress';
+import {
+ scroller,
+} from 'react-scroll';
+import {Dispatcher} from 'ts/redux/dispatcher';
+import {
+ SmartContractsDocSections,
+ Styles,
+ DoxityDocObj,
+ TypeDefinitionByName,
+ DocAgnosticFormat,
+ SolidityMethod,
+ Property,
+ CustomType,
+ MenuSubsectionsBySection,
+ Event,
+ Docs,
+ AddressByContractName,
+ Networks,
+ EtherscanLinkSuffixes,
+} from 'ts/types';
+import {TopBar} from 'ts/components/top_bar';
+import {utils} from 'ts/utils/utils';
+import {docUtils} from 'ts/utils/doc_utils';
+import {constants} from 'ts/utils/constants';
+import {MethodBlock} from 'ts/pages/documentation/method_block';
+import {SourceLink} from 'ts/pages/documentation/source_link';
+import {Type} from 'ts/pages/documentation/type';
+import {TypeDefinition} from 'ts/pages/documentation/type_definition';
+import {MarkdownSection} from 'ts/pages/shared/markdown_section';
+import {Comment} from 'ts/pages/documentation/comment';
+import {Badge} from 'ts/components/ui/badge';
+import {EventDefinition} from 'ts/pages/documentation/event_definition';
+import {AnchorTitle} from 'ts/pages/shared/anchor_title';
+import {SectionHeader} from 'ts/pages/shared/section_header';
+import {NestedSidebarMenu} from 'ts/pages/shared/nested_sidebar_menu';
+import {doxityUtils} from 'ts/utils/doxity_utils';
+/* tslint:disable:no-var-requires */
+const IntroMarkdown = require('md/docs/smart_contracts/introduction');
+/* tslint:enable:no-var-requires */
+
+const SCROLL_TO_TIMEOUT = 500;
+const CUSTOM_PURPLE = '#690596';
+const CUSTOM_RED = '#e91751';
+const CUSTOM_TURQUOIS = '#058789';
+const DOC_JSON_ROOT = constants.S3_SMART_CONTRACTS_DOCUMENTATION_JSON_ROOT;
+
+const sectionNameToMarkdown = {
+ [SmartContractsDocSections.Introduction]: IntroMarkdown,
+};
+const networkNameToColor: {[network: string]: string} = {
+ [Networks.kovan]: CUSTOM_PURPLE,
+ [Networks.ropsten]: CUSTOM_RED,
+ [Networks.mainnet]: CUSTOM_TURQUOIS,
+};
+
+export interface SmartContractsDocumentationAllProps {
+ source: string;
+ location: Location;
+ dispatcher: Dispatcher;
+ docsVersion: string;
+ availableDocVersions: string[];
+}
+
+interface SmartContractsDocumentationState {
+ docAgnosticFormat?: DocAgnosticFormat;
+}
+
+const styles: Styles = {
+ mainContainers: {
+ position: 'absolute',
+ top: 60,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ overflowZ: 'hidden',
+ overflowY: 'scroll',
+ minHeight: 'calc(100vh - 60px)',
+ WebkitOverflowScrolling: 'touch',
+ },
+ menuContainer: {
+ borderColor: colors.grey300,
+ maxWidth: 330,
+ marginLeft: 20,
+ },
+};
+
+export class SmartContractsDocumentation extends
+ React.Component<SmartContractsDocumentationAllProps, SmartContractsDocumentationState> {
+ constructor(props: SmartContractsDocumentationAllProps) {
+ super(props);
+ this.state = {
+ docAgnosticFormat: undefined,
+ };
+ }
+ public componentWillMount() {
+ const pathName = this.props.location.pathname;
+ const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1);
+ const versions = findVersions(lastSegment);
+ const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined;
+ this.fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
+ }
+ public render() {
+ const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
+ ? {}
+ : this.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
+ return (
+ <div>
+ <DocumentTitle title="0x Smart Contract Documentation"/>
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ docsVersion={this.props.docsVersion}
+ availableDocVersions={this.props.availableDocVersions}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ shouldFullWidth={true}
+ doc={Docs.SmartContracts}
+ />
+ {_.isUndefined(this.state.docAgnosticFormat) ?
+ <div
+ className="col col-12"
+ style={styles.mainContainers}
+ >
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{height: 122, top: '50%', transform: 'translateY(-50%)'}}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{paddingBottom: 11}}>Loading documentation...</div>
+ </div>
+ </div> :
+ <div
+ className="mx-auto flex"
+ style={{color: colors.grey800, height: 43}}
+ >
+ <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
+ <div
+ className="border-right absolute"
+ style={{...styles.menuContainer, ...styles.mainContainers}}
+ >
+ <NestedSidebarMenu
+ selectedVersion={this.props.docsVersion}
+ versions={this.props.availableDocVersions}
+ topLevelMenu={constants.menuSmartContracts}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ doc={Docs.SmartContracts}
+ />
+ </div>
+ </div>
+ <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
+ <div
+ id="documentation"
+ style={styles.mainContainers}
+ className="absolute"
+ >
+ <div id="smartContractsDocs" />
+ <h1 className="md-pl2 sm-pl3">
+ <a href={constants.GITHUB_CONTRACTS_URL} target="_blank">
+ 0x Smart Contracts
+ </a>
+ </h1>
+ {this.renderDocumentation()}
+ </div>
+ </div>
+ </div>
+ }
+ </div>
+ );
+ }
+ private renderDocumentation(): React.ReactNode {
+ const subMenus = _.values(constants.menuSmartContracts);
+ const orderedSectionNames = _.flatten(subMenus);
+ // Since smart contract method params are all base types, no need to pass
+ // down the typeDefinitionByName
+ const typeDefinitionByName = {};
+ const sections = _.map(orderedSectionNames, this.renderSection.bind(this, typeDefinitionByName));
+
+ return sections;
+ }
+ private renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
+ const docSection = this.state.docAgnosticFormat[sectionName];
+
+ const markdownFileIfExists = sectionNameToMarkdown[sectionName];
+ if (!_.isUndefined(markdownFileIfExists)) {
+ return (
+ <MarkdownSection
+ key={`markdown-section-${sectionName}`}
+ sectionName={sectionName}
+ markdownContent={markdownFileIfExists}
+ />
+ );
+ }
+
+ if (_.isUndefined(docSection)) {
+ return null;
+ }
+
+ const sortedProperties = _.sortBy(docSection.properties, 'name');
+ const propertyDefs = _.map(sortedProperties, this.renderProperty.bind(this));
+
+ const sortedMethods = _.sortBy(docSection.methods, 'name');
+ const methodDefs = _.map(sortedMethods, method => {
+ const isConstructor = false;
+ return this.renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName);
+ });
+
+ const sortedEvents = _.sortBy(docSection.events, 'name');
+ const eventDefs = _.map(sortedEvents, (event: Event, i: number) => {
+ return (
+ <EventDefinition
+ key={`event-${event.name}-${i}`}
+ event={event}
+ />
+ );
+ });
+ return (
+ <div
+ key={`section-${sectionName}`}
+ className="py2 pr3 md-pl2 sm-pl3"
+ >
+ <div className="flex">
+ <div style={{marginRight: 7}}>
+ <SectionHeader sectionName={sectionName} />
+ </div>
+ {this.renderNetworkBadges(sectionName)}
+ </div>
+ {docSection.comment &&
+ <Comment
+ comment={docSection.comment}
+ />
+ }
+ {docSection.constructors.length > 0 &&
+ <div>
+ <h2 className="thin">Constructor</h2>
+ {this.renderConstructors(docSection.constructors, typeDefinitionByName)}
+ </div>
+ }
+ {docSection.properties.length > 0 &&
+ <div>
+ <h2 className="thin">Properties</h2>
+ <div>{propertyDefs}</div>
+ </div>
+ }
+ {docSection.methods.length > 0 &&
+ <div>
+ <h2 className="thin">Methods</h2>
+ <div>{methodDefs}</div>
+ </div>
+ }
+ {docSection.events.length > 0 &&
+ <div>
+ <h2 className="thin">Events</h2>
+ <div>{eventDefs}</div>
+ </div>
+ }
+ </div>
+ );
+ }
+ private renderNetworkBadges(sectionName: string) {
+ const networkToAddressByContractName = constants.contractAddresses[this.props.docsVersion];
+ const badges = _.map(networkToAddressByContractName,
+ (addressByContractName: AddressByContractName, networkName: string) => {
+ const contractAddress = addressByContractName[sectionName];
+ const linkIfExists = utils.getEtherScanLinkIfExists(
+ contractAddress, constants.networkIdByName[networkName], EtherscanLinkSuffixes.address,
+ );
+ return (
+ <a
+ key={`badge-${networkName}-${sectionName}`}
+ href={linkIfExists}
+ target="_blank"
+ style={{color: 'white', textDecoration: 'none'}}
+ >
+ <Badge
+ title={networkName}
+ backgroundColor={networkNameToColor[networkName]}
+ />
+ </a>
+ );
+ });
+ return badges;
+ }
+ private renderConstructors(constructors: SolidityMethod[],
+ typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
+ const constructorDefs = _.map(constructors, constructor => {
+ return this.renderMethodBlocks(
+ constructor, SmartContractsDocSections.zeroEx, constructor.isConstructor, typeDefinitionByName,
+ );
+ });
+ return (
+ <div>
+ {constructorDefs}
+ </div>
+ );
+ }
+ private renderProperty(property: Property): React.ReactNode {
+ return (
+ <div
+ key={`property-${property.name}-${property.type.name}`}
+ className="pb3"
+ >
+ <code className="hljs">
+ {property.name}: <Type type={property.type} />
+ </code>
+ {property.source &&
+ <SourceLink
+ version={this.props.docsVersion}
+ source={property.source}
+ />
+ }
+ {property.comment &&
+ <Comment
+ comment={property.comment}
+ className="py2"
+ />
+ }
+ </div>
+ );
+ }
+ private renderMethodBlocks(method: SolidityMethod, sectionName: string, isConstructor: boolean,
+ typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
+ return (
+ <MethodBlock
+ key={`method-${method.name}`}
+ method={method}
+ typeDefinitionByName={typeDefinitionByName}
+ libraryVersion={this.props.docsVersion}
+ />
+ );
+ }
+ private scrollToHash(): void {
+ const hashWithPrefix = this.props.location.hash;
+ let hash = hashWithPrefix.slice(1);
+ if (_.isEmpty(hash)) {
+ hash = 'smartContractsDocs'; // scroll to the top
+ }
+
+ scroller.scrollTo(hash, {duration: 0, offset: 0, containerId: 'documentation'});
+ }
+ private getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection {
+ const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
+ if (_.isUndefined(docAgnosticFormat)) {
+ return menuSubsectionsBySection;
+ }
+
+ const docSections = _.keys(SmartContractsDocSections);
+ _.each(docSections, sectionName => {
+ const docSection = docAgnosticFormat[sectionName];
+ if (_.isUndefined(docSection)) {
+ return; // no-op
+ }
+
+ if (sectionName === SmartContractsDocSections.types) {
+ const sortedTypesNames = _.sortBy(docSection.types, 'name');
+ const typeNames = _.map(sortedTypesNames, t => t.name);
+ menuSubsectionsBySection[sectionName] = typeNames;
+ } else {
+ const sortedEventNames = _.sortBy(docSection.events, 'name');
+ const eventNames = _.map(sortedEventNames, m => m.name);
+ const sortedMethodNames = _.sortBy(docSection.methods, 'name');
+ const methodNames = _.map(sortedMethodNames, m => m.name);
+ menuSubsectionsBySection[sectionName] = [...methodNames, ...eventNames];
+ }
+ });
+ return menuSubsectionsBySection;
+ }
+ private async fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> {
+ const versionToFileName = await docUtils.getVersionToFileNameAsync(DOC_JSON_ROOT);
+ const versions = _.keys(versionToFileName);
+ this.props.dispatcher.updateAvailableDocVersions(versions);
+ const sortedVersions = semverSort.desc(versions);
+ const latestVersion = sortedVersions[0];
+
+ let versionToFetch = latestVersion;
+ if (!_.isUndefined(preferredVersionIfExists)) {
+ const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists];
+ if (!_.isUndefined(preferredVersionFileNameIfExists)) {
+ versionToFetch = preferredVersionIfExists;
+ }
+ }
+ this.props.dispatcher.updateCurrentDocsVersion(versionToFetch);
+
+ const versionFileNameToFetch = versionToFileName[versionToFetch];
+ const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, DOC_JSON_ROOT);
+ const docAgnosticFormat = doxityUtils.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);
+
+ this.setState({
+ docAgnosticFormat,
+ }, () => {
+ this.scrollToHash();
+ });
+ }
+}
diff --git a/packages/website/ts/pages/documentation/source_link.tsx b/packages/website/ts/pages/documentation/source_link.tsx
new file mode 100644
index 000000000..2fb69e2f0
--- /dev/null
+++ b/packages/website/ts/pages/documentation/source_link.tsx
@@ -0,0 +1,27 @@
+import * as React from 'react';
+import {colors} from 'material-ui/styles';
+import {Source} from 'ts/types';
+import {constants} from 'ts/utils/constants';
+
+interface SourceLinkProps {
+ source: Source;
+ version: string;
+}
+
+export function SourceLink(props: SourceLinkProps) {
+ const source = props.source;
+ const githubUrl = constants.GITHUB_0X_JS_URL;
+ const sourceCodeUrl = `${githubUrl}/blob/v${props.version}/${source.fileName}#L${source.line}`;
+ return (
+ <div className="pt2" style={{fontSize: 14}}>
+ <a
+ href={sourceCodeUrl}
+ target="_blank"
+ className="underline"
+ style={{color: colors.grey500}}
+ >
+ Source
+ </a>
+ </div>
+ );
+}
diff --git a/packages/website/ts/pages/documentation/type.tsx b/packages/website/ts/pages/documentation/type.tsx
new file mode 100644
index 000000000..af18f97c2
--- /dev/null
+++ b/packages/website/ts/pages/documentation/type.tsx
@@ -0,0 +1,187 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {Link as ScrollLink} from 'react-scroll';
+import * as ReactTooltip from 'react-tooltip';
+import {colors} from 'material-ui/styles';
+import {typeDocUtils} from 'ts/utils/typedoc_utils';
+import {constants} from 'ts/utils/constants';
+import {Type as TypeDef, TypeDocTypes, TypeDefinitionByName} from 'ts/types';
+import {utils} from 'ts/utils/utils';
+import {TypeDefinition} from 'ts/pages/documentation/type_definition';
+
+const BUILT_IN_TYPE_COLOR = '#e69d00';
+const STRING_LITERAL_COLOR = '#4da24b';
+
+// Some types reference other libraries. For these types, we want to link the user to the relevant documentation.
+const typeToUrl: {[typeName: string]: string} = {
+ Web3: constants.WEB3_DOCS_URL,
+ Provider: constants.WEB3_PROVIDER_DOCS_URL,
+ BigNumber: constants.BIGNUMBERJS_GITHUB_URL,
+};
+
+const typeToSection: {[typeName: string]: string} = {
+ ExchangeWrapper: 'exchange',
+ TokenWrapper: 'token',
+ TokenRegistryWrapper: 'tokenRegistry',
+ EtherTokenWrapper: 'etherToken',
+ ProxyWrapper: 'proxy',
+ TokenTransferProxyWrapper: 'proxy',
+};
+
+interface TypeProps {
+ type: TypeDef;
+ typeDefinitionByName?: TypeDefinitionByName;
+}
+
+// The return type needs to be `any` here so that we can recursively define <Type /> components within
+// <Type /> components (e.g when rendering the union type).
+export function Type(props: TypeProps): any {
+ const type = props.type;
+ const isIntrinsic = type.typeDocType === TypeDocTypes.Intrinsic;
+ const isReference = type.typeDocType === TypeDocTypes.Reference;
+ const isArray = type.typeDocType === TypeDocTypes.Array;
+ const isStringLiteral = type.typeDocType === TypeDocTypes.StringLiteral;
+ let typeNameColor = 'inherit';
+ let typeName: string|React.ReactNode;
+ let typeArgs: React.ReactNode[] = [];
+ switch (type.typeDocType) {
+ case TypeDocTypes.Intrinsic:
+ case TypeDocTypes.Unknown:
+ typeName = type.name;
+ typeNameColor = BUILT_IN_TYPE_COLOR;
+ break;
+
+ case TypeDocTypes.Reference:
+ typeName = type.name;
+ typeArgs = _.map(type.typeArguments, (arg: TypeDef) => {
+ if (arg.typeDocType === TypeDocTypes.Array) {
+ const key = `type-${arg.elementType.name}-${arg.elementType.typeDocType}`;
+ return (
+ <span>
+ <Type
+ key={key}
+ type={arg.elementType}
+ typeDefinitionByName={props.typeDefinitionByName}
+ />[]
+ </span>
+ );
+ } else {
+ const subType = (
+ <Type
+ key={`type-${arg.name}-${arg.value}-${arg.typeDocType}`}
+ type={arg}
+ typeDefinitionByName={props.typeDefinitionByName}
+ />
+ );
+ return subType;
+ }
+ });
+ break;
+
+ case TypeDocTypes.StringLiteral:
+ typeName = `'${type.value}'`;
+ typeNameColor = STRING_LITERAL_COLOR;
+ break;
+
+ case TypeDocTypes.Array:
+ typeName = type.elementType.name;
+ break;
+
+ case TypeDocTypes.Union:
+ const unionTypes = _.map(type.types, t => {
+ return (
+ <Type
+ key={`type-${t.name}-${t.value}-${t.typeDocType}`}
+ type={t}
+ typeDefinitionByName={props.typeDefinitionByName}
+ />
+ );
+ });
+ typeName = _.reduce(unionTypes, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, '|', curr];
+ });
+ break;
+
+ case TypeDocTypes.TypeParameter:
+ typeName = type.name;
+ break;
+
+ default:
+ throw utils.spawnSwitchErr('type.typeDocType', type.typeDocType);
+ }
+ // HACK: Normalize BigNumber to simply BigNumber. For some reason the type
+ // name is unpredictably one or the other.
+ if (typeName === 'BigNumber') {
+ typeName = 'BigNumber';
+ }
+ const commaSeparatedTypeArgs = _.reduce(typeArgs, (prev: React.ReactNode, curr: React.ReactNode) => {
+ return [prev, ', ', curr];
+ });
+
+ const typeNameUrlIfExists = typeToUrl[(typeName as string)];
+ const sectionNameIfExists = typeToSection[(typeName as string)];
+ if (!_.isUndefined(typeNameUrlIfExists)) {
+ typeName = (
+ <a
+ href={typeNameUrlIfExists}
+ target="_blank"
+ className="text-decoration-none"
+ style={{color: colors.lightBlueA700}}
+ >
+ {typeName}
+ </a>
+ );
+ } else if ((isReference || isArray) &&
+ (typeDocUtils.isPublicType(typeName as string) ||
+ !_.isUndefined(sectionNameIfExists))) {
+ const id = Math.random().toString();
+ const typeDefinitionAnchorId = _.isUndefined(sectionNameIfExists) ? typeName : sectionNameIfExists;
+ let typeDefinition;
+ if (props.typeDefinitionByName) {
+ typeDefinition = props.typeDefinitionByName[typeName as string];
+ }
+ typeName = (
+ <ScrollLink
+ to={typeDefinitionAnchorId}
+ offset={0}
+ duration={constants.DOCS_SCROLL_DURATION_MS}
+ containerId={constants.DOCS_CONTAINER_ID}
+ >
+ {_.isUndefined(typeDefinition) || utils.isUserOnMobile() ?
+ <span
+ onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
+ style={{color: colors.lightBlueA700, cursor: 'pointer'}}
+ >
+ {typeName}
+ </span> :
+ <span
+ data-tip={true}
+ data-for={id}
+ onClick={utils.setUrlHash.bind(null, typeDefinitionAnchorId)}
+ style={{color: colors.lightBlueA700, cursor: 'pointer', display: 'inline-block'}}
+ >
+ {typeName}
+ <ReactTooltip
+ type="light"
+ effect="solid"
+ id={id}
+ className="typeTooltip"
+ >
+ <TypeDefinition customType={typeDefinition} shouldAddId={false} />
+ </ReactTooltip>
+ </span>
+ }
+ </ScrollLink>
+ );
+ }
+ return (
+ <span>
+ <span style={{color: typeNameColor}}>{typeName}</span>
+ {isArray && '[]'}{!_.isEmpty(typeArgs) &&
+ <span>
+ {'<'}{commaSeparatedTypeArgs}{'>'}
+ </span>
+ }
+ </span>
+ );
+}
diff --git a/packages/website/ts/pages/documentation/type_definition.tsx b/packages/website/ts/pages/documentation/type_definition.tsx
new file mode 100644
index 000000000..bcb07be8e
--- /dev/null
+++ b/packages/website/ts/pages/documentation/type_definition.tsx
@@ -0,0 +1,135 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import {constants} from 'ts/utils/constants';
+import {utils} from 'ts/utils/utils';
+import {KindString, CustomType, TypeDocTypes, CustomTypeChild, HeaderSizes} from 'ts/types';
+import {Type} from 'ts/pages/documentation/type';
+import {Interface} from 'ts/pages/documentation/interface';
+import {CustomEnum} from 'ts/pages/documentation/custom_enum';
+import {Enum} from 'ts/pages/documentation/enum';
+import {MethodSignature} from 'ts/pages/documentation/method_signature';
+import {AnchorTitle} from 'ts/pages/shared/anchor_title';
+import {Comment} from 'ts/pages/documentation/comment';
+import {typeDocUtils} from 'ts/utils/typedoc_utils';
+
+const KEYWORD_COLOR = '#a81ca6';
+
+interface TypeDefinitionProps {
+ customType: CustomType;
+ shouldAddId?: boolean;
+}
+
+interface TypeDefinitionState {
+ shouldShowAnchor: boolean;
+}
+
+export class TypeDefinition extends React.Component<TypeDefinitionProps, TypeDefinitionState> {
+ public static defaultProps: Partial<TypeDefinitionProps> = {
+ shouldAddId: true,
+ };
+ constructor(props: TypeDefinitionProps) {
+ super(props);
+ this.state = {
+ shouldShowAnchor: false,
+ };
+ }
+ public render() {
+ const customType = this.props.customType;
+ if (!typeDocUtils.isPublicType(customType.name)) {
+ return null; // no-op
+ }
+
+ let typePrefix: string;
+ let codeSnippet: React.ReactNode;
+ switch (customType.kindString) {
+ case KindString.Interface:
+ typePrefix = 'Interface';
+ codeSnippet = (
+ <Interface
+ type={customType}
+ />
+ );
+ break;
+
+ case KindString.Variable:
+ typePrefix = 'Enum';
+ codeSnippet = (
+ <CustomEnum
+ type={customType}
+ />
+ );
+ break;
+
+ case KindString.Enumeration:
+ typePrefix = 'Enum';
+ const enumValues = _.map(customType.children, (c: CustomTypeChild) => {
+ return {
+ name: c.name,
+ defaultValue: c.defaultValue,
+ };
+ });
+ codeSnippet = (
+ <Enum
+ values={enumValues}
+ />
+ );
+ break;
+
+ case KindString['Type alias']:
+ typePrefix = 'Type Alias';
+ codeSnippet = (
+ <span>
+ <span style={{color: KEYWORD_COLOR}}>type</span> {customType.name} ={' '}
+ {customType.type.typeDocType !== TypeDocTypes.Reflection ?
+ <Type type={customType.type} /> :
+ <MethodSignature
+ method={customType.type.method}
+ shouldHideMethodName={true}
+ shouldUseArrowSyntax={true}
+ />
+ }
+ </span>
+ );
+ break;
+
+ default:
+ throw utils.spawnSwitchErr('type.kindString', customType.kindString);
+ }
+
+ const typeDefinitionAnchorId = customType.name;
+ return (
+ <div
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ className="pb2"
+ style={{overflow: 'hidden', width: '100%'}}
+ onMouseOver={this.setAnchorVisibility.bind(this, true)}
+ onMouseOut={this.setAnchorVisibility.bind(this, false)}
+ >
+ <AnchorTitle
+ headerSize={HeaderSizes.H3}
+ title={`${typePrefix} ${customType.name}`}
+ id={this.props.shouldAddId ? typeDefinitionAnchorId : ''}
+ shouldShowAnchor={this.state.shouldShowAnchor}
+ />
+ <div style={{fontSize: 16}}>
+ <pre>
+ <code className="hljs">
+ {codeSnippet}
+ </code>
+ </pre>
+ </div>
+ {customType.comment &&
+ <Comment
+ comment={customType.comment}
+ className="py2"
+ />
+ }
+ </div>
+ );
+ }
+ private setAnchorVisibility(shouldShowAnchor: boolean) {
+ this.setState({
+ shouldShowAnchor,
+ });
+ }
+}
diff --git a/packages/website/ts/pages/documentation/zero_ex_js_documentation.tsx b/packages/website/ts/pages/documentation/zero_ex_js_documentation.tsx
new file mode 100644
index 000000000..c26fb7d0a
--- /dev/null
+++ b/packages/website/ts/pages/documentation/zero_ex_js_documentation.tsx
@@ -0,0 +1,340 @@
+import * as _ from 'lodash';
+import * as React from 'react';
+import * as ReactMarkdown from 'react-markdown';
+import DocumentTitle = require('react-document-title');
+import findVersions = require('find-versions');
+import semverSort = require('semver-sort');
+import {colors} from 'material-ui/styles';
+import MenuItem from 'material-ui/MenuItem';
+import CircularProgress from 'material-ui/CircularProgress';
+import Paper from 'material-ui/Paper';
+import {
+ Link as ScrollLink,
+ Element as ScrollElement,
+ scroller,
+} from 'react-scroll';
+import {Dispatcher} from 'ts/redux/dispatcher';
+import {
+ KindString,
+ TypeDocNode,
+ ZeroExJsDocSections,
+ Styles,
+ ScreenWidths,
+ TypeDefinitionByName,
+ DocAgnosticFormat,
+ TypescriptMethod,
+ Property,
+ CustomType,
+ Docs,
+} from 'ts/types';
+import {TopBar} from 'ts/components/top_bar';
+import {utils} from 'ts/utils/utils';
+import {docUtils} from 'ts/utils/doc_utils';
+import {constants} from 'ts/utils/constants';
+import {Loading} from 'ts/components/ui/loading';
+import {MethodBlock} from 'ts/pages/documentation/method_block';
+import {SourceLink} from 'ts/pages/documentation/source_link';
+import {Type} from 'ts/pages/documentation/type';
+import {TypeDefinition} from 'ts/pages/documentation/type_definition';
+import {MarkdownSection} from 'ts/pages/shared/markdown_section';
+import {Comment} from 'ts/pages/documentation/comment';
+import {AnchorTitle} from 'ts/pages/shared/anchor_title';
+import {SectionHeader} from 'ts/pages/shared/section_header';
+import {NestedSidebarMenu} from 'ts/pages/shared/nested_sidebar_menu';
+import {typeDocUtils} from 'ts/utils/typedoc_utils';
+/* tslint:disable:no-var-requires */
+const IntroMarkdown = require('md/docs/0xjs/introduction');
+const InstallationMarkdown = require('md/docs/0xjs/installation');
+const AsyncMarkdown = require('md/docs/0xjs/async');
+const ErrorsMarkdown = require('md/docs/0xjs/errors');
+const versioningMarkdown = require('md/docs/0xjs/versioning');
+/* tslint:enable:no-var-requires */
+
+const SCROLL_TO_TIMEOUT = 500;
+const DOC_JSON_ROOT = constants.S3_0XJS_DOCUMENTATION_JSON_ROOT;
+
+const sectionNameToMarkdown = {
+ [ZeroExJsDocSections.introduction]: IntroMarkdown,
+ [ZeroExJsDocSections.installation]: InstallationMarkdown,
+ [ZeroExJsDocSections.async]: AsyncMarkdown,
+ [ZeroExJsDocSections.errors]: ErrorsMarkdown,
+ [ZeroExJsDocSections.versioning]: versioningMarkdown,
+};
+
+export interface ZeroExJSDocumentationPassedProps {
+ source: string;
+ location: Location;
+}
+
+export interface ZeroExJSDocumentationAllProps {
+ source: string;
+ location: Location;
+ dispatcher: Dispatcher;
+ docsVersion: string;
+ availableDocVersions: string[];
+}
+
+interface ZeroExJSDocumentationState {
+ docAgnosticFormat?: DocAgnosticFormat;
+}
+
+const styles: Styles = {
+ mainContainers: {
+ position: 'absolute',
+ top: 60,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ overflowZ: 'hidden',
+ overflowY: 'scroll',
+ minHeight: 'calc(100vh - 60px)',
+ WebkitOverflowScrolling: 'touch',
+ },
+ menuContainer: {
+ borderColor: colors.grey300,
+ maxWidth: 330,
+ marginLeft: 20,
+ },
+};
+
+export class ZeroExJSDocumentation extends React.Component<ZeroExJSDocumentationAllProps, ZeroExJSDocumentationState> {
+ constructor(props: ZeroExJSDocumentationAllProps) {
+ super(props);
+ this.state = {
+ docAgnosticFormat: undefined,
+ };
+ }
+ public componentWillMount() {
+ const pathName = this.props.location.pathname;
+ const lastSegment = pathName.substr(pathName.lastIndexOf('/') + 1);
+ const versions = findVersions(lastSegment);
+ const preferredVersionIfExists = versions.length > 0 ? versions[0] : undefined;
+ this.fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
+ }
+ public render() {
+ const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
+ ? {}
+ : typeDocUtils.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
+ return (
+ <div>
+ <DocumentTitle title="0x.js Documentation"/>
+ <TopBar
+ blockchainIsLoaded={false}
+ location={this.props.location}
+ docsVersion={this.props.docsVersion}
+ availableDocVersions={this.props.availableDocVersions}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ shouldFullWidth={true}
+ doc={Docs.ZeroExJs}
+ />
+ {_.isUndefined(this.state.docAgnosticFormat) ?
+ <div
+ className="col col-12"
+ style={styles.mainContainers}
+ >
+ <div
+ className="relative sm-px2 sm-pt2 sm-m1"
+ style={{height: 122, top: '50%', transform: 'translateY(-50%)'}}
+ >
+ <div className="center pb2">
+ <CircularProgress size={40} thickness={5} />
+ </div>
+ <div className="center pt2" style={{paddingBottom: 11}}>Loading documentation...</div>
+ </div>
+ </div> :
+ <div
+ className="mx-auto flex"
+ style={{color: colors.grey800, height: 43}}
+ >
+ <div className="relative col md-col-3 lg-col-3 lg-pl0 md-pl1 sm-hide xs-hide">
+ <div
+ className="border-right absolute"
+ style={{...styles.menuContainer, ...styles.mainContainers}}
+ >
+ <NestedSidebarMenu
+ selectedVersion={this.props.docsVersion}
+ versions={this.props.availableDocVersions}
+ topLevelMenu={typeDocUtils.getFinal0xjsMenu(this.props.docsVersion)}
+ menuSubsectionsBySection={menuSubsectionsBySection}
+ doc={Docs.ZeroExJs}
+ />
+ </div>
+ </div>
+ <div className="relative col lg-col-9 md-col-9 sm-col-12 col-12">
+ <div
+ id="documentation"
+ style={styles.mainContainers}
+ className="absolute"
+ >
+ <div id="zeroExJSDocs" />
+ <h1 className="md-pl2 sm-pl3">
+ <a href={constants.GITHUB_0X_JS_URL} target="_blank">
+ 0x.js
+ </a>
+ </h1>
+ {this.renderDocumentation()}
+ </div>
+ </div>
+ </div>
+ }
+ </div>
+ );
+ }
+ private renderDocumentation(): React.ReactNode {
+ const typeDocSection = this.state.docAgnosticFormat[ZeroExJsDocSections.types];
+ const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
+
+ const subMenus = _.values(constants.menu0xjs);
+ const orderedSectionNames = _.flatten(subMenus);
+ const sections = _.map(orderedSectionNames, this.renderSection.bind(this, typeDefinitionByName));
+
+ return sections;
+ }
+ private renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
+ const docSection = this.state.docAgnosticFormat[sectionName];
+
+ const markdownFileIfExists = sectionNameToMarkdown[sectionName];
+ if (!_.isUndefined(markdownFileIfExists)) {
+ return (
+ <MarkdownSection
+ key={`markdown-section-${sectionName}`}
+ sectionName={sectionName}
+ markdownContent={markdownFileIfExists}
+ />
+ );
+ }
+
+ if (_.isUndefined(docSection)) {
+ return null;
+ }
+
+ const typeDefs = _.map(docSection.types, customType => {
+ return (
+ <TypeDefinition
+ key={`type-${customType.name}`}
+ customType={customType}
+ />
+ );
+ });
+ const propertyDefs = _.map(docSection.properties, this.renderProperty.bind(this));
+ const methodDefs = _.map(docSection.methods, method => {
+ const isConstructor = false;
+ return this.renderMethodBlocks(method, sectionName, isConstructor, typeDefinitionByName);
+ });
+ return (
+ <div
+ key={`section-${sectionName}`}
+ className="py2 pr3 md-pl2 sm-pl3"
+ >
+ <SectionHeader sectionName={sectionName} />
+ <Comment
+ comment={docSection.comment}
+ />
+ {sectionName === ZeroExJsDocSections.zeroEx && docSection.constructors.length > 0 &&
+ <div>
+ <h2 className="thin">Constructor</h2>
+ {this.renderZeroExConstructors(docSection.constructors, typeDefinitionByName)}
+ </div>
+ }
+ {docSection.properties.length > 0 &&
+ <div>
+ <h2 className="thin">Properties</h2>
+ <div>{propertyDefs}</div>
+ </div>
+ }
+ {docSection.methods.length > 0 &&
+ <div>
+ <h2 className="thin">Methods</h2>
+ <div>{methodDefs}</div>
+ </div>
+ }
+ {typeDefs.length > 0 &&
+ <div>
+ <div>{typeDefs}</div>
+ </div>
+ }
+ </div>
+ );
+ }
+ private renderZeroExConstructors(constructors: TypescriptMethod[],
+ typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
+ const constructorDefs = _.map(constructors, constructor => {
+ return this.renderMethodBlocks(
+ constructor, ZeroExJsDocSections.zeroEx, constructor.isConstructor, typeDefinitionByName,
+ );
+ });
+ return (
+ <div>
+ {constructorDefs}
+ </div>
+ );
+ }
+ private renderProperty(property: Property): React.ReactNode {
+ return (
+ <div
+ key={`property-${property.name}-${property.type.name}`}
+ className="pb3"
+ >
+ <code className="hljs">
+ {property.name}: <Type type={property.type} />
+ </code>
+ <SourceLink
+ version={this.props.docsVersion}
+ source={property.source}
+ />
+ {property.comment &&
+ <Comment
+ comment={property.comment}
+ className="py2"
+ />
+ }
+ </div>
+ );
+ }
+ private renderMethodBlocks(method: TypescriptMethod, sectionName: string, isConstructor: boolean,
+ typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
+ return (
+ <MethodBlock
+ key={`method-${method.name}-${!_.isUndefined(method.source) ? method.source.line : ''}`}
+ method={method}
+ typeDefinitionByName={typeDefinitionByName}
+ libraryVersion={this.props.docsVersion}
+ />
+ );
+ }
+ private scrollToHash(): void {
+ const hashWithPrefix = this.props.location.hash;
+ let hash = hashWithPrefix.slice(1);
+ if (_.isEmpty(hash)) {
+ hash = 'zeroExJSDocs'; // scroll to the top
+ }
+
+ scroller.scrollTo(hash, {duration: 0, offset: 0, containerId: 'documentation'});
+ }
+ private async fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists?: string): Promise<void> {
+ const versionToFileName = await docUtils.getVersionToFileNameAsync(DOC_JSON_ROOT);
+ const versions = _.keys(versionToFileName);
+ this.props.dispatcher.updateAvailableDocVersions(versions);
+ const sortedVersions = semverSort.desc(versions);
+ const latestVersion = sortedVersions[0];
+
+ let versionToFetch = latestVersion;
+ if (!_.isUndefined(preferredVersionIfExists)) {
+ const preferredVersionFileNameIfExists = versionToFileName[preferredVersionIfExists];
+ if (!_.isUndefined(preferredVersionFileNameIfExists)) {
+ versionToFetch = preferredVersionIfExists;
+ }
+ }
+ this.props.dispatcher.updateCurrentDocsVersion(versionToFetch);
+
+ const versionFileNameToFetch = versionToFileName[versionToFetch];
+ const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, DOC_JSON_ROOT);
+ const docAgnosticFormat = typeDocUtils.convertToDocAgnosticFormat((versionDocObj as TypeDocNode));
+
+ this.setState({
+ docAgnosticFormat,
+ }, () => {
+ this.scrollToHash();
+ });
+ }
+}