From 055763cceb9951b038b07488ddc7ae35210f1efe Mon Sep 17 00:00:00 2001
From: Fabio Berger <me@fabioberger.com>
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<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();
+        }
+    }
+}
-- 
cgit 

