aboutsummaryrefslogblamecommitdiffstats
path: root/packages/website/ts/pages/documentation/documentation.tsx
blob: 2315847add20c44aab994b6072d33624863aa7f5 (plain) (tree)
1
2
3
4
5
6
7
                                               
                            
                                                            

                                                       
                                        
                                           












                                                                          

                          
                      


                          

             


                         
                     
                  




                                               
 
                                      
 
                                                           


                                        

  
                                        




                                   
                       

 
                              





                                          
               




                            
                                       








                                         
                                                                                               
                                               









                                                                                       
                                                        
                                                                        

                     


                                                                                            

                 
                                                                                            




                                                                          
                                                                              

                                                                       
                                                  
                  

                                                                              

                                                                    
                                                                                              



                                                                            


                                                                                      
                              


                                                                                                


                                                                                                      
                                                                                             



                                                                              
                                                                                                      
                                                                                       
                                                                             



                                                                                         
                                                                                                       
                                                          
                                                              
                                                                                             
                                                                         

                                        
                                                             


                                  
                  


                  
                                                     
                                                                 
                                                        

                                                                                                                
                                                                                                                  
 
                                
     
                                                                                                              
                                                                                            









                                                           
                                                                     



                                        



                                                               
                                             






                                                   
                                                                         
                                                                                                   



                                                                   
                                                                                                      







                                                                            
                                             
                                                  



                  
                                                                                  
                                      



                                                                    
                      
                                                                                
                                                      






                                                                                                                  



                                                            

                                                   



                                                         













                                                            


                  
                                                               
                                                                                                

                                           

                                                                                    


                                                     
                                                                    


                                                              





                                                                   
                                                                               
                     
                                                                                                       

                        

              

                      




                                                            
                                                                    
                                                                                                                       
           
                                            
     
                                                                                       
                
                                                                                         
                                       
                                    
                                                                                                          
                       
                                     


                                                        
                                                                
                                                                           
                      

                                                                                            


                  





                                                   

                        





                                                            


              
                                   


                                                        
                                                      

         




                                         
     
                                                                                                      
                                                                                                             














                                                                                                 
                                                                 

                                             

                                                                                                                
 







                                     

     
import findVersions = require('find-versions');
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import * as React from 'react';
import DocumentTitle = require('react-document-title');
import { scroller } from 'react-scroll';
import semverSort = require('semver-sort');
import { TopBar } from 'ts/components/top_bar';
import { Badge } from 'ts/components/ui/badge';
import { Comment } from 'ts/pages/documentation/comment';
import { DocsInfo } from 'ts/pages/documentation/docs_info';
import { EventDefinition } from 'ts/pages/documentation/event_definition';
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 { NestedSidebarMenu } from 'ts/pages/shared/nested_sidebar_menu';
import { SectionHeader } from 'ts/pages/shared/section_header';
import { Dispatcher } from 'ts/redux/dispatcher';
import {
    AddressByContractName,
    DocAgnosticFormat,
    DoxityDocObj,
    EtherscanLinkSuffixes,
    Event,
    Networks,
    Property,
    SolidityMethod,
    Styles,
    TypeDefinitionByName,
    TypescriptMethod,
} from 'ts/types';
import { colors } from 'ts/utils/colors';
import { configs } from 'ts/utils/configs';
import { constants } from 'ts/utils/constants';
import { docUtils } from 'ts/utils/doc_utils';
import { utils } from 'ts/utils/utils';

const SCROLL_TOP_ID = 'docsScrollTop';

const networkNameToColor: { [network: string]: string } = {
    [Networks.kovan]: colors.purple,
    [Networks.ropsten]: colors.red,
    [Networks.mainnet]: colors.turquois,
};

export interface DocumentationAllProps {
    source: string;
    location: Location;
    dispatcher: Dispatcher;
    docsVersion: string;
    availableDocVersions: string[];
    docsInfo: DocsInfo;
}

interface DocumentationState {
    docAgnosticFormat?: DocAgnosticFormat;
}

const styles: Styles = {
    mainContainers: {
        position: 'absolute',
        top: 1,
        left: 0,
        bottom: 0,
        right: 0,
        overflowZ: 'hidden',
        overflowY: 'scroll',
        minHeight: 'calc(100vh - 1px)',
        WebkitOverflowScrolling: 'touch',
    },
    menuContainer: {
        borderColor: colors.grey300,
        maxWidth: 330,
        marginLeft: 20,
    },
};

