Update react-markdown, properly scroll to section for wiki internal links, consolidate scrollTo logic and make external links open in new tabs
This commit is contained in:
parent
edaa0b0e34
commit
67c834841e
@ -46,7 +46,7 @@
|
|||||||
"react-highlight": "0xproject/react-highlight",
|
"react-highlight": "0xproject/react-highlight",
|
||||||
"react-html5video": "^2.1.0",
|
"react-html5video": "^2.1.0",
|
||||||
"react-inlinesvg": "^0.5.5",
|
"react-inlinesvg": "^0.5.5",
|
||||||
"react-markdown": "^2.5.0",
|
"react-markdown": "^3.2.2",
|
||||||
"react-recaptcha": "^2.3.2",
|
"react-recaptcha": "^2.3.2",
|
||||||
"react-redux": "^5.0.3",
|
"react-redux": "^5.0.3",
|
||||||
"react-router-dom": "^4.1.1",
|
"react-router-dom": "^4.1.1",
|
||||||
|
@ -15,7 +15,7 @@ const defaultProps = {
|
|||||||
export const Comment: React.SFC<CommentProps> = (props: CommentProps) => {
|
export const Comment: React.SFC<CommentProps> = (props: CommentProps) => {
|
||||||
return (
|
return (
|
||||||
<div className={`${props.className} comment`}>
|
<div className={`${props.className} comment`}>
|
||||||
<ReactMarkdown source={props.comment} renderers={{ CodeBlock: MarkdownCodeBlock }} />
|
<ReactMarkdown source={props.comment} renderers={{ code: MarkdownCodeBlock }} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -29,11 +29,11 @@ import {
|
|||||||
TypescriptMethod,
|
TypescriptMethod,
|
||||||
} from 'ts/types';
|
} from 'ts/types';
|
||||||
import { colors } from 'ts/utils/colors';
|
import { colors } from 'ts/utils/colors';
|
||||||
|
import { configs } from 'ts/utils/configs';
|
||||||
import { constants } from 'ts/utils/constants';
|
import { constants } from 'ts/utils/constants';
|
||||||
import { utils } from 'ts/utils/utils';
|
import { utils } from 'ts/utils/utils';
|
||||||
|
|
||||||
const TOP_BAR_HEIGHT = 60;
|
const TOP_BAR_HEIGHT = 60;
|
||||||
const SCROLL_TOP_ID = 'docsScrollTop';
|
|
||||||
|
|
||||||
const networkNameToColor: { [network: string]: string } = {
|
const networkNameToColor: { [network: string]: string } = {
|
||||||
[Networks.Kovan]: colors.purple,
|
[Networks.Kovan]: colors.purple,
|
||||||
@ -76,7 +76,7 @@ const styles: Styles = {
|
|||||||
export class Documentation extends React.Component<DocumentationProps, DocumentationState> {
|
export class Documentation extends React.Component<DocumentationProps, DocumentationState> {
|
||||||
public componentDidUpdate(prevProps: DocumentationProps, prevState: DocumentationState) {
|
public componentDidUpdate(prevProps: DocumentationProps, prevState: DocumentationState) {
|
||||||
if (!_.isEqual(prevProps.docAgnosticFormat, this.props.docAgnosticFormat)) {
|
if (!_.isEqual(prevProps.docAgnosticFormat, this.props.docAgnosticFormat)) {
|
||||||
this._scrollToHash();
|
utils.scrollToHash(this.props.location.hash, configs.SCROLL_CONTAINER_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public render() {
|
public render() {
|
||||||
@ -115,8 +115,12 @@ export class Documentation extends React.Component<DocumentationProps, Documenta
|
|||||||
className="relative col lg-col-9 md-col-9 sm-col-12 col-12"
|
className="relative col lg-col-9 md-col-9 sm-col-12 col-12"
|
||||||
style={{ backgroundColor: colors.white }}
|
style={{ backgroundColor: colors.white }}
|
||||||
>
|
>
|
||||||
<div id="documentation" style={styles.mainContainers} className="absolute px1">
|
<div
|
||||||
<div id={SCROLL_TOP_ID} />
|
id={configs.SCROLL_CONTAINER_ID}
|
||||||
|
style={styles.mainContainers}
|
||||||
|
className="absolute px1"
|
||||||
|
>
|
||||||
|
<div id={configs.SCROLL_TOP_ID} />
|
||||||
{this._renderDocumentation()}
|
{this._renderDocumentation()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -325,17 +329,4 @@ export class Documentation extends React.Component<DocumentationProps, Documenta
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import * as React from 'react';
|
|||||||
import * as HighLight from 'react-highlight';
|
import * as HighLight from 'react-highlight';
|
||||||
|
|
||||||
interface MarkdownCodeBlockProps {
|
interface MarkdownCodeBlockProps {
|
||||||
literal: string;
|
value: string;
|
||||||
language: string;
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,12 +13,12 @@ export class MarkdownCodeBlock extends React.Component<MarkdownCodeBlockProps, M
|
|||||||
// Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying
|
// Re-rendering a codeblock causes any use selection to become de-selected. This is annoying when trying
|
||||||
// to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed.
|
// to copy-paste code examples. We therefore noop re-renders on this component if it's props haven't changed.
|
||||||
public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) {
|
public shouldComponentUpdate(nextProps: MarkdownCodeBlockProps, nextState: MarkdownCodeBlockState) {
|
||||||
return nextProps.literal !== this.props.literal || nextProps.language !== this.props.language;
|
return nextProps.value !== this.props.value || nextProps.language !== this.props.language;
|
||||||
}
|
}
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<span style={{ fontSize: 14 }}>
|
<span style={{ fontSize: 14 }}>
|
||||||
<HighLight className={this.props.language || 'javascript'}>{this.props.literal}</HighLight>
|
<HighLight className={this.props.language || 'javascript'}>{this.props.value}</HighLight>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
41
packages/website/ts/pages/shared/markdown_link_block.tsx
Normal file
41
packages/website/ts/pages/shared/markdown_link_block.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import * as _ from 'lodash';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { configs } from 'ts/utils/configs';
|
||||||
|
import { utils } from 'ts/utils/utils';
|
||||||
|
|
||||||
|
interface MarkdownLinkBlockProps {
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MarkdownLinkBlockState {}
|
||||||
|
|
||||||
|
export class MarkdownLinkBlock extends React.Component<MarkdownLinkBlockProps, MarkdownLinkBlockState> {
|
||||||
|
// Re-rendering a linkBlock causes any use selection to become de-selected making the link unclickable.
|
||||||
|
// We therefore noop re-renders on this component if it's props haven't changed.
|
||||||
|
public shouldComponentUpdate(nextProps: MarkdownLinkBlockProps, nextState: MarkdownLinkBlockState) {
|
||||||
|
return nextProps.href !== this.props.href;
|
||||||
|
}
|
||||||
|
public render() {
|
||||||
|
const href = this.props.href;
|
||||||
|
// If protocol is http or https, we can open in a new tab, otherwise don't for security reasons
|
||||||
|
if (_.startsWith(href, 'http') || _.startsWith(href, 'https')) {
|
||||||
|
return (
|
||||||
|
<a href={href} target="_blank" rel="nofollow noreferrer noopener">
|
||||||
|
{this.props.children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else if (_.startsWith(href, '#')) {
|
||||||
|
return (
|
||||||
|
<a style={{ cursor: 'pointer' }} onClick={this._onHashUrlClick.bind(this, href)}>
|
||||||
|
{this.props.children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <a href={href}>{this.props.children}</a>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private _onHashUrlClick(href: string) {
|
||||||
|
const hashWithPrefix = `#${href.split('#')[1]}`;
|
||||||
|
utils.scrollToHash(hashWithPrefix, configs.SCROLL_CONTAINER_ID);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import * as ReactMarkdown from 'react-markdown';
|
|||||||
import { Element as ScrollElement } from 'react-scroll';
|
import { Element as ScrollElement } from 'react-scroll';
|
||||||
import { AnchorTitle } from 'ts/pages/shared/anchor_title';
|
import { AnchorTitle } from 'ts/pages/shared/anchor_title';
|
||||||
import { MarkdownCodeBlock } from 'ts/pages/shared/markdown_code_block';
|
import { MarkdownCodeBlock } from 'ts/pages/shared/markdown_code_block';
|
||||||
|
import { MarkdownLinkBlock } from 'ts/pages/shared/markdown_link_block';
|
||||||
import { HeaderSizes } from 'ts/types';
|
import { HeaderSizes } from 'ts/types';
|
||||||
import { colors } from 'ts/utils/colors';
|
import { colors } from 'ts/utils/colors';
|
||||||
import { utils } from 'ts/utils/utils';
|
import { utils } from 'ts/utils/utils';
|
||||||
@ -64,7 +65,14 @@ export class MarkdownSection extends React.Component<MarkdownSectionProps, Markd
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr style={{ border: `1px solid ${colors.lightestGrey}` }} />
|
<hr style={{ border: `1px solid ${colors.lightestGrey}` }} />
|
||||||
<ReactMarkdown source={this.props.markdownContent} renderers={{ CodeBlock: MarkdownCodeBlock }} />
|
<ReactMarkdown
|
||||||
|
source={this.props.markdownContent}
|
||||||
|
escapeHtml={false}
|
||||||
|
renderers={{
|
||||||
|
code: MarkdownCodeBlock,
|
||||||
|
link: MarkdownLinkBlock,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</ScrollElement>
|
</ScrollElement>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ import * as _ from 'lodash';
|
|||||||
import DropDownMenu from 'material-ui/DropDownMenu';
|
import DropDownMenu from 'material-ui/DropDownMenu';
|
||||||
import MenuItem from 'material-ui/MenuItem';
|
import MenuItem from 'material-ui/MenuItem';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import { utils } from 'ts/utils/utils';
|
||||||
|
|
||||||
interface VersionDropDownProps {
|
interface VersionDropDownProps {
|
||||||
selectedVersion: string;
|
selectedVersion: string;
|
||||||
@ -31,8 +32,6 @@ export class VersionDropDown extends React.Component<VersionDropDownProps, Versi
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
private _updateSelectedVersion(e: any, index: number, semver: string) {
|
private _updateSelectedVersion(e: any, index: number, semver: string) {
|
||||||
const port = window.location.port;
|
|
||||||
const hasPort = !_.isUndefined(port);
|
|
||||||
let path = window.location.pathname;
|
let path = window.location.pathname;
|
||||||
const lastChar = path[path.length - 1];
|
const lastChar = path[path.length - 1];
|
||||||
if (_.isFinite(_.parseInt(lastChar))) {
|
if (_.isFinite(_.parseInt(lastChar))) {
|
||||||
@ -40,7 +39,7 @@ export class VersionDropDown extends React.Component<VersionDropDownProps, Versi
|
|||||||
pathSections.pop();
|
pathSections.pop();
|
||||||
path = pathSections.join('/');
|
path = pathSections.join('/');
|
||||||
}
|
}
|
||||||
const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}${path}`;
|
const baseUrl = utils.getCurrentBaseUrl();
|
||||||
window.location.href = `${baseUrl}/${semver}${window.location.hash}`;
|
window.location.href = `${baseUrl}${path}/${semver}${window.location.hash}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,11 +135,11 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="documentation"
|
id={configs.SCROLL_CONTAINER_ID}
|
||||||
style={{ ...mainContainersStyle, overflow: 'auto' }}
|
style={{ ...mainContainersStyle, overflow: 'auto' }}
|
||||||
className="absolute"
|
className="absolute"
|
||||||
>
|
>
|
||||||
<div id="0xProtocolWiki" />
|
<div id={configs.SCROLL_TOP_ID} />
|
||||||
<div id="wiki" style={{ paddingRight: 2 }}>
|
<div id="wiki" style={{ paddingRight: 2 }}>
|
||||||
{this._renderWikiArticles()}
|
{this._renderWikiArticles()}
|
||||||
</div>
|
</div>
|
||||||
@ -188,19 +188,6 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private _scrollToHash(): void {
|
|
||||||
const hashWithPrefix = this.props.location.hash;
|
|
||||||
let hash = hashWithPrefix.slice(1);
|
|
||||||
if (_.isEmpty(hash)) {
|
|
||||||
hash = '0xProtocolWiki'; // scroll to the top
|
|
||||||
}
|
|
||||||
|
|
||||||
scroller.scrollTo(hash, {
|
|
||||||
duration: 0,
|
|
||||||
offset: 0,
|
|
||||||
containerId: 'documentation',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private async _fetchArticlesBySectionAsync(): Promise<void> {
|
private async _fetchArticlesBySectionAsync(): Promise<void> {
|
||||||
const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`;
|
const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`;
|
||||||
const response = await fetch(endpoint);
|
const response = await fetch(endpoint);
|
||||||
@ -225,7 +212,7 @@ export class Wiki extends React.Component<WikiProps, WikiState> {
|
|||||||
articlesBySection,
|
articlesBySection,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this._scrollToHash();
|
utils.scrollToHash(this.props.location.hash, configs.SCROLL_CONTAINER_ID);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,8 @@ import * as _ from 'lodash';
|
|||||||
import { ContractAddresses, Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types';
|
import { ContractAddresses, Environments, OutdatedWrappedEtherByNetworkId, PublicNodeUrlsByNetworkId } from 'ts/types';
|
||||||
|
|
||||||
const BASE_URL = window.location.origin;
|
const BASE_URL = window.location.origin;
|
||||||
const isDevelopment = _.includes(
|
const developmentUrls = ['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'];
|
||||||
['https://0xproject.localhost:3572', 'https://localhost:3572', 'https://127.0.0.1'],
|
const isDevelopment = _.includes(developmentUrls, BASE_URL);
|
||||||
BASE_URL,
|
|
||||||
);
|
|
||||||
const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
|
const INFURA_API_KEY = 'T5WSC8cautR4KXyYgsRs';
|
||||||
|
|
||||||
export const configs = {
|
export const configs = {
|
||||||
@ -15,6 +13,7 @@ export const configs = {
|
|||||||
DEFAULT_DERIVATION_PATH: `44'/60'/0'`,
|
DEFAULT_DERIVATION_PATH: `44'/60'/0'`,
|
||||||
// WARNING: ZRX & WETH MUST always be default trackedTokens
|
// WARNING: ZRX & WETH MUST always be default trackedTokens
|
||||||
DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
|
DEFAULT_TRACKED_TOKEN_SYMBOLS: ['WETH', 'ZRX'],
|
||||||
|
DEVELOPMENT_URLS: developmentUrls,
|
||||||
DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
|
DOMAIN_STAGING: 'staging-0xproject.s3-website-us-east-1.amazonaws.com',
|
||||||
DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
|
DOMAIN_DEVELOPMENT: '0xproject.localhost:3572',
|
||||||
DOMAIN_PRODUCTION: '0xproject.com',
|
DOMAIN_PRODUCTION: '0xproject.com',
|
||||||
@ -94,6 +93,8 @@ export const configs = {
|
|||||||
[3]: [`https://ropsten.infura.io/${INFURA_API_KEY}`],
|
[3]: [`https://ropsten.infura.io/${INFURA_API_KEY}`],
|
||||||
[4]: [`https://rinkeby.infura.io/${INFURA_API_KEY}`],
|
[4]: [`https://rinkeby.infura.io/${INFURA_API_KEY}`],
|
||||||
} as PublicNodeUrlsByNetworkId,
|
} as PublicNodeUrlsByNetworkId,
|
||||||
|
SCROLL_CONTAINER_ID: 'documentation',
|
||||||
|
SCROLL_TOP_ID: 'pageScrollTop',
|
||||||
SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
|
SHOULD_DEPRECATE_OLD_WETH_TOKEN: true,
|
||||||
SYMBOLS_OF_MINTABLE_KOVAN_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
|
SYMBOLS_OF_MINTABLE_KOVAN_TOKENS: ['MKR', 'MLN', 'GNT', 'DGD', 'REP'],
|
||||||
SYMBOLS_OF_MINTABLE_RINKEBY_ROPSTEN_TOKENS: [
|
SYMBOLS_OF_MINTABLE_RINKEBY_ROPSTEN_TOKENS: [
|
||||||
|
@ -4,6 +4,7 @@ import deepEqual = require('deep-equal');
|
|||||||
import isMobile = require('is-mobile');
|
import isMobile = require('is-mobile');
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
import { scroller } from 'react-scroll';
|
||||||
import {
|
import {
|
||||||
EtherscanLinkSuffixes,
|
EtherscanLinkSuffixes,
|
||||||
Networks,
|
Networks,
|
||||||
@ -290,4 +291,22 @@ export const utils = {
|
|||||||
);
|
);
|
||||||
return isTestNetwork;
|
return isTestNetwork;
|
||||||
},
|
},
|
||||||
|
getCurrentBaseUrl() {
|
||||||
|
const port = window.location.port;
|
||||||
|
const hasPort = !_.isUndefined(port);
|
||||||
|
const baseUrl = `https://${window.location.hostname}${hasPort ? `:${port}` : ''}`;
|
||||||
|
return baseUrl;
|
||||||
|
},
|
||||||
|
scrollToHash(hashWithPrefix: string, containerId: string): void {
|
||||||
|
let hash = hashWithPrefix.slice(1);
|
||||||
|
if (_.isEmpty(hash)) {
|
||||||
|
hash = configs.SCROLL_TOP_ID; // scroll to the top
|
||||||
|
}
|
||||||
|
|
||||||
|
scroller.scrollTo(hash, {
|
||||||
|
duration: 0,
|
||||||
|
offset: 0,
|
||||||
|
containerId,
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user