From 437612f8b8c28fb384698c5c2b331e173cee8767 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 3 Oct 2018 16:37:59 +0100 Subject: Use same Link UI component for react-scroll links --- packages/react-shared/src/components/link.tsx | 100 ++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 packages/react-shared/src/components/link.tsx (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx new file mode 100644 index 000000000..7425b9365 --- /dev/null +++ b/packages/react-shared/src/components/link.tsx @@ -0,0 +1,100 @@ +import * as _ from 'lodash'; +import * as React from 'react'; +import { Link as ReactRounterLink } from 'react-router-dom'; +import { Link as ScrollLink } from 'react-scroll'; + +import { LinkType } from '../types'; +import { constants } from '../utils/constants'; + +export interface LinkProps { + to: string; + type?: LinkType; + shouldOpenInNewTab?: boolean; + style?: React.CSSProperties; + className?: string; + onMouseOver?: (event: React.MouseEvent) => void; + onMouseLeave?: (event: React.MouseEvent) => void; + onMouseEnter?: (event: React.MouseEvent) => void; + containerId?: string; +} + +/** + * A generic link component which let's the developer render internal & external links, and their associated + * behaviors with a single link component. Many times we want a menu including both internal & external links + * and this abstracts away the differences of rendering both types of links. + */ +export const Link: React.StatelessComponent = ({ + style, + className, + type, + to, + shouldOpenInNewTab, + children, + onMouseOver, + onMouseLeave, + onMouseEnter, + containerId, +}) => { + const styleWithDefault = { + textDecoration: 'none', + ...style, + }; + + switch (type) { + case LinkType.External: + return ( + + {children} + + ); + case LinkType.ReactRoute: + return ( + + {children} + + ); + case LinkType.ReactScroll: + return ( + + {children} + + ); + default: + throw new Error(`Unrecognized LinkType: ${type}`); + } +}; + +Link.defaultProps = { + type: LinkType.ReactRoute, + shouldOpenInNewTab: false, + style: {}, + className: '', + onMouseOver: _.noop.bind(_), + onMouseLeave: _.noop.bind(_), + onMouseEnter: _.noop.bind(_), + containerId: constants.DOCS_CONTAINER_ID, +}; + +Link.displayName = 'Link'; -- cgit v1.2.3 From 1bef42cdcb45e4614ffdc8d8a1e455b4fe970a45 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 3 Oct 2018 17:17:16 +0100 Subject: Improve comment --- packages/react-shared/src/components/link.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 7425b9365..6200bfbd3 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -19,9 +19,10 @@ export interface LinkProps { } /** - * A generic link component which let's the developer render internal & external links, and their associated - * behaviors with a single link component. Many times we want a menu including both internal & external links - * and this abstracts away the differences of rendering both types of links. + * A generic link component which let's the developer render internal, external and scroll-to-hash links, and + * their associated behaviors with a single link component. Many times we want a menu including a combination of + * internal, external and scroll links and the abstraction of the differences of rendering each types of link + * makes it much easier to do so. */ export const Link: React.StatelessComponent = ({ style, -- cgit v1.2.3 From 055763cceb9951b038b07488ddc7ae35210f1efe Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 11:13:33 +0100 Subject: Fix bug where wiki links in dev dropdown weren't working when on the wiki --- packages/react-shared/src/components/link.tsx | 154 ++++++++++++++------------ 1 file changed, 82 insertions(+), 72 deletions(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 6200bfbd3..7b22dc4fa 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -18,84 +18,94 @@ export interface LinkProps { containerId?: string; } +export interface LinkState {} + /** * A generic link component which let's the developer render internal, external and scroll-to-hash links, and * their associated behaviors with a single link component. Many times we want a menu including a combination of * internal, external and scroll links and the abstraction of the differences of rendering each types of link * makes it much easier to do so. */ -export const Link: React.StatelessComponent = ({ - style, - className, - type, - to, - shouldOpenInNewTab, - children, - onMouseOver, - onMouseLeave, - onMouseEnter, - containerId, -}) => { - const styleWithDefault = { - textDecoration: 'none', - ...style, +export class Link extends React.Component { + public static defaultProps: Partial = { + type: LinkType.ReactRoute, + shouldOpenInNewTab: false, + style: {}, + className: '', + onMouseOver: _.noop.bind(_), + onMouseLeave: _.noop.bind(_), + onMouseEnter: _.noop.bind(_), + containerId: constants.DOCS_CONTAINER_ID, }; - - switch (type) { - case LinkType.External: - return ( - - {children} - - ); - case LinkType.ReactRoute: - return ( - - {children} - - ); - case LinkType.ReactScroll: - return ( - - {children} - - ); - default: - throw new Error(`Unrecognized LinkType: ${type}`); + private _outerReactScrollSpan: HTMLSpanElement | null; + constructor(props: LinkProps) { + super(props); + this._outerReactScrollSpan = null; } -}; - -Link.defaultProps = { - type: LinkType.ReactRoute, - shouldOpenInNewTab: false, - style: {}, - className: '', - onMouseOver: _.noop.bind(_), - onMouseLeave: _.noop.bind(_), - onMouseEnter: _.noop.bind(_), - containerId: constants.DOCS_CONTAINER_ID, -}; + public render(): React.ReactNode { + const styleWithDefault = { + textDecoration: 'none', + cursor: 'pointer', + ...this.props.style, + }; -Link.displayName = 'Link'; + switch (this.props.type) { + case LinkType.External: + return ( + + {this.props.children} + + ); + case LinkType.ReactRoute: + return ( + + {this.props.children} + + ); + case LinkType.ReactScroll: + return ( + (this._outerReactScrollSpan = input)}> + + + {this.props.children} + + + + ); + default: + throw new Error(`Unrecognized LinkType: ${this.props.type}`); + } + } + // HACK(fabio): For some reason, the react-scroll link decided to stop the propagation of click events. + // We do however rely on these events being propagated in certain scenarios (e.g when the link + // is within a dropdown we want to close upon being clicked). Because of this, we registry the + // click event of an inner span, and pass it around the react-scroll link to an outer span. + private _onClickPropagateClickEventAroundScrollLink(): void { + if (!_.isNull(this._outerReactScrollSpan)) { + this._outerReactScrollSpan.click(); + } + } +} -- cgit v1.2.3 From e7de4b953fe8aaefee9c043679b021a485dc6dee Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 11:51:52 +0100 Subject: Apply props to all link variants when possible --- packages/react-shared/src/components/link.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 7b22dc4fa..ee55cc642 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -80,13 +80,19 @@ export class Link extends React.Component { ); case LinkType.ReactScroll: return ( - (this._outerReactScrollSpan = input)}> + (this._outerReactScrollSpan = input)} + onMouseOver={this.props.onMouseOver} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + > -- cgit v1.2.3 From f7aee9c7a9df36e20888c10616a249c2a79ed749 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 12:00:24 +0100 Subject: Remove containerId option and throw if passing in shouldOpenInNewTab with type ReactScroll --- packages/react-shared/src/components/link.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index ee55cc642..e8a13d506 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -6,7 +6,7 @@ import { Link as ScrollLink } from 'react-scroll'; import { LinkType } from '../types'; import { constants } from '../utils/constants'; -export interface LinkProps { +interface LinkProps { to: string; type?: LinkType; shouldOpenInNewTab?: boolean; @@ -15,7 +15,6 @@ export interface LinkProps { onMouseOver?: (event: React.MouseEvent) => void; onMouseLeave?: (event: React.MouseEvent) => void; onMouseEnter?: (event: React.MouseEvent) => void; - containerId?: string; } export interface LinkState {} @@ -35,7 +34,6 @@ export class Link extends React.Component { onMouseOver: _.noop.bind(_), onMouseLeave: _.noop.bind(_), onMouseEnter: _.noop.bind(_), - containerId: constants.DOCS_CONTAINER_ID, }; private _outerReactScrollSpan: HTMLSpanElement | null; constructor(props: LinkProps) { @@ -43,6 +41,10 @@ export class Link extends React.Component { this._outerReactScrollSpan = null; } public render(): React.ReactNode { + if (this.props.type === LinkType.ReactScroll && this.props.shouldOpenInNewTab) { + throw new Error(`Cannot open LinkType.ReactScroll links in new tab. link.to: ${this.props.to}`); + } + const styleWithDefault = { textDecoration: 'none', cursor: 'pointer', @@ -91,7 +93,7 @@ export class Link extends React.Component { offset={0} hashSpy={true} duration={constants.DOCS_SCROLL_DURATION_MS} - containerId={this.props.containerId} + containerId={constants.DOCS_CONTAINER_ID} className={this.props.className} style={styleWithDefault} > -- cgit v1.2.3 From e0355a2e395fc056fd1d5c5e1780784853546acd Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 12:03:15 +0100 Subject: Rename scroll container --- packages/react-shared/src/components/link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index e8a13d506..51ffa1e1d 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -93,7 +93,7 @@ export class Link extends React.Component { offset={0} hashSpy={true} duration={constants.DOCS_SCROLL_DURATION_MS} - containerId={constants.DOCS_CONTAINER_ID} + containerId={constants.SCROLL_CONTAINER_ID} className={this.props.className} style={styleWithDefault} > -- cgit v1.2.3 From 5f2cd33da07a7acc65f2e05acb35755f74af75a4 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 13:54:37 +0100 Subject: Remove style prop from Link --- packages/react-shared/src/components/link.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 51ffa1e1d..60891dd7a 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -10,11 +10,12 @@ interface LinkProps { to: string; type?: LinkType; shouldOpenInNewTab?: boolean; - style?: React.CSSProperties; className?: string; onMouseOver?: (event: React.MouseEvent) => void; onMouseLeave?: (event: React.MouseEvent) => void; onMouseEnter?: (event: React.MouseEvent) => void; + textDecoration?: string; + fontColor?: string; } export interface LinkState {} @@ -29,11 +30,12 @@ export class Link extends React.Component { public static defaultProps: Partial = { type: LinkType.ReactRoute, shouldOpenInNewTab: false, - style: {}, className: '', onMouseOver: _.noop.bind(_), onMouseLeave: _.noop.bind(_), onMouseEnter: _.noop.bind(_), + textDecoration: 'none', + fontColor: 'inherit', }; private _outerReactScrollSpan: HTMLSpanElement | null; constructor(props: LinkProps) { @@ -46,11 +48,13 @@ export class Link extends React.Component { } const styleWithDefault = { - textDecoration: 'none', + textDecoration: this.props.textDecoration, cursor: 'pointer', - ...this.props.style, + color: this.props.fontColor, }; + console.log('styleWithDefault', styleWithDefault); + switch (this.props.type) { case LinkType.External: return ( -- cgit v1.2.3 From fa6bd348992674192d07fef3d60950980212ecbb Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 14:55:28 +0100 Subject: Remove type prop and instead infer it from the value of to --- packages/react-shared/src/components/link.tsx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 60891dd7a..05ca93cc5 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -2,13 +2,13 @@ import * as _ from 'lodash'; import * as React from 'react'; import { Link as ReactRounterLink } from 'react-router-dom'; import { Link as ScrollLink } from 'react-scroll'; +import * as validUrl from 'valid-url'; import { LinkType } from '../types'; import { constants } from '../utils/constants'; interface LinkProps { to: string; - type?: LinkType; shouldOpenInNewTab?: boolean; className?: string; onMouseOver?: (event: React.MouseEvent) => void; @@ -28,7 +28,6 @@ export interface LinkState {} */ export class Link extends React.Component { public static defaultProps: Partial = { - type: LinkType.ReactRoute, shouldOpenInNewTab: false, className: '', onMouseOver: _.noop.bind(_), @@ -43,7 +42,18 @@ export class Link extends React.Component { this._outerReactScrollSpan = null; } public render(): React.ReactNode { - if (this.props.type === LinkType.ReactScroll && this.props.shouldOpenInNewTab) { + let type: LinkType; + const isReactRoute = _.startsWith(this.props.to, '/'); + const isExternal = validUrl.isWebUri(this.props.to) || _.startsWith(this.props.to, 'mailto:'); + if (isReactRoute) { + type = LinkType.ReactRoute; + } else if (isExternal) { + type = LinkType.External; + } else { + type = LinkType.ReactScroll; + } + + if (type === LinkType.ReactScroll && this.props.shouldOpenInNewTab) { throw new Error(`Cannot open LinkType.ReactScroll links in new tab. link.to: ${this.props.to}`); } @@ -53,9 +63,7 @@ export class Link extends React.Component { color: this.props.fontColor, }; - console.log('styleWithDefault', styleWithDefault); - - switch (this.props.type) { + switch (type) { case LinkType.External: return ( { ); default: - throw new Error(`Unrecognized LinkType: ${this.props.type}`); + throw new Error(`Unrecognized LinkType: ${type}`); } } // HACK(fabio): For some reason, the react-scroll link decided to stop the propagation of click events. -- cgit v1.2.3 From 3ce665eeabb9fc75dd50847304242de096e54d46 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 5 Oct 2018 17:58:22 +0100 Subject: Fix comment --- packages/react-shared/src/components/link.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 05ca93cc5..5a456109b 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -121,7 +121,7 @@ export class Link extends React.Component { } // HACK(fabio): For some reason, the react-scroll link decided to stop the propagation of click events. // We do however rely on these events being propagated in certain scenarios (e.g when the link - // is within a dropdown we want to close upon being clicked). Because of this, we registry the + // is within a dropdown we want to close upon being clicked). Because of this, we register the // click event of an inner span, and pass it around the react-scroll link to an outer span. private _onClickPropagateClickEventAroundScrollLink(): void { if (!_.isNull(this._outerReactScrollSpan)) { -- cgit v1.2.3 From 0de654bbd52f7d4702cec9f1a9a5a2cbb793181b Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 19 Oct 2018 17:40:55 +0100 Subject: fix: scroll lag on doc reference and wiki pages by using react-scroll `spy` and only updating the sidebar menu items whose active state had changed --- packages/react-shared/src/components/link.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'packages/react-shared/src/components/link.tsx') diff --git a/packages/react-shared/src/components/link.tsx b/packages/react-shared/src/components/link.tsx index 5a456109b..089e6e2ba 100644 --- a/packages/react-shared/src/components/link.tsx +++ b/packages/react-shared/src/components/link.tsx @@ -7,7 +7,7 @@ import * as validUrl from 'valid-url'; import { LinkType } from '../types'; import { constants } from '../utils/constants'; -interface LinkProps { +interface BaseLinkProps { to: string; shouldOpenInNewTab?: boolean; className?: string; @@ -18,6 +18,12 @@ interface LinkProps { fontColor?: string; } +interface ScrollLinkProps extends BaseLinkProps { + onActivityChanged?: (isActive: boolean) => void; +} + +type LinkProps = BaseLinkProps & ScrollLinkProps; + export interface LinkState {} /** @@ -103,11 +109,14 @@ export class Link extends React.Component { {this.props.children} @@ -119,6 +128,11 @@ export class Link extends React.Component { throw new Error(`Unrecognized LinkType: ${type}`); } } + private _onActivityChanged(isActive: boolean): void { + if (this.props.onActivityChanged) { + this.props.onActivityChanged(isActive); + } + } // HACK(fabio): For some reason, the react-scroll link decided to stop the propagation of click events. // We do however rely on these events being propagated in certain scenarios (e.g when the link // is within a dropdown we want to close upon being clicked). Because of this, we register the -- cgit v1.2.3