Browse Source

Refactored withPoper HOC to PopperController using render prop

Dominik Prokop 7 years ago
parent
commit
de4e1a91f7

+ 6 - 11
public/app/core/components/Tooltip/Popper.tsx

@@ -1,6 +1,7 @@
 import React, { PureComponent } from 'react';
+import * as PopperJS from 'popper.js';
+import { Manager, Popper as ReactPopper } from 'react-popper';
 import Portal from 'app/core/components/Portal/Portal';
-import { Manager, Popper as ReactPopper, Reference } from 'react-popper';
 import Transition from 'react-transition-group/Transition';
 
 const defaultTransitionStyles = {
@@ -18,29 +19,23 @@ const transitionStyles = {
 interface Props {
   renderContent: (content: any) => any;
   show: boolean;
-  placement?: any;
+  placement?: PopperJS.Placement;
   content: string | ((props: any) => JSX.Element);
   refClassName?: string;
+  referenceElement: PopperJS.ReferenceObject;
 }
 
 class Popper extends PureComponent<Props> {
   render() {
-    const { children, renderContent, show, placement, refClassName } = this.props;
+    const { renderContent, show, placement } = this.props;
     const { content } = this.props;
 
     return (
       <Manager>
-        <Reference>
-          {({ ref }) => (
-            <div className={`popper_ref ${refClassName || ''}`} ref={ref}>
-              {children}
-            </div>
-          )}
-        </Reference>
         <Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
           {transitionState => (
             <Portal>
-              <ReactPopper placement={placement}>
+              <ReactPopper placement={placement} referenceElement={this.props.referenceElement}>
                 {({ ref, style, placement, arrowProps }) => {
                   return (
                     <div

+ 94 - 0
public/app/core/components/Tooltip/PopperController.tsx

@@ -0,0 +1,94 @@
+import React from 'react';
+import * as PopperJS from 'popper.js';
+
+type PopperContent = string | (() => JSX.Element);
+
+export interface UsingPopperProps {
+  show?: boolean;
+  placement?: PopperJS.Placement;
+  content: PopperContent;
+  children: JSX.Element;
+  renderContent?: (content: PopperContent) => JSX.Element;
+}
+
+type PopperControllerRenderProp = (
+  showPopper: () => void,
+  hidePopper: () => void,
+  popperProps: {
+    show: boolean;
+    placement: PopperJS.Placement;
+    content: string | ((props: any) => JSX.Element);
+    renderContent: (content: any) => any;
+  }
+) => JSX.Element;
+
+interface Props {
+  placement?: PopperJS.Placement;
+  content: PopperContent;
+  className?: string;
+  children: PopperControllerRenderProp;
+}
+
+interface State {
+  placement: PopperJS.Placement;
+  show: boolean;
+}
+
+class PopperController extends React.Component<Props, State> {
+  constructor(props: Props) {
+    super(props);
+
+    this.state = {
+      placement: this.props.placement || 'auto',
+      show: false,
+    };
+  }
+
+  componentWillReceiveProps(nextProps: Props) {
+    if (nextProps.placement && nextProps.placement !== this.state.placement) {
+      this.setState(prevState => {
+        return {
+          ...prevState,
+          placement: nextProps.placement,
+        };
+      });
+    }
+  }
+
+  showPopper = () => {
+    this.setState(prevState => ({
+      ...prevState,
+      show: true,
+    }));
+  };
+
+  hidePopper = () => {
+    this.setState(prevState => ({
+      ...prevState,
+      show: false,
+    }));
+  };
+
+  renderContent(content: PopperContent) {
+    if (typeof content === 'function') {
+      // If it's a function we assume it's a React component
+      const ReactComponent = content;
+      return <ReactComponent />;
+    }
+    return content;
+  }
+
+  render() {
+    const { children, content } = this.props;
+    const { show, placement } = this.state;
+
+    return children(this.showPopper, this.hidePopper, {
+      show,
+      placement,
+      content,
+      renderContent: this.renderContent,
+    });
+  }
+}
+
+export default PopperController;

+ 0 - 88
public/app/core/components/Tooltip/withPopper.tsx

@@ -1,88 +0,0 @@
-import React from 'react';
-
-export interface UsingPopperProps {
-  showPopper: (prevState: object) => void;
-  hidePopper: (prevState: object) => void;
-  renderContent: (content: any) => any;
-  show: boolean;
-  placement?: string;
-  content: string | ((props: any) => JSX.Element);
-  className?: string;
-  refClassName?: string;
-}
-
-interface Props {
-  placement?: string;
-  className?: string;
-  refClassName?: string;
-  content: string | ((props: any) => JSX.Element);
-}
-
-interface State {
-  placement: string;
-  show: boolean;
-}
-
-export default function withPopper(WrappedComponent) {
-  return class extends React.Component<Props, State> {
-    constructor(props) {
-      super(props);
-      this.setState = this.setState.bind(this);
-      this.state = {
-        placement: this.props.placement || 'auto',
-        show: false,
-      };
-    }
-
-    componentWillReceiveProps(nextProps) {
-      if (nextProps.placement && nextProps.placement !== this.state.placement) {
-        this.setState(prevState => {
-          return {
-            ...prevState,
-            placement: nextProps.placement,
-          };
-        });
-      }
-    }
-
-    showPopper = () => {
-      this.setState(prevState => ({
-        ...prevState,
-        show: true,
-      }));
-    };
-
-    hidePopper = () => {
-      this.setState(prevState => ({
-        ...prevState,
-        show: false,
-      }));
-    };
-
-    renderContent(content) {
-      if (typeof content === 'function') {
-        // If it's a function we assume it's a React component
-        const ReactComponent = content;
-        return <ReactComponent />;
-      }
-      return content;
-    }
-
-    render() {
-      const { show, placement } = this.state;
-      const className = this.props.className || '';
-
-      return (
-        <WrappedComponent
-          {...this.props}
-          showPopper={this.showPopper}
-          hidePopper={this.hidePopper}
-          renderContent={this.renderContent}
-          show={show}
-          placement={placement}
-          className={className}
-        />
-      );
-    }
-  };
-}