All files / src/store propsToRedux.js

72% Statements 36/50
57.69% Branches 15/26
71.43% Functions 10/14
71.43% Lines 35/49
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                                                              4x       4x 56x 36x       4x     8x 24x 24x   4x 4x   4x       7x 7x 7x 7x 7x 7x             6x             1x   1x 8x 2x   2x   2x   2x 2x         2x                                               8x 8x     8x             4x 12x         4x 12x           4x        
/**
* Copyright 2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
 
/**
 * This wrapper listens to prop changes and forwards these to their
 * appropriate redux store actions.
 */
 
import React, { Component } from 'react';
import createRef from 'create-react-ref/lib/createRef';
 
import {
  forOwn,
  isEqual,
  pick,
  reduce,
  omit,
} from 'lodash-es';
 
import createMSAStore from './createMSAStore';
import {MSAPropTypes, PropTypes} from '../PropTypes';
import mainStoreActions from './actions';
import { actions as positionStoreActions } from './positionReducers';
import requestAnimation from '../utils/requestAnimation';
 
/// Maps property changes to redux actions
const reduxActions = {
  "sequences": "updateSequences",
}
 
Object.keys(MSAPropTypes).forEach(key => {
  if(!(key in reduxActions) && MSAPropTypes[key] !== PropTypes.func) {
    reduxActions[key] = 'updateProp';
  }
});
 
const attributesToStore = Object.keys(reduxActions);
 
// precompute [action.key]: action for performance
const mapToActionKeys = (obj) => reduce(obj, (acc, v, k) => {
  acc[v.key] = v;
  return acc;
}, {});
const mainStoreActionKeys = mapToActionKeys(mainStoreActions);
const positionStoreActionKeys = mapToActionKeys(positionStoreActions);
 
export const PropsToRedux = (WrappedComponent) => {
  class PropsToReduxComponent extends Component {
 
    constructor(props) {
      super(props);
      const storeProps = pick(props, attributesToStore) || {};
      this.el = createRef();
      this.msaStore = props.msaStore;
      Eif (storeProps.sequences !== undefined) {
        this.msaStore = createMSAStore(storeProps);
      } else {
        console.warn("Check your MSA properties", storeProps);
      }
    }
 
    componentDidMount() {
      Iif (this.props.position !== undefined) {
        this.updatePosition(this.props.position);
      }
    }
 
    // Notify the internal Redux store about property updates
    componentDidUpdate(oldProps) {
      const newProps = this.props;
      // TODO: support batch updates
      for (const prop in pick(newProps, attributesToStore)) {
        if (!isEqual(oldProps[prop], newProps[prop])) {
          Iif (prop === "position") {
            this.updatePosition(newProps[prop]);
          } else Eif (prop in reduxActions) {
            let action;
            switch(reduxActions[prop]){
              case 'updateProp':
                action = mainStoreActions[reduxActions[prop]](prop, newProps[prop]);
                break;
              default:
                action = mainStoreActions[reduxActions[prop]](newProps[prop]);
            }
            //console.log("Prop -> Redux: ", action, newProps[prop]);
            this.msaStore.dispatch(action);
          } else {
            console.error(prop, " is unknown.");
          }
        }
      }
    }
 
    /**
     * Dispatch actions into the MSAViewer component.
     *
     * @param {Object} Action to be be dispatched. Must contain "type" and "payload"
     */
    dispatch(action) {
      if (action.type in mainStoreActionKeys) {
        this.msaStore.dispatch(action);
      } else if (action.type in positionStoreActionKeys) {
        this.el.current.positionStore.dispatch(action);
      } else {
        throw new Error("Invalid action", action);
      }
    }
 
    render() {
      const {msaStore, ...props} = omit(this.props, attributesToStore);
      Iif (this.msaStore === undefined) {
        return (<div> Error initializing the MSAViewer. </div>)
      } else {
        return (
          <WrappedComponent ref={this.el} msaStore={msaStore || this.msaStore} {...props} />
        );
      }
    }
  }
  // add action from the main store directly to the main MSA instance
  forOwn(mainStoreActions, (v, k) => {
    PropsToReduxComponent.prototype[k] = function(payload){
      this.msaStore.dispatch(v(payload));
    };
  });
  // add action from the position store directly to the main MSA instance
  forOwn(positionStoreActions, (v, k) => {
    PropsToReduxComponent.prototype[k] = function(payload){
      requestAnimation(this, () => {
        this.el.current.positionStore.dispatch(v(payload));
      });
    }
  });
  return PropsToReduxComponent;
}
 
export default PropsToRedux;