Refactor the topLevel documentation react components for 0x.js and Smart contracts into a single component

This commit is contained in:
Fabio Berger
2017-11-28 15:11:04 -06:00
parent fcddd503b7
commit 72a00ac2df
10 changed files with 323 additions and 599 deletions

View File

@@ -2,12 +2,54 @@ import * as _ from 'lodash';
import * as React from 'react';
import {connect} from 'react-redux';
import {Dispatch, Store as ReduxStore} from 'redux';
import {DocsInfo} from 'ts/pages/documentation/docs_info';
import {
SmartContractsDocumentation as SmartContractsDocumentationComponent,
SmartContractsDocumentationAllProps,
} from 'ts/pages/documentation/smart_contracts_documentation';
Documentation as DocumentationComponent,
DocumentationAllProps,
} from 'ts/pages/documentation/documentation';
import {Dispatcher} from 'ts/redux/dispatcher';
import {State} from 'ts/redux/reducer';
import {DocsInfoConfig, WebsitePaths} from 'ts/types';
import {constants} from 'ts/utils/constants';
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 sections = constants.smartContractDocSections;
const docsInfoConfig: DocsInfoConfig = {
packageName: '0x Smart Contracts',
packageUrl: 'https://github.com/0xProject/contracts',
websitePath: WebsitePaths.SmartContracts,
docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json',
menu: {
introduction: [
sections.Introduction,
],
contracts: [
sections.Exchange,
sections.TokenRegistry,
sections.ZRXToken,
sections.EtherToken,
sections.TokenTransferProxy,
],
},
sectionNameToMarkdown: {
[sections.Introduction]: IntroMarkdown,
},
sections,
visibleConstructors: [
sections.Exchange,
sections.TokenRegistry,
sections.ZRXToken,
sections.EtherToken,
sections.TokenTransferProxy,
],
convertToDocAgnosticFormatFn: doxityUtils.convertToDocAgnosticFormat.bind(doxityUtils),
};
const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
docsVersion: string;
@@ -16,16 +58,18 @@ interface ConnectedState {
interface ConnectedDispatch {
dispatcher: Dispatcher;
docsInfo: DocsInfo;
}
const mapStateToProps = (state: State, ownProps: SmartContractsDocumentationAllProps): ConnectedState => ({
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
docsVersion: state.docsVersion,
availableDocVersions: state.availableDocVersions,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
dispatcher: new Dispatcher(dispatch),
docsInfo,
});
export const SmartContractsDocumentation: React.ComponentClass<SmartContractsDocumentationAllProps> =
connect(mapStateToProps, mapDispatchToProps)(SmartContractsDocumentationComponent);
export const Documentation: React.ComponentClass<DocumentationAllProps> =
connect(mapStateToProps, mapDispatchToProps)(DocumentationComponent);

View File

@@ -4,30 +4,164 @@ import * as React from 'react';
import {connect} from 'react-redux';
import {Dispatch, Store as ReduxStore} from 'redux';
import {Blockchain} from 'ts/blockchain';
import {DocsInfo} from 'ts/pages/documentation/docs_info';
import {
ZeroExJSDocumentation as ZeroExJSDocumentationComponent,
ZeroExJSDocumentationAllProps,
} from 'ts/pages/documentation/zero_ex_js_documentation';
Documentation as DocumentationComponent,
DocumentationAllProps,
} from 'ts/pages/documentation/documentation';
import {Dispatcher} from 'ts/redux/dispatcher';
import {State} from 'ts/redux/reducer';
import {DocsInfoConfig, WebsitePaths} from 'ts/types';
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 zeroExJsDocSections = {
introduction: 'introduction',
installation: 'installation',
testrpc: 'testrpc',
async: 'async',
errors: 'errors',
versioning: 'versioning',
zeroEx: 'zeroEx',
exchange: 'exchange',
token: 'token',
tokenRegistry: 'tokenRegistry',
etherToken: 'etherToken',
proxy: 'proxy',
types: 'types',
};
const docsInfoConfig: DocsInfoConfig = {
packageName: '0x.js',
packageUrl: 'https://github.com/0xProject/0x.js',
websitePath: WebsitePaths.ZeroExJs,
docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons',
menu: {
introduction: [
zeroExJsDocSections.introduction,
],
install: [
zeroExJsDocSections.installation,
],
topics: [
zeroExJsDocSections.async,
zeroExJsDocSections.errors,
zeroExJsDocSections.versioning,
],
zeroEx: [
zeroExJsDocSections.zeroEx,
],
contracts: [
zeroExJsDocSections.exchange,
zeroExJsDocSections.token,
zeroExJsDocSections.tokenRegistry,
zeroExJsDocSections.etherToken,
zeroExJsDocSections.proxy,
],
types: [
zeroExJsDocSections.types,
],
},
sectionNameToMarkdown: {
[zeroExJsDocSections.introduction]: IntroMarkdown,
[zeroExJsDocSections.installation]: InstallationMarkdown,
[zeroExJsDocSections.async]: AsyncMarkdown,
[zeroExJsDocSections.errors]: ErrorsMarkdown,
[zeroExJsDocSections.versioning]: versioningMarkdown,
},
// Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
// currently no way to extract the re-exported types from index.ts via TypeDoc :(
publicTypes: [
'Order',
'SignedOrder',
'ECSignature',
'ZeroExError',
'EventCallback',
'EventCallbackAsync',
'EventCallbackSync',
'ExchangeContractErrs',
'ContractEvent',
'Token',
'ExchangeEvents',
'IndexedFilterValues',
'SubscriptionOpts',
'BlockParam',
'OrderFillOrKillRequest',
'OrderCancellationRequest',
'OrderFillRequest',
'ContractEventEmitter',
'Web3Provider',
'ContractEventArgs',
'LogCancelArgs',
'LogFillArgs',
'LogErrorContractEventArgs',
'LogFillContractEventArgs',
'LogCancelContractEventArgs',
'TokenEvents',
'ExchangeContractEventArgs',
'TransferContractEventArgs',
'ApprovalContractEventArgs',
'TokenContractEventArgs',
'ZeroExConfig',
'TransactionReceiptWithDecodedLogs',
'LogWithDecodedArgs',
'DecodedLogArgs',
'MethodOpts',
'ValidateOrderFillableOpts',
'OrderTransactionOpts',
'ContractEventArg',
'LogEvent',
'LogEntry',
'DecodedLogEvent',
],
sectionNameToModulePath: {
[zeroExJsDocSections.zeroEx]: ['"src/0x"'],
[zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'],
[zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'],
[zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'],
[zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'],
[zeroExJsDocSections.proxy]: [
'"src/contract_wrappers/proxy_wrapper"',
'"src/contract_wrappers/token_transfer_proxy_wrapper"',
],
[zeroExJsDocSections.types]: ['"src/types"'],
},
menuSubsectionToVersionWhenIntroduced: {
[zeroExJsDocSections.etherToken]: '0.7.1',
[zeroExJsDocSections.proxy]: '0.8.0',
},
sections: zeroExJsDocSections,
visibleConstructors: [zeroExJsDocSections.zeroEx],
convertToDocAgnosticFormatFn: typeDocUtils.convertToDocAgnosticFormat.bind(typeDocUtils),
};
const docsInfo = new DocsInfo(docsInfoConfig);
interface ConnectedState {
docsVersion: string;
availableDocVersions: string[];
docsInfo: DocsInfo;
}
interface ConnectedDispatch {
dispatcher: Dispatcher;
}
const mapStateToProps = (state: State, ownProps: ZeroExJSDocumentationAllProps): ConnectedState => ({
const mapStateToProps = (state: State, ownProps: DocumentationAllProps): ConnectedState => ({
docsVersion: state.docsVersion,
availableDocVersions: state.availableDocVersions,
docsInfo,
});
const mapDispatchToProps = (dispatch: Dispatch<State>): ConnectedDispatch => ({
dispatcher: new Dispatcher(dispatch),
});
export const ZeroExJSDocumentation: React.ComponentClass<ZeroExJSDocumentationAllProps> =
connect(mapStateToProps, mapDispatchToProps)(ZeroExJSDocumentationComponent);
export const Documentation: React.ComponentClass<DocumentationAllProps> =
connect(mapStateToProps, mapDispatchToProps)(DocumentationComponent);

View File

@@ -78,16 +78,25 @@ const LazyPortal = createLazyComponent(
'Portal', async () => System.import<any>(/* webpackChunkName: "portal" */'ts/containers/portal'),
);
const LazyZeroExJSDocumentation = createLazyComponent(
'ZeroExJSDocumentation',
'Documentation',
async () => System.import<any>(/* webpackChunkName: "zeroExDocs" */'ts/containers/zero_ex_js_documentation'),
);
const LazySmartContractsDocumentation = createLazyComponent(
'SmartContractsDocumentation',
'Documentation',
async () => System.import<any>(
/* webpackChunkName: "smartContractDocs" */'ts/containers/smart_contracts_documentation',
),
);
const docs = class Documentation extends
React.Component<any, any> {
public render() {
return (
<div>hlwkdjaeljdflajfesli</div>
);
}
};
const store: ReduxStore<State> = createStore(reducer);
render(
<Router>

View File

@@ -56,6 +56,9 @@ export const createLazyComponent = (componentName: string, lazyImport: () => Pro
const reactComponentPromise = (async (): Promise<React.ComponentClass<any>> => {
const mod = await lazyImport();
const component = mod[componentName];
if (_.isUndefined(component)) {
throw new Error(`Did not find exported component: ${componentName}`);
}
return component;
})();
return (

View File

@@ -1,6 +1,14 @@
import compareVersions = require('compare-versions');
import * as _ from 'lodash';
import {DocsInfoConfig, DocsMenu, SectionsMap} from 'ts/types';
import {
DocAgnosticFormat,
DocsInfoConfig,
DocsMenu,
DoxityDocObj,
MenuSubsectionsBySection,
SectionsMap,
TypeDocNode,
} from 'ts/types';
export class DocsInfo {
public packageName: string;
@@ -49,4 +57,49 @@ export class DocsInfo {
});
return finalMenu;
}
public getMenuSubsectionsBySection(docAgnosticFormat?: DocAgnosticFormat): MenuSubsectionsBySection {
const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
if (_.isUndefined(docAgnosticFormat)) {
return menuSubsectionsBySection;
}
const docSections = _.keys(this.sections);
_.each(docSections, sectionName => {
const docSection = docAgnosticFormat[sectionName];
if (_.isUndefined(docSection)) {
return; // no-op
}
if (!_.isUndefined(this.sections.types) && sectionName === this.sections.types) {
const sortedTypesNames = _.sortBy(docSection.types, 'name');
const typeNames = _.map(sortedTypesNames, t => t.name);
menuSubsectionsBySection[sectionName] = typeNames;
} else {
let eventNames: string[] = [];
if (!_.isUndefined(docSection.events)) {
const sortedEventNames = _.sortBy(docSection.events, 'name');
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;
}
public getTypeDefinitionsByName(docAgnosticFormat: DocAgnosticFormat) {
if (_.isUndefined(this.sections.types)) {
return {};
}
const typeDocSection = docAgnosticFormat[this.sections.types];
const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
return typeDefinitionByName;
}
public isVisibleConstructor(sectionName: string): boolean {
return _.includes(this.docsInfo.visibleConstructors, sectionName);
}
public convertToDocAgnosticFormat(docObj: DoxityDocObj|TypeDocNode): DocAgnosticFormat {
return this.docsInfo.convertToDocAgnosticFormatFn(docObj, this);
}
}

View File

@@ -37,15 +37,13 @@ import {
SolidityMethod,
Styles,
TypeDefinitionByName,
TypeDocNode,
TypescriptMethod,
WebsitePaths,
} from 'ts/types';
import {constants} from 'ts/utils/constants';
import {docUtils} from 'ts/utils/doc_utils';
import {doxityUtils} from 'ts/utils/doxity_utils';
import {utils} from 'ts/utils/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 SCROLL_TOP_ID = 'docsScrollTop';
@@ -53,46 +51,22 @@ const CUSTOM_PURPLE = '#690596';
const CUSTOM_RED = '#e91751';
const CUSTOM_TURQUOIS = '#058789';
const sections = constants.smartContractDocSections;
const docsInfoConfig: DocsInfoConfig = {
packageName: '0x Smart Contracts',
packageUrl: 'https://github.com/0xProject/contracts',
websitePath: WebsitePaths.SmartContracts,
docsJsonRoot: 'https://s3.amazonaws.com/smart-contracts-docs-json',
menu: {
introduction: [
sections.Introduction,
],
contracts: [
sections.Exchange,
sections.TokenRegistry,
sections.ZRXToken,
sections.EtherToken,
sections.TokenTransferProxy,
],
},
sectionNameToMarkdown: {
[sections.Introduction]: IntroMarkdown,
},
sections,
};
const networkNameToColor: {[network: string]: string} = {
[Networks.kovan]: CUSTOM_PURPLE,
[Networks.ropsten]: CUSTOM_RED,
[Networks.mainnet]: CUSTOM_TURQUOIS,
};
export interface SmartContractsDocumentationAllProps {
export interface DocumentationAllProps {
source: string;
location: Location;
dispatcher: Dispatcher;
docsVersion: string;
availableDocVersions: string[];
docsInfo: DocsInfo;
}
interface SmartContractsDocumentationState {
interface DocumentationState {
docAgnosticFormat?: DocAgnosticFormat;
}
@@ -114,11 +88,10 @@ const styles: Styles = {
marginLeft: 20,
},
};
const docsInfo = new DocsInfo(docsInfoConfig);
export class SmartContractsDocumentation extends
React.Component<SmartContractsDocumentationAllProps, SmartContractsDocumentationState> {
constructor(props: SmartContractsDocumentationAllProps) {
export class Documentation extends
React.Component<DocumentationAllProps, DocumentationState> {
constructor(props: DocumentationAllProps) {
super(props);
this.state = {
docAgnosticFormat: undefined,
@@ -133,21 +106,21 @@ export class SmartContractsDocumentation extends
this.fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
}
public render() {
const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat)
? {}
: this.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) ?
{} :
this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
return (
<div>
<DocumentTitle title="0x Smart Contract Documentation"/>
<DocumentTitle title={`${this.props.docsInfo.packageName} Documentation`}/>
<TopBar
blockchainIsLoaded={false}
location={this.props.location}
docsVersion={this.props.docsVersion}
availableDocVersions={this.props.availableDocVersions}
menu={docsInfo.getMenu(this.props.docsVersion)}
menu={this.props.docsInfo.getMenu(this.props.docsVersion)}
menuSubsectionsBySection={menuSubsectionsBySection}
shouldFullWidth={true}
docPath={docsInfo.websitePath}
docPath={this.props.docsInfo.websitePath}
/>
{_.isUndefined(this.state.docAgnosticFormat) ?
<div
@@ -176,9 +149,9 @@ export class SmartContractsDocumentation extends
<NestedSidebarMenu
selectedVersion={this.props.docsVersion}
versions={this.props.availableDocVersions}
topLevelMenu={docsInfo.getMenu()}
topLevelMenu={this.props.docsInfo.getMenu(this.props.docsVersion)}
menuSubsectionsBySection={menuSubsectionsBySection}
docPath={docsInfo.websitePath}
docPath={this.props.docsInfo.websitePath}
/>
</div>
</div>
@@ -190,8 +163,8 @@ export class SmartContractsDocumentation extends
>
<div id={SCROLL_TOP_ID} />
<h1 className="md-pl2 sm-pl3">
<a href={constants.GITHUB_CONTRACTS_URL} target="_blank">
0x Smart Contracts
<a href={this.props.docsInfo.packageUrl} target="_blank">
{this.props.docsInfo.packageName}
</a>
</h1>
{this.renderDocumentation()}
@@ -203,19 +176,16 @@ export class SmartContractsDocumentation extends
);
}
private renderDocumentation(): React.ReactNode {
const subMenus = _.values(docsInfo.getMenu());
const subMenus = _.values(this.props.docsInfo.getMenu());
const orderedSectionNames = _.flatten(subMenus);
// Since smart contract method params are all base types, no need to pass
// down the typeDefinitionByName
const typeDefinitionByName = {};
const typeDefinitionByName = this.props.docsInfo.getTypeDefinitionsByName(this.state.docAgnosticFormat);
const renderedSections = _.map(orderedSectionNames, this.renderSection.bind(this, typeDefinitionByName));
return renderedSections;
}
private renderSection(typeDefinitionByName: TypeDefinitionByName, sectionName: string): React.ReactNode {
const docSection = this.state.docAgnosticFormat[sectionName];
const markdownFileIfExists = docsInfo.sectionNameToMarkdown[sectionName];
const markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName];
if (!_.isUndefined(markdownFileIfExists)) {
return (
<MarkdownSection
@@ -226,10 +196,22 @@ export class SmartContractsDocumentation extends
);
}
const docSection = this.state.docAgnosticFormat[sectionName];
if (_.isUndefined(docSection)) {
return null;
}
const sortedTypes = _.sortBy(docSection.types, 'name');
const typeDefs = _.map(sortedTypes, customType => {
return (
<TypeDefinition
key={`type-${customType.name}`}
customType={customType}
docsInfo={this.props.docsInfo}
/>
);
});
const sortedProperties = _.sortBy(docSection.properties, 'name');
const propertyDefs = _.map(sortedProperties, this.renderProperty.bind(this));
@@ -245,7 +227,7 @@ export class SmartContractsDocumentation extends
<EventDefinition
key={`event-${event.name}-${i}`}
event={event}
docsInfo={docsInfo}
docsInfo={this.props.docsInfo}
/>
);
});
@@ -258,7 +240,7 @@ export class SmartContractsDocumentation extends
<div style={{marginRight: 7}}>
<SectionHeader sectionName={sectionName} />
</div>
{this.renderNetworkBadges(sectionName)}
{this.renderNetworkBadgesIfExists(sectionName)}
</div>
{docSection.comment &&
<Comment
@@ -266,9 +248,10 @@ export class SmartContractsDocumentation extends
/>
}
{docSection.constructors.length > 0 &&
this.props.docsInfo.isVisibleConstructor(sectionName) &&
<div>
<h2 className="thin">Constructor</h2>
{this.renderConstructors(docSection.constructors, typeDefinitionByName)}
{this.renderConstructors(docSection.constructors, sectionName, typeDefinitionByName)}
</div>
}
{docSection.properties.length > 0 &&
@@ -283,20 +266,28 @@ export class SmartContractsDocumentation extends
<div>{methodDefs}</div>
</div>
}
{docSection.events.length > 0 &&
{!_.isUndefined(docSection.events) && docSection.events.length > 0 &&
<div>
<h2 className="thin">Events</h2>
<div>{eventDefs}</div>
</div>
}
{!_.isUndefined(typeDefs) && typeDefs.length > 0 &&
<div>
<div>{typeDefs}</div>
</div>
}
</div>
);
}
private renderNetworkBadges(sectionName: string) {
private renderNetworkBadgesIfExists(sectionName: string) {
const networkToAddressByContractName = constants.contractAddresses[this.props.docsVersion];
const badges = _.map(networkToAddressByContractName,
(addressByContractName: AddressByContractName, networkName: string) => {
const contractAddress = addressByContractName[sectionName];
if (_.isUndefined(contractAddress)) {
return null;
}
const linkIfExists = utils.getEtherScanLinkIfExists(
contractAddress, constants.networkIdByName[networkName], EtherscanLinkSuffixes.address,
);
@@ -316,11 +307,12 @@ export class SmartContractsDocumentation extends
});
return badges;
}
private renderConstructors(constructors: SolidityMethod[],
private renderConstructors(constructors: SolidityMethod[]|TypescriptMethod[],
sectionName: string,
typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
const constructorDefs = _.map(constructors, constructor => {
return this.renderMethodBlocks(
constructor, docsInfo.sections.zeroEx, constructor.isConstructor, typeDefinitionByName,
constructor, sectionName, constructor.isConstructor, typeDefinitionByName,
);
});
return (
@@ -336,13 +328,13 @@ export class SmartContractsDocumentation extends
className="pb3"
>
<code className="hljs">
{property.name}: <Type type={property.type} docsInfo={docsInfo} />
{property.name}: <Type type={property.type} docsInfo={this.props.docsInfo} />
</code>
{property.source &&
<SourceLink
version={this.props.docsVersion}
source={property.source}
baseUrl={docsInfo.packageUrl}
baseUrl={this.props.docsInfo.packageUrl}
/>
}
{property.comment &&
@@ -354,15 +346,15 @@ export class SmartContractsDocumentation extends
</div>
);
}
private renderMethodBlocks(method: SolidityMethod, sectionName: string, isConstructor: boolean,
typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
private renderMethodBlocks(method: SolidityMethod|TypescriptMethod, sectionName: string,
isConstructor: boolean, typeDefinitionByName: TypeDefinitionByName): React.ReactNode {
return (
<MethodBlock
key={`method-${method.name}`}
key={`method-${method.name}-${sectionName}`}
method={method}
typeDefinitionByName={typeDefinitionByName}
libraryVersion={this.props.docsVersion}
docsInfo={docsInfo}
docsInfo={this.props.docsInfo}
/>
);
}
@@ -375,35 +367,8 @@ export class SmartContractsDocumentation extends
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(docsInfo.sections);
_.each(docSections, sectionName => {
const docSection = docAgnosticFormat[sectionName];
if (_.isUndefined(docSection)) {
return; // no-op
}
if (sectionName === docsInfo.sections.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(docsInfo.docsJsonRoot);
const versionToFileName = await docUtils.getVersionToFileNameAsync(this.props.docsInfo.docsJsonRoot);
const versions = _.keys(versionToFileName);
this.props.dispatcher.updateAvailableDocVersions(versions);
const sortedVersions = semverSort.desc(versions);
@@ -419,8 +384,10 @@ export class SmartContractsDocumentation extends
this.props.dispatcher.updateCurrentDocsVersion(versionToFetch);
const versionFileNameToFetch = versionToFileName[versionToFetch];
const versionDocObj = await docUtils.getJSONDocFileAsync(versionFileNameToFetch, docsInfo.docsJsonRoot);
const docAgnosticFormat = doxityUtils.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);
const versionDocObj = await docUtils.getJSONDocFileAsync(
versionFileNameToFetch, this.props.docsInfo.docsJsonRoot,
);
const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);
this.setState({
docAgnosticFormat,

View File

@@ -1,462 +0,0 @@
import findVersions = require('find-versions');
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import MenuItem from 'material-ui/MenuItem';
import Paper from 'material-ui/Paper';
import {colors} from 'material-ui/styles';
import * as React from 'react';
import DocumentTitle = require('react-document-title');
import * as ReactMarkdown from 'react-markdown';
import {
Element as ScrollElement,
Link as ScrollLink,
scroller,
} from 'react-scroll';
import semverSort = require('semver-sort');
import {TopBar} from 'ts/components/top_bar';
import {Loading} from 'ts/components/ui/loading';
import {Comment} from 'ts/pages/documentation/comment';
import {DocsInfo} from 'ts/pages/documentation/docs_info';
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 {AnchorTitle} from 'ts/pages/shared/anchor_title';
import {MarkdownSection} from 'ts/pages/shared/markdown_section';
import {NestedSidebarMenu} from 'ts/pages/shared/nested_sidebar_menu';
import {SectionHeader} from 'ts/pages/shared/section_header';
import {Dispatcher} from 'ts/redux/dispatcher';
import {
CustomType,
DocAgnosticFormat,
Docs,
DocsInfoConfig,
KindString,
Property,
ScreenWidths,
Styles,
TypeDefinitionByName,
TypeDocNode,
TypescriptMethod,
WebsitePaths,
} from 'ts/types';
import {constants} from 'ts/utils/constants';
import {docUtils} from 'ts/utils/doc_utils';
import {typeDocUtils} from 'ts/utils/typedoc_utils';
import {utils} from 'ts/utils/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 SCROLL_TOP_ID = 'docsScrollTop';
const zeroExJsDocSections = {
introduction: 'introduction',
installation: 'installation',
testrpc: 'testrpc',
async: 'async',
errors: 'errors',
versioning: 'versioning',
zeroEx: 'zeroEx',
exchange: 'exchange',
token: 'token',
tokenRegistry: 'tokenRegistry',
etherToken: 'etherToken',
proxy: 'proxy',
types: 'types',
};
const docsInfoConfig: DocsInfoConfig = {
packageName: '0x.js',
packageUrl: 'https://github.com/0xProject/0x.js',
websitePath: WebsitePaths.ZeroExJs,
docsJsonRoot: 'https://s3.amazonaws.com/0xjs-docs-jsons',
menu: {
introduction: [
zeroExJsDocSections.introduction,
],
install: [
zeroExJsDocSections.installation,
],
topics: [
zeroExJsDocSections.async,
zeroExJsDocSections.errors,
zeroExJsDocSections.versioning,
],
zeroEx: [
zeroExJsDocSections.zeroEx,
],
contracts: [
zeroExJsDocSections.exchange,
zeroExJsDocSections.token,
zeroExJsDocSections.tokenRegistry,
zeroExJsDocSections.etherToken,
zeroExJsDocSections.proxy,
],
types: [
zeroExJsDocSections.types,
],
},
sectionNameToMarkdown: {
[zeroExJsDocSections.introduction]: IntroMarkdown,
[zeroExJsDocSections.installation]: InstallationMarkdown,
[zeroExJsDocSections.async]: AsyncMarkdown,
[zeroExJsDocSections.errors]: ErrorsMarkdown,
[zeroExJsDocSections.versioning]: versioningMarkdown,
},
// Note: This needs to be kept in sync with the types exported in index.ts. Unfortunately there is
// currently no way to extract the re-exported types from index.ts via TypeDoc :(
publicTypes: [
'Order',
'SignedOrder',
'ECSignature',
'ZeroExError',
'EventCallback',
'EventCallbackAsync',
'EventCallbackSync',
'ExchangeContractErrs',
'ContractEvent',
'Token',
'ExchangeEvents',
'IndexedFilterValues',
'SubscriptionOpts',
'BlockParam',
'OrderFillOrKillRequest',
'OrderCancellationRequest',
'OrderFillRequest',
'ContractEventEmitter',
'Web3Provider',
'ContractEventArgs',
'LogCancelArgs',
'LogFillArgs',
'LogErrorContractEventArgs',
'LogFillContractEventArgs',
'LogCancelContractEventArgs',
'TokenEvents',
'ExchangeContractEventArgs',
'TransferContractEventArgs',
'ApprovalContractEventArgs',
'TokenContractEventArgs',
'ZeroExConfig',
'TransactionReceiptWithDecodedLogs',
'LogWithDecodedArgs',
'DecodedLogArgs',
'MethodOpts',
'ValidateOrderFillableOpts',
'OrderTransactionOpts',
'ContractEventArg',
'LogEvent',
'LogEntry',
'DecodedLogEvent',
],
sectionNameToModulePath: {
[zeroExJsDocSections.zeroEx]: ['"src/0x"'],
[zeroExJsDocSections.exchange]: ['"src/contract_wrappers/exchange_wrapper"'],
[zeroExJsDocSections.tokenRegistry]: ['"src/contract_wrappers/token_registry_wrapper"'],
[zeroExJsDocSections.token]: ['"src/contract_wrappers/token_wrapper"'],
[zeroExJsDocSections.etherToken]: ['"src/contract_wrappers/ether_token_wrapper"'],
[zeroExJsDocSections.proxy]: [
'"src/contract_wrappers/proxy_wrapper"',
'"src/contract_wrappers/token_transfer_proxy_wrapper"',
],
[zeroExJsDocSections.types]: ['"src/types"'],
},
menuSubsectionToVersionWhenIntroduced: {
[zeroExJsDocSections.etherToken]: '0.7.1',
[zeroExJsDocSections.proxy]: '0.8.0',
},
sections: zeroExJsDocSections,
};
const docsInfo = new DocsInfo(docsInfoConfig);
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;
// tslint:disable-next-line:no-floating-promises
this.fetchJSONDocsFireAndForgetAsync(preferredVersionIfExists);
}
public render() {
const menuSubsectionsBySection = _.isUndefined(this.state.docAgnosticFormat) ?
{} :
typeDocUtils.getMenuSubsectionsBySection(docsInfo.sections, this.state.docAgnosticFormat);
return (
<div>
<DocumentTitle title={`${docsInfo.packageName} Documentation`}/>
<TopBar
blockchainIsLoaded={false}
location={this.props.location}
docsVersion={this.props.docsVersion}
availableDocVersions={this.props.availableDocVersions}
menu={docsInfo.getMenu(this.props.docsVersion)}
menuSubsectionsBySection={menuSubsectionsBySection}
shouldFullWidth={true}
docPath={docsInfo.websitePath}
/>
{_.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={docsInfo.getMenu(this.props.docsVersion)}
menuSubsectionsBySection={menuSubsectionsBySection}
docPath={docsInfo.websitePath}
/>
</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={SCROLL_TOP_ID} />
<h1 className="md-pl2 sm-pl3">
<a href={docsInfo.packageUrl} target="_blank">
{docsInfo.packageName}
</a>
</h1>
{this.renderDocumentation()}
</div>
</div>
</div>
}
</div>
);
}
private renderDocumentation(): React.ReactNode {
const typeDocSection = this.state.docAgnosticFormat[docsInfo.sections.types];
const typeDefinitionByName = _.keyBy(typeDocSection.types, 'name');
const subMenus = _.values(docsInfo.getMenu());
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 = docsInfo.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}
docsInfo={docsInfo}
/>
);
});
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 === docsInfo.sections.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, docsInfo.sections.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} docsInfo={docsInfo} />
</code>
<SourceLink
version={this.props.docsVersion}
source={property.source}
baseUrl={docsInfo.packageUrl}
/>
{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}
docsInfo={docsInfo}
/>
);
}
private scrollToHash(): void {
const hashWithPrefix = this.props.location.hash;
let hash = hashWithPrefix.slice(1);
if (_.isEmpty(hash)) {
hash = SCROLL_TOP_ID; // 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(docsInfo.docsJsonRoot);
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, docsInfo.docsJsonRoot,
);
const docAgnosticFormat = typeDocUtils.convertToDocAgnosticFormat(
docsInfo, (versionDocObj as TypeDocNode),
);
this.setState({
docAgnosticFormat,
}, () => {
this.scrollToHash();
});
}
}

View File

@@ -680,6 +680,8 @@ export interface DocsInfoConfig {
menu: DocsMenu;
sections: SectionsMap;
sectionNameToMarkdown: {[sectionName: string]: string};
visibleConstructors: string[];
convertToDocAgnosticFormatFn: (docObj: DoxityDocObj|TypeDocNode, docsInfo?: any) => DocAgnosticFormat;
publicTypes?: string[];
sectionNameToModulePath?: {[sectionName: string]: string[]};
menuSubsectionToVersionWhenIntroduced?: {[sectionName: string]: string};

View File

@@ -33,7 +33,6 @@ export const constants = {
FEE_RECIPIENT_ADDRESS: '0x0000000000000000000000000000000000000000',
FIREFOX_U2F_ADDON: 'https://addons.mozilla.org/en-US/firefox/addon/u2f-support-add-on/',
GITHUB_URL: 'https://github.com/0xProject',
GITHUB_CONTRACTS_URL: 'https://github.com/0xProject/contracts',
GITHUB_WIKI_URL: 'https://github.com/0xProject/wiki',
HTTP_NO_CONTENT_STATUS_CODE: 204,
ACCEPT_DISCLAIMER_LOCAL_STORAGE_KEY: 'didAcceptPortalDisclaimer',

View File

@@ -53,32 +53,7 @@ export const typeDocUtils = {
}
return undefined;
},
getMenuSubsectionsBySection(
sections: SectionsMap, docAgnosticFormat?: DocAgnosticFormat,
): MenuSubsectionsBySection {
const menuSubsectionsBySection = {} as MenuSubsectionsBySection;
if (_.isUndefined(docAgnosticFormat)) {
return menuSubsectionsBySection;
}
const docSections = _.keys(sections);
_.each(docSections, sectionName => {
const docSection = docAgnosticFormat[sectionName];
if (_.isUndefined(docSection)) {
return; // no-op
}
if (!_.isUndefined(sections.types) && sectionName === sections.types) {
const typeNames = _.map(docSection.types, t => t.name);
menuSubsectionsBySection[sectionName] = typeNames;
} else {
const methodNames = _.map(docSection.methods, m => m.name);
menuSubsectionsBySection[sectionName] = methodNames;
}
});
return menuSubsectionsBySection;
},
convertToDocAgnosticFormat(docsInfo: DocsInfo, typeDocJson: TypeDocNode): DocAgnosticFormat {
convertToDocAgnosticFormat(typeDocJson: TypeDocNode, docsInfo: DocsInfo): DocAgnosticFormat {
const subMenus = _.values(docsInfo.getMenu());
const orderedSectionNames = _.flatten(subMenus);
const docAgnosticFormat: DocAgnosticFormat = {};