export class Documentation extends React.Component<DocumentationAllProps, DocumentationState> {
    constructor(props: DocumentationAllProps) {
        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)
            ? {}
            : this.props.docsInfo.getMenuSubsectionsBySection(this.state.docAgnosticFormat);
        return (
            <div>
                <DocumentTitle title={`${this.props.docsInfo.displayName} Documentation`} />
                <TopBar
                    blockchainIsLoaded={false}
                    location={this.props.location}
                    docsVersion={this.props.docsVersion}
                    availableDocVersions={this.props.availableDocVersions}
                    menu={this.props.docsInfo.getMenu(this.props.docsVersion)}
                    menuSubsectionsBySection={menuSubsectionsBySection}
                    shouldFullWidth={true}
                    docsInfo={this.props.docsInfo}
                />
                {_.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={this.props.docsInfo.getMenu(this.props.docsVersion)}
                                    menuSubsectionsBySection={menuSubsectionsBySection}
                                    docPath={this.props.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={this.props.docsInfo.packageUrl} target="_blank">
                                        {this.props.docsInfo.displayName}
                                    </a>
                                </h1>
                                {this._renderDocumentation()}
                            </div>
                        </div>
                    </div>
                )}
            </div>
        );
    }
    private _renderDocumentation(): React.ReactNode {
        const subMenus = _.values(this.props.docsInfo.getMenu());
        const orderedSectionNames = _.flatten(subMenus);

        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 markdownFileIfExists = this.props.docsInfo.sectionNameToMarkdown[sectionName];
        if (!_.isUndefined(markdownFileIfExists)) {
            return (
                <MarkdownSection
                    key={`markdown-section-${sectionName}`}
                    sectionName={sectionName}
                    markdownContent={markdownFileIfExists}
                />
            );
        }

        const docSection = this.state.docAgnosticFormat[sectionName];
        if (_.isUndefined(docSection)) {
            return null;
        }

        const sortedTypes = _.sortBy(docSection.types, 'name');
        const typeDefs = _.map(sortedTypes, customType => {
            return (
                <TypeDefinition
                    sectionName={sectionName}
                    key={`type-${customType.name}`}
                    customType={customType}
                    docsInfo={this.props.docsInfo}
                />
            );
        });

        const sortedProperties = _.sortBy(docSection.properties, 'name');
        const propertyDefs = _.map(sortedProperties, this._renderProperty.bind(this, sectionName));

        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}
                    sectionName={sectionName}
                    docsInfo={this.props.docsInfo}
                />
            );
        });
        return (
            <div key={`section-${sectionName}`} className="py2 pr3 md-pl2 sm-pl3">
                <div className="flex">
                    <div style={{ marginRight: 7 }}>
                        <SectionHeader sectionName={sectionName} />
                    </div>
                    {this._renderNetworkBadgesIfExists(sectionName)}
                </div>
                {docSection.comment && <Comment comment={docSection.comment} />}
                {docSection.constructors.length > 0 &&
                    this.props.docsInfo.isVisibleConstructor(sectionName) && (
                        <div>
                            <h2 className="thin">Constructor</h2>
                            {this._renderConstructors(docSection.constructors, sectionName, 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>
                )}
                {!_.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 _renderNetworkBadgesIfExists(sectionName: string) {
        const networkToAddressByContractName = configs.CONTRACT_ADDRESS[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.NETWORK_ID_BY_NAME[networkName],
                    EtherscanLinkSuffixes.Address,
                );
                return (
                    <a
                        key={`badge-${networkName}-${sectionName}`}
                        href={linkIfExists}
                        target="_blank"
                        style={{ color: colors.white, textDecoration: 'none' }}
                    >
                        <Badge title={networkName} backgroundColor={networkNameToColor[networkName]} />
                    </a>
                );
            },
        );
        return badges;
    }
    private _renderConstructors(
        constructors: SolidityMethod[] | TypescriptMethod[],
        sectionName: string,
        typeDefinitionByName: TypeDefinitionByName,
    ): React.ReactNode {
        const constructorDefs = _.map(constructors, constructor => {
            return this._renderMethodBlocks(constructor, sectionName, constructor.isConstructor, typeDefinitionByName);
        });
        return <div>{constructorDefs}</div>;
    }
    private _renderProperty(sectionName: string, property: Property): React.ReactNode {
        return (
            <div key={`property-${property.name}-${property.type.name}`} className="pb3">
                <code className="hljs">
                    {property.name}:
                    <Type type={property.type} sectionName={sectionName} docsInfo={this.props.docsInfo} />
                </code>
                {property.source && (
                    <SourceLink
                        version={this.props.docsVersion}
                        source={property.source}
                        baseUrl={this.props.docsInfo.packageUrl}
                        subPackageName={this.props.docsInfo.subPackageName}
                    />
                )}
                {property.comment && <Comment comment={property.comment} className="py2" />}
            </div>
        );
    }
    private _renderMethodBlocks(
        method: SolidityMethod | TypescriptMethod,
        sectionName: string,
        isConstructor: boolean,
        typeDefinitionByName: TypeDefinitionByName,
    ): React.ReactNode {
        return (
            <MethodBlock
                key={`method-${method.name}-${sectionName}`}
                sectionName={sectionName}
                method={method}
                typeDefinitionByName={typeDefinitionByName}
                libraryVersion={this.props.docsVersion}
                docsInfo={this.props.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(this.props.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,
            this.props.docsInfo.docsJsonRoot,
        );
        const docAgnosticFormat = this.props.docsInfo.convertToDocAgnosticFormat(versionDocObj as DoxityDocObj);

        this.setState(
            {
                docAgnosticFormat,
            },
            () => {
                this._scrollToHash();
            },
        );
    }
}