diff options
Diffstat (limited to 'packages/react-shared')
-rw-r--r-- | packages/react-shared/src/components/link.tsx | 154 |
1 files changed, 82 insertions, 72 deletions
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<LinkProps> = ({ - style, - className, - type, - to, - shouldOpenInNewTab, - children, - onMouseOver, - onMouseLeave, - onMouseEnter, - containerId, -}) => { - const styleWithDefault = { - textDecoration: 'none', - ...style, +export class Link extends React.Component<LinkProps, LinkState> { + public static defaultProps: Partial<LinkProps> = { + 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 ( - <a - target={shouldOpenInNewTab ? '_blank' : ''} - className={className} - style={styleWithDefault} - href={to} - onMouseOver={onMouseOver} - onMouseEnter={onMouseEnter} - onMouseLeave={onMouseLeave} - > - {children} - </a> - ); - case LinkType.ReactRoute: - return ( - <ReactRounterLink - to={to} - className={className} - style={styleWithDefault} - target={shouldOpenInNewTab ? '_blank' : ''} - onMouseOver={onMouseOver} - onMouseEnter={onMouseEnter} - onMouseLeave={onMouseLeave} - > - {children} - </ReactRounterLink> - ); - case LinkType.ReactScroll: - return ( - <ScrollLink - to={to} - offset={0} - hashSpy={true} - duration={constants.DOCS_SCROLL_DURATION_MS} - containerId={containerId} - > - {children} - </ScrollLink> - ); - 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 ( + <a + target={this.props.shouldOpenInNewTab ? '_blank' : ''} + className={this.props.className} + style={styleWithDefault} + href={this.props.to} + onMouseOver={this.props.onMouseOver} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + > + {this.props.children} + </a> + ); + case LinkType.ReactRoute: + return ( + <ReactRounterLink + to={this.props.to} + className={this.props.className} + style={styleWithDefault} + target={this.props.shouldOpenInNewTab ? '_blank' : ''} + onMouseOver={this.props.onMouseOver} + onMouseEnter={this.props.onMouseEnter} + onMouseLeave={this.props.onMouseLeave} + > + {this.props.children} + </ReactRounterLink> + ); + case LinkType.ReactScroll: + return ( + <span ref={input => (this._outerReactScrollSpan = input)}> + <ScrollLink + to={this.props.to} + offset={0} + hashSpy={true} + duration={constants.DOCS_SCROLL_DURATION_MS} + containerId={this.props.containerId} + style={styleWithDefault} + > + <span onClick={this._onClickPropagateClickEventAroundScrollLink.bind(this)}> + {this.props.children} + </span> + </ScrollLink> + </span> + ); + 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(); + } + } +} |