<commit_before>define(function (require) {

  var React = require('react'),
      moment = require('moment'),
      stores = require('stores'),
      actions = require('actions'),
      BootstrapModalMixin = require('components/mixins/BootstrapModalMixin.react'),
      VersionName = require('../instance/image/components/VersionName.react'),
      VersionChanges = require('../instance/image/components/VersionChangeLog.react'),
      EditAvailabilityView = require('./availability/EditAvailabilityView.react'),
      EditDescriptionView = require('components/images/detail/description/EditDescriptionView.react'),
      EditMembershipView = require('./membership/EditMembershipView.react'),
      EditLicensesView = require('./licenses/EditLicensesView.react'),
      EditScriptsView = require('./scripts/EditScriptsView.react'),
      ImageSelect = require('components/modals/image_version/ImageSelect.react');

  return React.createClass({
    displayName: "ImageVersionEditModal",

    mixins: [BootstrapModalMixin],

    propTypes: {
      version: React.PropTypes.object.isRequired,
      image: React.PropTypes.instanceOf(Backbone.Model).isRequired
    },
    getInitialState: function () {
      var version = this.props.version;

      return {
        showOptions: false,
        version: version,
        versionImageID: this.props.image.id,
        versionName: version.get('name'),
        versionChangeLog: (version.get('change_log') == null) ? "" : version.get('change_log'),
        versionStartDate: (version.get('start_date') == null) ? "" : version.get('start_date'),
        versionEndDate: (version.get('end_date') == null) ? "" : version.get('end_date'),
        versionCanImage: version.get('allow_imaging'),
        versionParentID: version.get('parent').id,
        versionLicenses: null,
        versionScripts: null,
        versionMemberships: null,
      }
    },

    updateState: function () {
      if (this.isMounted()) this.setState(this.getState());
    },

    getState: function() {
      return this.state;
    },

    componentDidMount: function () {
      stores.ImageStore.addChangeListener(this.updateState);
      stores.UserStore.addChangeListener(this.updateState);
      stores.MembershipStore.addChangeListener(this.updateState);
      stores.ScriptStore.addChangeListener(this.updateState);
      stores.LicenseStore.addChangeListener(this.updateState);
      stores.ImageVersionStore.addChangeListener(this.updateState);
      stores.ImageVersionMembershipStore.addChangeListener(this.updateState);
      stores.ImageVersionScriptStore.addChangeListener(this.updateState);
      stores.ImageVersionLicenseStore.addChangeListener(this.updateState);
      stores.ProviderMachineStore.addChangeListener(this.updateState);
    },

    componentWillUnmount: function () {
      stores.ImageStore.removeChangeListener(this.updateState);
      stores.UserStore.removeChangeListener(this.updateState);
      stores.MembershipStore.removeChangeListener(this.updateState);
      stores.LicenseStore.removeChangeListener(this.updateState);
      stores.ScriptStore.removeChangeListener(this.updateState);
      stores.ImageVersionStore.removeChangeListener(this.updateState);
      stores.ImageVersionMembershipStore.removeChangeListener(this.updateState);
      stores.ImageVersionLicenseStore.removeChangeListener(this.updateState);
      stores.ImageVersionScriptStore.removeChangeListener(this.updateState);
      stores.ProviderMachineStore.removeChangeListener(this.updateState);
    },

    //TODO: Pull this out to commons
    valid_date: function (date_stamp) {
      if (date_stamp === "") return true;
      var the_date = Date.parse(date_stamp);
      return !isNaN(the_date);
    },

    isSubmittable: function(){
      var hasVersionName   = !!this.state.versionName;
      var hasChangeLog = !!this.state.versionChangeLog;
      var validEndDate = !!this.valid_date(this.state.versionEndDate);
      return hasVersionName && hasChangeLog && validEndDate;
    },

    //
    // Internal Modal Callbacks
    // ------------------------
    //

    cancel: function(){
      this.hide();
    },

    confirm: function () {
      this.hide();
      this.props.onConfirm(
        this.props.version,
        this.state.versionName,
        this.state.versionChangeLog,
        this.state.versionEndDate,
        this.state.versionCanImage,
        this.state.versionImageID
      );
    },

    //
    // Custom Modal Callbacks
    // ----------------------
    //

    // todo: I don't think there's a reason to update state unless
    // there's a risk of the component being re-rendered by the parent.
    // Should probably verify this behavior, but for now, we play it safe.
    onVersionChange: function (e) {
      this.setState({versionName: e.target.value});
    },

    onEndDateChange: function (e) {
      this.setState({versionEndDate: e.target.value});
    },
    setEndDateNow: function (e) {
      var now_time = moment(new Date());
      this.setState({versionEndDate: now_time.format("MMM D, YYYY hh:mm a")});
    },

    unsetEndDateNow: function (e) {
      this.setState({versionEndDate: ""});
    },

    onUncopyableSelected: function (e) {
      var uncopyable = (e.target.checked);
      this.setState({versionCanImage: uncopyable});
    },

    onImageSelected: function (selection) {
      this.setState({versionImageID: selection});
    },

    onMembershipChanged: function (membership_list) {
      this.setState({versionMembership: membership_list});
    },

    //
    //
    // Render
    // ------
    //

    handleNameChange: function(name){
      this.setState({versionName: name});
    },
    handleDescriptionChange: function(description){
      this.setState({versionChangeLog: description});
    },
    onOptionsChange: function() {
      this.setState({showOptions: !this.state.showOptions});
    },
    onScriptCreate: function(scriptObj){
      actions.ScriptActions.create_AddToImageVersion(this.props.version, {
        title: scriptObj.title,
        type: scriptObj.type,
        text: scriptObj.text
      });
    },

    onScriptAdded: function(script){
      actions.ImageVersionScriptActions.add({
        image_version: this.props.version,
        script: script
      });
    },

    onScriptRemoved: function(script){
      actions.ImageVersionScriptActions.remove({
        image_version: this.props.version,
        script: script
      });
    },

    onLicenseCreate: function(licenseObj){
      actions.LicenseActions.create_AddToImageVersion(this.props.version, {
        title: licenseObj.title,
        type: licenseObj.type,
        text: licenseObj.text
      });
    },

    onLicenseAdded: function(license){
      actions.ImageVersionLicenseActions.add({
        image_version: this.props.version,
        license: license
      });
    },

    onLicenseRemoved: function(license){
      actions.ImageVersionLicenseActions.remove({
        image_version: this.props.version,
        license: license
      });
    },

    onMembershipAdded: function(membership){
      actions.ImageVersionMembershipActions.add({
        image_version: this.props.version,
        group: membership
      });
    },

    onMembershipRemoved: function(membership){
      actions.ImageVersionMembershipActions.remove({
        image_version: this.props.version,
        group: membership
      });
    },
    renderBody: function() {
      var applicationView, availabilityView, canImageView, nameView, descriptionView, startDateView, endDateView, membershipView, licensesView, scriptsView;

      var name = this.state.versionName,
        created = this.state.versionStartDate.format("MMM D, YYYY hh:mm a"),
        ended,
        advancedOptions,
        optionsButtonText = (this.state.showOptions) ? "Hide Options" : "Show Options",
        membershipsList = stores.MembershipStore.getAll(),
        licensesList = stores.LicenseStore.getAll(),
        activeLicensesList = stores.ImageVersionLicenseStore.getLicensesFor(this.props.version),
        scriptsList = stores.ScriptStore.getAll(),
        activeScriptsList = stores.ImageVersionScriptStore.getScriptsFor(this.props.version),
        versionMembers = stores.ImageVersionMembershipStore.getMembershipsFor(this.props.version);

      if(this.state.versionEndDate
        && this.state.versionEndDate._isAMomentObject
        && this.state.versionEndDate.isValid()) {
        ended = this.state.versionEndDate.format("MMM D, YYYY hh:mm a");
      } else {
        ended = this.state.versionEndDate;
      }

      if(!name) {
          return (
);
      }
      licensesView = (
        <EditLicensesView
          activeLicenses={activeLicensesList}
          licenses={licensesList}
          onLicenseAdded={this.onLicenseAdded}
          onLicenseRemoved={this.onLicenseRemoved}
          onCreateNewLicense={this.onLicenseCreate}
          label={"Licenses Required"}
          />
      );
      scriptsView = (
        <EditScriptsView
          activeScripts={activeScriptsList}
          scripts={scriptsList}
          onScriptAdded={this.onScriptAdded}
          onScriptRemoved={this.onScriptRemoved}
          onCreateNewScript={this.onScriptCreate}
          label={"Scripts Required"}
          />
      );
      nameView = (
        <VersionName
               update={true}
               value={this.state.versionName}
               onChange={this.handleNameChange}
            />
      );
      descriptionView = (
        <VersionChanges
          value={this.state.versionChangeLog}
          onChange={this.handleDescriptionChange}
          />
      );
      availabilityView = (<EditAvailabilityView
        image={this.props.image}
        version={this.props.version}
        />);
      if(this.props.image.get('is_public')) {
        membershipView = (

          Here lies a pretty view telling users
          they can add/edit/remove users they shared a
          specific version with.. ONLY IF that image is private

)
      } else {
        membershipView = (<EditMembershipView
          memberships={membershipsList}
          activeMemberships={versionMembers}
          onMembershipAdded={this.onMembershipAdded}
          onMembershipRemoved={this.onMembershipRemoved}
          label={"Version Shared With:"}
          />);
      }
      applicationView = (

        <ImageSelect
          imageId={this.state.versionImageID}
          onChange={this.onImageSelected}
          />

      );
      //FUTURE_keyTODO: Pull this functionality out if you use it anywhere else..
      endDateView = (

        Version Removed On

          <input type='text' className='form-control' value={ended} onChange={this.onEndDateChange}/>
          Set
          Clear

);
      startDateView = (

          Version Created On
          <input type='text' className='form-control' value={created} readOnly={true} editable={false}/>

      );
      canImageView = (

            <input type='checkbox' className='form-control'
                   checked={this.state.versionCanImage}
                   onChange={this.onUncopyableSelected}/>

      );
      if(this.state.showOptions) {
        advancedOptions = (

            {availabilityView}

            {membershipView}

            {licensesView}

            {scriptsView}

            {applicationView}

        );
      } else {
        advancedOptions = null;
      }

      return (

          {nameView}

          {descriptionView}

          {
            //TODO: implement 'allow Imaging' in the next iteration
            //canImageView
          }
          {startDateView}
          {endDateView}
          <button type="button" className="btn btn-primary"
                  onClick={this.onOptionsChange}>
            {optionsButtonText}
          </button>

          {advancedOptions}

      );
    },

    renderMember: function(member) {
      return (
{member}
);
    },

    renderAddMember: function() {
      return (
"Add New Membership"
);
    },

    render: function () {
      var images = stores.ImageStore.getAll(),
        providers = stores.ProviderStore.getAll();

      var version = this.props.version,
        end_date = version.get('end_date'),
        versionId = version.id;

      if (!end_date) {
      end_date =""
      }

      return (

                {this.renderCloseButton()}

Edit Image Version

                {this.renderBody()}

                <button type="button" className="btn btn-primary" onClick={this.confirm} disabled={!this.isSubmittable()}>
                  Save Changes
                </button>

      );
    }

  });

});
<commit_msg>Changed 'options' to 'Advanced Options'<commit_after>define(function (require) {

  var React = require('react'),
      moment = require('moment'),
      stores = require('stores'),
      actions = require('actions'),
      BootstrapModalMixin = require('components/mixins/BootstrapModalMixin.react'),
      VersionName = require('../instance/image/components/VersionName.react'),
      VersionChanges = require('../instance/image/components/VersionChangeLog.react'),
      EditAvailabilityView = require('./availability/EditAvailabilityView.react'),
      EditDescriptionView = require('components/images/detail/description/EditDescriptionView.react'),
      EditMembershipView = require('./membership/EditMembershipView.react'),
      EditLicensesView = require('./licenses/EditLicensesView.react'),
      EditScriptsView = require('./scripts/EditScriptsView.react'),
      ImageSelect = require('components/modals/image_version/ImageSelect.react');

  return React.createClass({
    displayName: "ImageVersionEditModal",

    mixins: [BootstrapModalMixin],

    propTypes: {
      version: React.PropTypes.object.isRequired,
      image: React.PropTypes.instanceOf(Backbone.Model).isRequired
    },
    getInitialState: function () {
      var version = this.props.version;

      return {
        showOptions: false,
        version: version,
        versionImageID: this.props.image.id,
        versionName: version.get('name'),
        versionChangeLog: (version.get('change_log') == null) ? "" : version.get('change_log'),
        versionStartDate: (version.get('start_date') == null) ? "" : version.get('start_date'),
        versionEndDate: (version.get('end_date') == null) ? "" : version.get('end_date'),
        versionCanImage: version.get('allow_imaging'),
        versionParentID: version.get('parent').id,
        versionLicenses: null,
        versionScripts: null,
        versionMemberships: null,
      }
    },

    updateState: function () {
      if (this.isMounted()) this.setState(this.getState());
    },

    getState: function() {
      return this.state;
    },

    componentDidMount: function () {
      stores.ImageStore.addChangeListener(this.updateState);
      stores.UserStore.addChangeListener(this.updateState);
      stores.MembershipStore.addChangeListener(this.updateState);
      stores.ScriptStore.addChangeListener(this.updateState);
      stores.LicenseStore.addChangeListener(this.updateState);
      stores.ImageVersionStore.addChangeListener(this.updateState);
      stores.ImageVersionMembershipStore.addChangeListener(this.updateState);
      stores.ImageVersionScriptStore.addChangeListener(this.updateState);
      stores.ImageVersionLicenseStore.addChangeListener(this.updateState);
      stores.ProviderMachineStore.addChangeListener(this.updateState);
    },

    componentWillUnmount: function () {
      stores.ImageStore.removeChangeListener(this.updateState);
      stores.UserStore.removeChangeListener(this.updateState);
      stores.MembershipStore.removeChangeListener(this.updateState);
      stores.LicenseStore.removeChangeListener(this.updateState);
      stores.ScriptStore.removeChangeListener(this.updateState);
      stores.ImageVersionStore.removeChangeListener(this.updateState);
      stores.ImageVersionMembershipStore.removeChangeListener(this.updateState);
      stores.ImageVersionLicenseStore.removeChangeListener(this.updateState);
      stores.ImageVersionScriptStore.removeChangeListener(this.updateState);
      stores.ProviderMachineStore.removeChangeListener(this.updateState);
    },

    //TODO: Pull this out to commons
    valid_date: function (date_stamp) {
      if (date_stamp === "") return true;
      var the_date = Date.parse(date_stamp);
      return !isNaN(the_date);
    },

    isSubmittable: function(){
      var hasVersionName   = !!this.state.versionName;
      var hasChangeLog = !!this.state.versionChangeLog;
      var validEndDate = !!this.valid_date(this.state.versionEndDate);
      return hasVersionName && hasChangeLog && validEndDate;
    },

    //
    // Internal Modal Callbacks
    // ------------------------
    //

    cancel: function(){
      this.hide();
    },

    confirm: function () {
      this.hide();
      this.props.onConfirm(
        this.props.version,
        this.state.versionName,
        this.state.versionChangeLog,
        this.state.versionEndDate,
        this.state.versionCanImage,
        this.state.versionImageID
      );
    },

    //
    // Custom Modal Callbacks
    // ----------------------
    //

    // todo: I don't think there's a reason to update state unless
    // there's a risk of the component being re-rendered by the parent.
    // Should probably verify this behavior, but for now, we play it safe.
    onVersionChange: function (e) {
      this.setState({versionName: e.target.value});
    },

    onEndDateChange: function (e) {
      this.setState({versionEndDate: e.target.value});
    },
    setEndDateNow: function (e) {
      var now_time = moment(new Date());
      this.setState({versionEndDate: now_time.format("MMM D, YYYY hh:mm a")});
    },

    unsetEndDateNow: function (e) {
      this.setState({versionEndDate: ""});
    },

    onUncopyableSelected: function (e) {
      var uncopyable = (e.target.checked);
      this.setState({versionCanImage: uncopyable});
    },

    onImageSelected: function (selection) {
      this.setState({versionImageID: selection});
    },

    onMembershipChanged: function (membership_list) {
      this.setState({versionMembership: membership_list});
    },

    //
    //
    // Render
    // ------
    //

    handleNameChange: function(name){
      this.setState({versionName: name});
    },
    handleDescriptionChange: function(description){
      this.setState({versionChangeLog: description});
    },
    onOptionsChange: function() {
      this.setState({showOptions: !this.state.showOptions});
    },
    onScriptCreate: function(scriptObj){
      actions.ScriptActions.create_AddToImageVersion(this.props.version, {
        title: scriptObj.title,
        type: scriptObj.type,
        text: scriptObj.text
      });
    },

    onScriptAdded: function(script){
      actions.ImageVersionScriptActions.add({
        image_version: this.props.version,
        script: script
      });
    },

    onScriptRemoved: function(script){
      actions.ImageVersionScriptActions.remove({
        image_version: this.props.version,
        script: script
      });
    },

    onLicenseCreate: function(licenseObj){
      actions.LicenseActions.create_AddToImageVersion(this.props.version, {
        title: licenseObj.title,
        type: licenseObj.type,
        text: licenseObj.text
      });
    },

    onLicenseAdded: function(license){
      actions.ImageVersionLicenseActions.add({
        image_version: this.props.version,
        license: license
      });
    },

    onLicenseRemoved: function(license){
      actions.ImageVersionLicenseActions.remove({
        image_version: this.props.version,
        license: license
      });
    },

    onMembershipAdded: function(membership){
      actions.ImageVersionMembershipActions.add({
        image_version: this.props.version,
        group: membership
      });
    },

    onMembershipRemoved: function(membership){
      actions.ImageVersionMembershipActions.remove({
        image_version: this.props.version,
        group: membership
      });
    },
    renderBody: function() {
      var applicationView, availabilityView, canImageView, nameView, descriptionView, startDateView, endDateView, membershipView, licensesView, scriptsView;

      var name = this.state.versionName,
        created = this.state.versionStartDate.format("MMM D, YYYY hh:mm a"),
        ended,
        advancedOptions,
        optionsButtonText = (this.state.showOptions) ? "Hide Options" : "Advanced Options",
        membershipsList = stores.MembershipStore.getAll(),
        licensesList = stores.LicenseStore.getAll(),
        activeLicensesList = stores.ImageVersionLicenseStore.getLicensesFor(this.props.version),
        scriptsList = stores.ScriptStore.getAll(),
        activeScriptsList = stores.ImageVersionScriptStore.getScriptsFor(this.props.version),
        versionMembers = stores.ImageVersionMembershipStore.getMembershipsFor(this.props.version);

      if(this.state.versionEndDate
        && this.state.versionEndDate._isAMomentObject
        && this.state.versionEndDate.isValid()) {
        ended = this.state.versionEndDate.format("MMM D, YYYY hh:mm a");
      } else {
        ended = this.state.versionEndDate;
      }

      if(!name) {
          return (
);
      }
      licensesView = (
        <EditLicensesView
          activeLicenses={activeLicensesList}
          licenses={licensesList}
          onLicenseAdded={this.onLicenseAdded}
          onLicenseRemoved={this.onLicenseRemoved}
          onCreateNewLicense={this.onLicenseCreate}
          label={"Licenses Required"}
          />
      );
      scriptsView = (
        <EditScriptsView
          activeScripts={activeScriptsList}
          scripts={scriptsList}
          onScriptAdded={this.onScriptAdded}
          onScriptRemoved={this.onScriptRemoved}
          onCreateNewScript={this.onScriptCreate}
          label={"Scripts Required"}
          />
      );
      nameView = (
        <VersionName
               update={true}
               value={this.state.versionName}
               onChange={this.handleNameChange}
            />
      );
      descriptionView = (
        <VersionChanges
          value={this.state.versionChangeLog}
          onChange={this.handleDescriptionChange}
          />
      );
      availabilityView = (<EditAvailabilityView
        image={this.props.image}
        version={this.props.version}
        />);
      if(this.props.image.get('is_public')) {
        membershipView = (

          Here lies a pretty view telling users
          they can add/edit/remove users they shared a
          specific version with.. ONLY IF that image is private

)
      } else {
        membershipView = (<EditMembershipView
          memberships={membershipsList}
          activeMemberships={versionMembers}
          onMembershipAdded={this.onMembershipAdded}
          onMembershipRemoved={this.onMembershipRemoved}
          label={"Version Shared With:"}
          />);
      }
      applicationView = (

        <ImageSelect
          imageId={this.state.versionImageID}
          onChange={this.onImageSelected}
          />

      );
      //FUTURE_keyTODO: Pull this functionality out if you use it anywhere else..
      endDateView = (

        Version Removed On

          <input type='text' className='form-control' value={ended} onChange={this.onEndDateChange}/>
          Set
          Clear

);
      startDateView = (

          Version Created On
          <input type='text' className='form-control' value={created} readOnly={true} editable={false}/>

      );
      canImageView = (

            <input type='checkbox' className='form-control'
                   checked={this.state.versionCanImage}
                   onChange={this.onUncopyableSelected}/>

      );
      if(this.state.showOptions) {
        advancedOptions = (

            {availabilityView}

            {membershipView}

            {licensesView}

            {scriptsView}

            {applicationView}

        );
      } else {
        advancedOptions = null;
      }

      return (

          {nameView}

          {descriptionView}

          {
            //TODO: implement 'allow Imaging' in the next iteration
            //canImageView
          }
          {startDateView}
          {endDateView}
          <button type="button" className="btn btn-primary pull-right"
                  onClick={this.onOptionsChange}>
            {optionsButtonText}
          </button>

          {advancedOptions}

      );
    },

    renderMember: function(member) {
      return (
{member}
);
    },

    renderAddMember: function() {
      return (
"Add New Membership"
);
    },

    render: function () {
      var images = stores.ImageStore.getAll(),
        providers = stores.ProviderStore.getAll();

      var version = this.props.version,
        end_date = version.get('end_date'),
        versionId = version.id;

      if (!end_date) {
      end_date =""
      }

      return (

                {this.renderCloseButton()}

Edit Image Version

                {this.renderBody()}

                <button type="button" className="btn btn-primary" onClick={this.confirm} disabled={!this.isSubmittable()}>
                  Save Changes
                </button>

      );
    }

  });

});
<|endoftext|>


<commit_before>/* global d3 */
/* eslint no-unused-vars: 0 */
function tfrD3Vis( data, tfrId ) {
  var stratify = d3.stratify()
    .id( function ( d ) { return ( d.name == null ? null : ( d.name + "_" + d.rank ) ); } )
    .parentId( function ( d ) { return ( d.parent_name == null ? null : ( d.parent_name + "_" + d.parent_rank ) ); } );

  function formatName( d ) {
    if ( d.id == null ) {
      return null;
    }
    var rankSplit = d.id.split( "_" );
    var rank = rankSplit[1];
    var splitString = rankSplit[0].split( " " );
    var firstWord = splitString.shift();
    if ( d.data.rank === "species" ) {
      splitString.unshift( firstWord[0] + "." );
      splitString = splitString.join( " " );
    } else if ( d.data.rank === "subspecies" ) {
      var secondWord = splitString.shift();
      splitString.unshift( secondWord[0] + "." );
      splitString.unshift( firstWord[0] + "." );
      splitString = splitString.join( " " );
    } else {
      splitString = rankSplit[0];
    }
    return splitString;
  }

  function truncate( d, cutoff ) {
    if ( d == null ) {
      return null;
    }
    var returnValue = d;
    if ( d.length > cutoff ) {
      returnValue = d.substring( 0, ( cutoff - 3 ) ) + "...";
    }
    return returnValue;
  }

  data.internal_taxa.sort( function ( a, b ) {
    return ( a.name > b.name ) ? 1 : -1;
  } );
  var rootInternal = stratify( data.internal_taxa );
  var sortOrder = rootInternal.descendants().map( function ( d ) { return d.data; } );

  var dataExternalTaxa = [];
  var dataInternalTaxaNames = sortOrder.map( function ( j ) { return j.name; } );
  var dataExternalTaxaNames = data.external_taxa.map( function ( j ) { return j.name; } );

  data.external_taxa.forEach( function ( et ) {
    var newEt = et;
    var match = dataInternalTaxaNames.indexOf( et.name );
    if ( match ) {
      newEt.ind = match;
    } else {
      newEt.ind = dataExternalTaxaNames.indexOf( et.name );
    }
    dataExternalTaxa.push( et );
  } );

  dataExternalTaxa.sort( function ( a, b ) {
    var a1 = a.ind;
    var b1 = b.ind;
    if ( a1 === b1 ) return 0;
    return a1 > b1 ? 1 : -1;
  } );

  var rootExternal = stratify( dataExternalTaxa ).sort( function ( a, b ) {
    var a1 = a.data.ind;
    var b1 = b.data.ind;
    if ( a1 === b1 ) return 0;
    return a1 > b1 ? 1 : -1;
  } );
  var numRows = Math.max( rootExternal.descendants().length, rootInternal.descendants().length );

  // set up the svg
  var margin = {
    top: 1, right: 1, bottom: 1, left: 1
  };
  var width = 800 - margin.left - margin.right;
  var offset1 = 100;
  var offset2 = ( width / 2 + 75 );
  var height = Math.max( ( numRows * 12 ), 200 ) - margin.top - margin.bottom;

  var svg = d3.select( "div.tfr_" + tfrId ).append( "svg" )
    .attr( "viewBox", "0 0 " + ( width + margin.left + margin.right ) + " " + ( height + margin.top + margin.bottom ) )
    .attr( "preserveAspectRatio", "xMinYMin meet" );

  var tree = d3.cluster()
    .size( [height, width / 4.5] );

  // get the trees
  tree( rootExternal );
  tree( rootInternal );

  // groups for the two background trees
  var g = svg.append( "g" ).attr( "transform", "translate(" + offset1 + ",0)" );
  var g2 = svg.append( "g" ).attr( "transform", "translate(" + offset2 + ")" );

  g.selectAll( ".link" )
    .data( rootInternal.descendants().slice( 1 ) )
    .enter().append( "path" )
    .attr( "class", "link" )
    .attr( "d", function ( d ) {
      return "M" + d.y + "," + d.x + "C" + ( d.parent.y + 50 ) + "," + d.x + " " + ( d.parent.y + 50 ) + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
    } );

  var trianglePoints = ( width / 2 - 35 ) + " " + ( height / 2 + 15 ) + ", " + ( width / 2 - 15 ) + " " + ( height / 2 + 10 ) + ", " + ( width / 2 - 35 ) + " " + ( height / 2 + 5 );

  svg.append( "polyline" )
    .attr( "points", trianglePoints );

  g2.selectAll( ".link" )
    .data( rootExternal.descendants().slice( 1 ) )
    .enter().append( "path" )
    .attr( "class", "link" )
    .attr( "d", function ( d ) {
      return "M" + d.y + "," + d.x + "C" + ( d.parent.y + 50 ) + "," + d.x + " " + ( d.parent.y + 50 ) + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
    } );

  var node = g.selectAll( ".node" )
    .data( rootInternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  node.append( "circle" )
    .style( "fill", function ( d ) {
      return d.parent == null ? "gray" : "#76AC1E";
    } )
    .attr( "r", 4 )
    .on( "mouseover", function ( d ) {
      if ( d.data.url != null ) {
        d3.select( this ).style( "cursor", "pointer" );
      }
    } )
    .on( "mouseout", function () {
      d3.select( this ).style( "cursor", "default" );
    } )
    .on( "click", function ( d ) {
      if ( d.data.url != null ) {
        var url = "/taxa/" + d.data.url;
        window.location = url;
      }
    } );

  var node2 = g2.selectAll( ".node" )
    .data( rootExternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  node2.append( "circle" )
    .style( "fill", function ( d ) {
      return d.parent == null ? "gray" : "#76AC1E";
    } )
    .attr( "r", 4 )
    .on( "mouseover", function ( d ) {
      if ( d.data.url != null ) {
        d3.select( this ).style( "cursor", "pointer" );
      }
    } )
    .on( "mouseout", function () {
      d3.select( this ).style( "cursor", "default" );
    } )
    .on( "click", function ( d ) {
      if ( d.data.url != null ) {
        window.location = d.data.url;
      }
    } );

  // group for the labels
  var g1 = svg.append( "g" ).attr( "transform", "translate(" + offset1 + ",0)" );
  var g21 = svg.append( "g" ).attr( "transform", "translate(" + offset2 + ")" );

  var lnode = g1.selectAll( ".node" )
    .data( rootInternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  lnode.append( "text" )
    .attr( "dy", 3 )
    .attr( "x", function ( d ) { return d.children ? -8 : 8; } )
    .style( "text-anchor", function ( d ) { return d.children ? "end" : "start"; } )
    .text( function ( d ) { return truncate( formatName( d ), 17 ); } );

  var lnode2 = g21.selectAll( ".node" )
    .data( rootExternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  lnode2.append( "text" )
    .attr( "dy", 3 )
    .attr( "x", function ( d ) { return d.children ? -8 : 8; } )
    .style( "text-anchor", function ( d ) { return d.children ? "end" : "start"; } )
    .text( function ( d ) { return truncate( formatName( d ), 17 ); } );
}
<commit_msg>tree fig improvement for var.<commit_after>/* global d3 */
/* eslint no-unused-vars: 0 */
function tfrD3Vis( data, tfrId ) {
  var stratify = d3.stratify()
    .id( function ( d ) { return ( d.name == null ? null : ( d.name + "_" + d.rank ) ); } )
    .parentId( function ( d ) { return ( d.parent_name == null ? null : ( d.parent_name + "_" + d.parent_rank ) ); } );

  function formatName( d ) {
    if ( d.id == null ) {
      return null;
    }
    var rankSplit = d.id.split( "_" );
    var rank = rankSplit[1];
    var splitString = rankSplit[0].split( " " );
    var firstWord = splitString.shift();
    if ( d.data.rank === "species" ) {
      splitString.unshift( firstWord[0] + "." );
      splitString = splitString.join( " " );
    } else if ( d.data.rank === "subspecies" || d.data.rank === "variety" ) {
      var secondWord = splitString.shift();
      splitString.unshift( secondWord[0] + "." );
      splitString.unshift( firstWord[0] + "." );
      splitString = splitString.join( " " );
    } else {
      splitString = rankSplit[0];
    }
    return splitString;
  }

  function truncate( d, cutoff ) {
    if ( d == null ) {
      return null;
    }
    var returnValue = d;
    if ( d.length > cutoff ) {
      returnValue = d.substring( 0, ( cutoff - 3 ) ) + "...";
    }
    return returnValue;
  }

  data.internal_taxa.sort( function ( a, b ) {
    return ( a.name > b.name ) ? 1 : -1;
  } );
  var rootInternal = stratify( data.internal_taxa );
  var sortOrder = rootInternal.descendants().map( function ( d ) { return d.data; } );

  var dataExternalTaxa = [];
  var dataInternalTaxaNames = sortOrder.map( function ( j ) { return j.name; } );
  var dataExternalTaxaNames = data.external_taxa.map( function ( j ) { return j.name; } );

  data.external_taxa.forEach( function ( et ) {
    var newEt = et;
    var match = dataInternalTaxaNames.indexOf( et.name );
    if ( match ) {
      newEt.ind = match;
    } else {
      newEt.ind = dataExternalTaxaNames.indexOf( et.name );
    }
    dataExternalTaxa.push( et );
  } );

  dataExternalTaxa.sort( function ( a, b ) {
    var a1 = a.ind;
    var b1 = b.ind;
    if ( a1 === b1 ) return 0;
    return a1 > b1 ? 1 : -1;
  } );

  var rootExternal = stratify( dataExternalTaxa ).sort( function ( a, b ) {
    var a1 = a.data.ind;
    var b1 = b.data.ind;
    if ( a1 === b1 ) return 0;
    return a1 > b1 ? 1 : -1;
  } );
  var numRows = Math.max( rootExternal.descendants().length, rootInternal.descendants().length );

  // set up the svg
  var margin = {
    top: 1, right: 1, bottom: 1, left: 1
  };
  var width = 800 - margin.left - margin.right;
  var offset1 = 100;
  var offset2 = ( width / 2 + 75 );
  var height = Math.max( ( numRows * 12 ), 200 ) - margin.top - margin.bottom;

  var svg = d3.select( "div.tfr_" + tfrId ).append( "svg" )
    .attr( "viewBox", "0 0 " + ( width + margin.left + margin.right ) + " " + ( height + margin.top + margin.bottom ) )
    .attr( "preserveAspectRatio", "xMinYMin meet" );

  var tree = d3.cluster()
    .size( [height, width / 4.5] );

  // get the trees
  tree( rootExternal );
  tree( rootInternal );

  // groups for the two background trees
  var g = svg.append( "g" ).attr( "transform", "translate(" + offset1 + ",0)" );
  var g2 = svg.append( "g" ).attr( "transform", "translate(" + offset2 + ")" );

  g.selectAll( ".link" )
    .data( rootInternal.descendants().slice( 1 ) )
    .enter().append( "path" )
    .attr( "class", "link" )
    .attr( "d", function ( d ) {
      return "M" + d.y + "," + d.x + "C" + ( d.parent.y + 50 ) + "," + d.x + " " + ( d.parent.y + 50 ) + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
    } );

  var trianglePoints = ( width / 2 - 35 ) + " " + ( height / 2 + 15 ) + ", " + ( width / 2 - 15 ) + " " + ( height / 2 + 10 ) + ", " + ( width / 2 - 35 ) + " " + ( height / 2 + 5 );

  svg.append( "polyline" )
    .attr( "points", trianglePoints );

  g2.selectAll( ".link" )
    .data( rootExternal.descendants().slice( 1 ) )
    .enter().append( "path" )
    .attr( "class", "link" )
    .attr( "d", function ( d ) {
      return "M" + d.y + "," + d.x + "C" + ( d.parent.y + 50 ) + "," + d.x + " " + ( d.parent.y + 50 ) + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
    } );

  var node = g.selectAll( ".node" )
    .data( rootInternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  node.append( "circle" )
    .style( "fill", function ( d ) {
      return d.parent == null ? "gray" : "#76AC1E";
    } )
    .attr( "r", 4 )
    .on( "mouseover", function ( d ) {
      if ( d.data.url != null ) {
        d3.select( this ).style( "cursor", "pointer" );
      }
    } )
    .on( "mouseout", function () {
      d3.select( this ).style( "cursor", "default" );
    } )
    .on( "click", function ( d ) {
      if ( d.data.url != null ) {
        var url = "/taxa/" + d.data.url;
        window.location = url;
      }
    } );

  var node2 = g2.selectAll( ".node" )
    .data( rootExternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  node2.append( "circle" )
    .style( "fill", function ( d ) {
      return d.parent == null ? "gray" : "#76AC1E";
    } )
    .attr( "r", 4 )
    .on( "mouseover", function ( d ) {
      if ( d.data.url != null ) {
        d3.select( this ).style( "cursor", "pointer" );
      }
    } )
    .on( "mouseout", function () {
      d3.select( this ).style( "cursor", "default" );
    } )
    .on( "click", function ( d ) {
      if ( d.data.url != null ) {
        window.location = d.data.url;
      }
    } );

  // group for the labels
  var g1 = svg.append( "g" ).attr( "transform", "translate(" + offset1 + ",0)" );
  var g21 = svg.append( "g" ).attr( "transform", "translate(" + offset2 + ")" );

  var lnode = g1.selectAll( ".node" )
    .data( rootInternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  lnode.append( "text" )
    .attr( "dy", 3 )
    .attr( "x", function ( d ) { return d.children ? -8 : 8; } )
    .style( "text-anchor", function ( d ) { return d.children ? "end" : "start"; } )
    .text( function ( d ) { return truncate( formatName( d ), 17 ); } );

  var lnode2 = g21.selectAll( ".node" )
    .data( rootExternal.descendants() )
    .enter().append( "g" )
    .attr( "class", function ( d ) { return "node" + ( d.children ? " node--internal" : " node--leaf" ); } )
    .attr( "transform", function ( d ) { return "translate(" + d.y + "," + d.x + ")"; } );

  lnode2.append( "text" )
    .attr( "dy", 3 )
    .attr( "x", function ( d ) { return d.children ? -8 : 8; } )
    .style( "text-anchor", function ( d ) { return d.children ? "end" : "start"; } )
    .text( function ( d ) { return truncate( formatName( d ), 17 ); } );
}
<|endoftext|>


<commit_before>/*
  react学习(一)
  个人理解,React组件分为两部分,即编写组件和使用组件
  编写组件
  var TestPlugin(组件名,首字母需要大写)=React.createClass({
    render:function(){
      return (

                Hello World!

      );//一些html标签
    }
  });
  传入一个对象,属性render返回组件的UI界面组成(html部分)

  使用组件
  ReactDOM.render(<TestPlugin />,document.getElementById("id"));
  第一个参数为编写的组件,第二个为组件要放在页面上的位置
*/
  var TestPlugin=React.createClass({
    render:function(){
      return (

                Hello World!

      );
    }
  });
  ReactDOM.render(<TestPlugin />,document.getElementById("container"));

/*
  react学习(二)
  组件的PropTypes属性
  编写组件时保证组件被正确使用变得非常有用,React的PropTypes属性可以对组件的属性值进行限定
  propTypes的常用值
  React.PropTypes.array|React.PropTypes.array.isRequired
  React.PropTypes.bool|React.PropTypes.bool.isRequired
  React.PropTypes.func|React.PropTypes.func.isRequired
  React.PropTypes.number|React.PropTypes.number.isRequired
  React.PropTypes.object|React.PropTypes.object.isRequired
  React.PropTypes.string|React.PropTypes.string.isRequired
  this.props代表组件的属性
*/
  var MyTitle=React.createClass({
    propTypes:{//此处propTypes为小写小写小写!!!
      title:React.PropTypes.string.isRequired,
    },
    render:function(){
      return (

Hello,{this.props.title}!

      );
    }
  });
  ReactDOM.render(<MyTitle title="NBA" />,document.body);
<commit_msg>Update exam.js<commit_after>/*
  react学习(一)
  个人理解,React组件分为两部分,即编写组件和使用组件
  编写组件
  var TestPlugin(组件名,首字母需要大写)=React.createClass({
    render:function(){
      return (

                Hello World!

      );//一些html标签
    }
  });
  传入一个对象,属性render返回组件的UI界面组成(html部分)

  使用组件
  ReactDOM.render(<TestPlugin />,document.getElementById("id"));
  第一个参数为编写的组件,第二个为组件要放在页面上的位置
*/
  var TestPlugin=React.createClass({
    render:function(){
      return (

                Hello World!

      );
    }
  });
  ReactDOM.render(<TestPlugin />,document.getElementById("container"));

/*
  react学习(二)
  组件的PropTypes属性
  编写组件时保证组件被正确使用变得非常有用,React的PropTypes属性可以对组件的属性值进行限定
  propTypes的常用值
  React.PropTypes.array|React.PropTypes.array.isRequired
  React.PropTypes.bool|React.PropTypes.bool.isRequired
  React.PropTypes.func|React.PropTypes.func.isRequired
  React.PropTypes.number|React.PropTypes.number.isRequired
  React.PropTypes.object|React.PropTypes.object.isRequired
  React.PropTypes.string|React.PropTypes.string.isRequired
  this.props代表组件的属性
*/
  var MyTitle=React.createClass({
    propTypes:{//此处propTypes为小写小写小写!!!
      title:React.PropTypes.string.isRequired,
    },
    render:function(){
      return (

Hello,{this.props.title}!

      );
    }
  });
  ReactDOM.render(<MyTitle title="NBA" />,document.body);
/*
  react学习(三)
  getDefaultProps来设置组件属性的默认值
*/
  var SearchPlugin=React.createClass({
    getDefaultProps:function(){
      return {
        value:"iphone 7"
      };
    },
    render:function(){
      return (

            <input type="text" value={this.props.value} />
            <button>搜索</button>

        );
    }
  });
  ReactDOM.render(<SearchPlugin />,document.getElementById("container"));
/*
  react学习(四)
  获取真实的DOM节点
  组件不是真实的DOM节点，而是存在于内存之中的一种数据结构，叫做虚拟DOM(virtual DOM).
  只有把它插入文档以后才会变成真实的DOM.所有的DOM变得都是先在虚拟DOM上发生,然后通过DOM diff算法反应到真实的DOM上
  但是若果需要获取真实的DOM节点,需要用到ref属性.
  有关于props和state的关系,可以看看这篇blog:
  http://blog.csdn.net/code_for_free/article/details/50762171
*/
  var SearchInput=React.createClass({
    getInitialState:function(){
      return {
        val:"iphone 7"
      };
    },
    handleFocus:function(){
      var inputnode=ReactDOM.findDOMNode(this.refs.sin);//网上说this.refs.sin就可以取到DOM节点，我试了不行，后来发现可能是版本的原因
      inputnode.style.borderColor="#2E8B57";
      this.setState({
        val:""
      });
    },
    handleBlur:function(){
      var inputnode=ReactDOM.findDOMNode(this.refs.sin);
      inputnode.style.borderColor="#DCDCDC";
      this.setState({
        val:"iphone 7"
      });
    },
    render:function(){
      return (

            <input type="text" ref="sin" value={this.state.val} onFocus={this.handleFocus} onBlur={this.handleBlur} />
            <button>搜索</button>

        );
    }
  });
  ReactDOM.render(<SearchInput />,document.getElementById("container"));

<|endoftext|>


<commit_before>/*
 * Copyright 2017 Banco Bilbao Vizcaya Argentaria, S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * OperationsController
 *
 */
var OperationsController = (function(dashboardId) {

  var observable = new Event('OperationsController');
  var service = Service.get(Service.types.userMetrics, dashboardId);
  var config;

  function getMetrics(response) {
    var model;

    if(response) {
      response = JSON.parse(response);
      model = {};
      if(response.length && response.length > 0) {

        var metricsMap = {};

        response.forEach(function(metric) {
          if(metricsMap[metric.name] === undefined) {
            metricsMap[metric.name] = {
              oneDayValue: 0,
              oneDaySampleSize: 0,
              sevenDaysValue: 0,
              sevenDaysSampleSize: 0,
              tendency: undefined
            };
          }

          if(metric.oneDayValue) {
            metricsMap[metric.name].oneDayValue += metric.oneDayValue;
            metricsMap[metric.name].oneDaySampleSize += metric.oneDaySampleSize;
          }
          if(metric.sevenDaysValue) {
            metricsMap[metric.name].sevenDaysValue += metric.sevenDaysValue;
            metricsMap[metric.name].sevenDaysSampleSize += metric.sevenDaysSampleSize;
          }
        });

        for (var metric in metricsMap) {
          var tendencyChange = Utils.getPercentageDifference(metricsMap[metric].sevenDaysValue / metricsMap[metric].sevenDaysSampleSize || 0, metricsMap[metric].oneDayValue / metricsMap[metric].oneDaySampleSize || 0);
          if(metric === 'errorsNumber' || metric === 'responseTime' || metric === 'infrastructureCost' ) {
            metricsMap[metric].tendency = tendencyChange < -10 ? 'threedown-green' : tendencyChange < -5 ? 'twodown-green' : tendencyChange < -1 ? 'onedown-green' : tendencyChange > 10 ? 'threeup-red' : tendencyChange > 5 ? 'twoup-red' : tendencyChange > 1 ? 'oneup-red' : 'eq';
          } else {
            metricsMap[metric].tendency = tendencyChange < -10 ? 'threedown' : tendencyChange < -5 ? 'twodown' : tendencyChange < -1 ? 'onedown' : tendencyChange > 10 ? 'threeup' : tendencyChange > 5 ? 'twoup' : tendencyChange > 1 ? 'oneup' : 'eq';
          }
        }

        model.metrics = {
          requestsNumber: metricsMap.requestsNumber && metricsMap.requestsNumber.oneDayValue,
          requestsNumberTendency: metricsMap.requestsNumber && metricsMap.requestsNumber.tendency,
          errorsRate: metricsMap.errorsNumber ? parseFloat((100 * metricsMap.errorsNumber.oneDaySampleSize / metricsMap.errorsNumber.oneDayValue).toFixed(2)) : 0,
          errorsRateTendency: metricsMap.errorsNumber && metricsMap.errorsNumber.tendency,
          availabilityRate: metricsMap.availabilityRate && parseFloat((metricsMap.availabilityRate.oneDayValue / metricsMap.availabilityRate.oneDaySampleSize).toFixed(2)),
          availabilityRateTendency: metricsMap.availabilityRate && metricsMap.availabilityRate.tendency,
          responseTime: metricsMap.responseTime && parseFloat((metricsMap.responseTime.oneDayValue / metricsMap.responseTime.oneDaySampleSize).toFixed(2)),
          responseTimeTendency: metricsMap.responseTime && metricsMap.responseTime.tendency,
          infraCost: metricsMap.infrastructureCost && parseFloat(metricsMap.infrastructureCost.oneDayValue.toFixed(2)),
          infraCostTendency: metricsMap.infrastructureCost && metricsMap.infrastructureCost.tendency
        };

        model.responseTimeAlertingLevels = {
          warning: config.responseTimeAlertingLevelWarning,
          error: config.responseTimeAlertingLevelError
        };

        model.errorsRateAlertingLevels = {
          warning: config.errorsRateAlertingLevelWarning,
          error: config.errorsRateAlertingLevelError
        };
      }
    }

    observable.notify(model);
  }

  this.observable = observable;
  this.dispose = function() {
    this.observable.reset();
    service.removeListener(getMetrics);
  };
  this.init = function(_config) {
    config = _config;
    // TO-DO remove analyticViews check
    if(!((config.analyticViews && config.analyticViews.length) || (config.operationViews && config.operationViews.length))) {
      return Promise.reject();
    }
    service.addListener(getMetrics);
  };

});
<commit_msg>fix(operations): display 0 metrics values properly<commit_after>/*
 * Copyright 2017 Banco Bilbao Vizcaya Argentaria, S.A.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * OperationsController
 *
 */
var OperationsController = (function(dashboardId) {

  var observable = new Event('OperationsController');
  var service = Service.get(Service.types.userMetrics, dashboardId);
  var config;

  function getMetrics(response) {
    var model;

    if(response) {
      response = JSON.parse(response);
      model = {};
      if(response.length && response.length > 0) {

        var metricsMap = {};

        response.forEach(function(metric) {
          if(metricsMap[metric.name] === undefined) {
            metricsMap[metric.name] = {
              oneDayValue: 0,
              oneDaySampleSize: 0,
              sevenDaysValue: 0,
              sevenDaysSampleSize: 0,
              tendency: undefined
            };
          }

          if(metric.oneDayValue) {
            metricsMap[metric.name].oneDayValue += metric.oneDayValue;
            metricsMap[metric.name].oneDaySampleSize += metric.oneDaySampleSize;
          }
          if(metric.sevenDaysValue) {
            metricsMap[metric.name].sevenDaysValue += metric.sevenDaysValue;
            metricsMap[metric.name].sevenDaysSampleSize += metric.sevenDaysSampleSize;
          }
        });

        for (var metric in metricsMap) {
          var tendencyChange = Utils.getPercentageDifference(metricsMap[metric].sevenDaysValue / metricsMap[metric].sevenDaysSampleSize || 0, metricsMap[metric].oneDayValue / metricsMap[metric].oneDaySampleSize || 0);
          if(metric === 'errorsNumber' || metric === 'responseTime' || metric === 'infrastructureCost' ) {
            metricsMap[metric].tendency = tendencyChange < -10 ? 'threedown-green' : tendencyChange < -5 ? 'twodown-green' : tendencyChange < -1 ? 'onedown-green' : tendencyChange > 10 ? 'threeup-red' : tendencyChange > 5 ? 'twoup-red' : tendencyChange > 1 ? 'oneup-red' : 'eq';
          } else {
            metricsMap[metric].tendency = tendencyChange < -10 ? 'threedown' : tendencyChange < -5 ? 'twodown' : tendencyChange < -1 ? 'onedown' : tendencyChange > 10 ? 'threeup' : tendencyChange > 5 ? 'twoup' : tendencyChange > 1 ? 'oneup' : 'eq';
          }
        }

        model.metrics = {
          requestsNumber: metricsMap.requestsNumber && metricsMap.requestsNumber.oneDayValue,
          requestsNumberTendency: metricsMap.requestsNumber && metricsMap.requestsNumber.tendency,
          errorsRate: metricsMap.errorsNumber && metricsMap.errorsNumber.oneDaySampleSize > 0 ? parseFloat((100 * metricsMap.errorsNumber.oneDayValue / metricsMap.errorsNumber.oneDaySampleSize || 0).toFixed(2)) : undefined,
          errorsRateTendency: metricsMap.errorsNumber && metricsMap.errorsNumber.oneDaySampleSize > 0 && metricsMap.errorsNumber.tendency,
          availabilityRate: metricsMap.availabilityRate && parseFloat((metricsMap.availabilityRate.oneDayValue / metricsMap.availabilityRate.oneDaySampleSize).toFixed(2)),
          availabilityRateTendency: metricsMap.availabilityRate && metricsMap.availabilityRate.tendency,
          responseTime: metricsMap.responseTime && metricsMap.responseTime.oneDaySampleSize > 0 ? parseFloat((metricsMap.responseTime.oneDayValue / metricsMap.responseTime.oneDaySampleSize).toFixed(2)) : undefined,
          responseTimeTendency: metricsMap.responseTime && metricsMap.responseTime.oneDaySampleSize > 0 ? metricsMap.responseTime.tendency : undefined,
          infraCost: metricsMap.infrastructureCost && parseFloat(metricsMap.infrastructureCost.oneDayValue.toFixed(2)),
          infraCostTendency: metricsMap.infrastructureCost && metricsMap.infrastructureCost.tendency
        };

        model.responseTimeAlertingLevels = {
          warning: config.responseTimeAlertingLevelWarning,
          error: config.responseTimeAlertingLevelError
        };

        model.errorsRateAlertingLevels = {
          warning: config.errorsRateAlertingLevelWarning,
          error: config.errorsRateAlertingLevelError
        };
      }
    }

    observable.notify(model);
  }

  this.observable = observable;
  this.dispose = function() {
    this.observable.reset();
    service.removeListener(getMetrics);
  };
  this.init = function(_config) {
    config = _config;
    // TO-DO remove analyticViews check
    if(!((config.analyticViews && config.analyticViews.length) || (config.operationViews && config.operationViews.length))) {
      return Promise.reject();
    }
    service.addListener(getMetrics);
  };

});
<|endoftext|>


<commit_before>import React from 'react';
import Highcharts from 'highcharts';
import addColorAxis from 'highcharts/modules/coloraxis';
import { HighchartsChart, withHighcharts } from '../../../src';
import Chart from '../../../src/components/Chart';
import ColorAxis from '../../../src/components/ColorAxis';
import Series from '../../../src/components/Series';
import YAxis from '../../../src/components/YAxis';
import XAxis from '../../../src/components/XAxis';
import Debug from '../../../src/components/Debug';

addColorAxis(Highcharts);

describe('<ColorAxis /> integration', () => {
  describe('when mounted', () => {
    it('renders series with correct coloraxis', () => {
      const data = [{ y: 1, colorValue: 0 }, { y: 10, colorValue: 10 }];
      const Component = props => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            <ColorAxis min={10} max={100} />
            <ColorAxis id="testcoloraxis" min={0} max={10}>
              <YAxis>
                <Series colorKey="colorValue" type="column" data={data} />
              </YAxis>
            </ColorAxis>
            <ColorAxis min={5} max={50} />
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      mount(<WithComponent />);
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis).toBeDefined();
      expect(colorAxis.options.min).toEqual(0);
      expect(colorAxis.options.max).toEqual(10);

      const series = chart.series[0];
      expect(series.colorAxis.options.id).toEqual('testcoloraxis');
    });
  });

  describe('when updated', () => {
    it('updates colorAxis options', () => {
      const data = [{ y: 1, colorValue: 0 }, { y: 10, colorValue: 10 }];
      const Component = ({ min, max }) => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            <ColorAxis id="testcoloraxis" min={min} max={max}>
              <YAxis>
                <Series colorKey="colorValue" type="column" data={data} />
              </YAxis>
            </ColorAxis>
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      const wrapper = mount(<WithComponent min={0} max={10} />);
      wrapper.setProps({ min: 1 });
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis.options.min).toEqual(1);
    });
  });

  describe('when unmounted', () => {
    it('removes colorAxis from chart', () => {
      const data = [{ y: 1, colorValue: 0 }, { y: 10, colorValue: 10 }];
      const Component = ({ mountColorAxis = true }) => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            {mountColorAxis ? (
              <ColorAxis id="testcoloraxis" min={0} max={10}></ColorAxis>
            ) : null}
            <YAxis>
              <Series
                colorAxisId="testcoloraxis"
                colorKey="colorValue"
                type="column"
                data={data}
              />
            </YAxis>
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      const wrapper = mount(<WithComponent min={0} max={10} />);
      wrapper.setProps({ mountColorAxis: false });
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis).not.toBeDefined();
    });
  });
});
<commit_msg>Add test for referencing coloraxis outside of context<commit_after>import React from 'react';
import Highcharts from 'highcharts';
import addColorAxis from 'highcharts/modules/coloraxis';
import { HighchartsChart, withHighcharts } from '../../../src';
import Chart from '../../../src/components/Chart';
import ColorAxis from '../../../src/components/ColorAxis';
import Series from '../../../src/components/Series';
import YAxis from '../../../src/components/YAxis';
import XAxis from '../../../src/components/XAxis';
import Debug from '../../../src/components/Debug';

addColorAxis(Highcharts);

describe('<ColorAxis /> integration', () => {
  describe('when mounted', () => {
    it('renders series with correct coloraxis', () => {
      const data = [
        { y: 1, colorValue: 0 },
        { y: 10, colorValue: 10 }
      ];
      const Component = props => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            <ColorAxis min={10} max={100} />
            <ColorAxis id="testcoloraxis" min={0} max={10}>
              <YAxis>
                <Series colorKey="colorValue" type="column" data={data} />
              </YAxis>
            </ColorAxis>
            <ColorAxis min={5} max={50} />
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      mount(<WithComponent />);
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis).toBeDefined();
      expect(colorAxis.options.min).toEqual(0);
      expect(colorAxis.options.max).toEqual(10);

      const series = chart.series[0];
      expect(series.colorAxis.options.id).toEqual('testcoloraxis');
    });

    it('renders series with correct coloraxis outside context', () => {
      const data = [
        { y: 1, colorValue: 0 },
        { y: 10, colorValue: 10 }
      ];
      const Component = props => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            <ColorAxis min={10} max={100} />
            <ColorAxis id="testcoloraxis" min={0} max={10} />
            <YAxis>
              <Series
                colorAxis="testcoloraxis"
                colorKey="colorValue"
                type="column"
                data={data}
              />
            </YAxis>
            <ColorAxis min={5} max={50} />
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      mount(<WithComponent />);
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis).toBeDefined();
      expect(colorAxis.options.min).toEqual(0);
      expect(colorAxis.options.max).toEqual(10);

      const series = chart.series[0];
      expect(series.colorAxis.options.id).toEqual('testcoloraxis');
    });
  });

  describe('when updated', () => {
    it('updates colorAxis options', () => {
      const data = [
        { y: 1, colorValue: 0 },
        { y: 10, colorValue: 10 }
      ];
      const Component = ({ min, max }) => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            <ColorAxis id="testcoloraxis" min={min} max={max}>
              <YAxis>
                <Series colorKey="colorValue" type="column" data={data} />
              </YAxis>
            </ColorAxis>
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      const wrapper = mount(<WithComponent min={0} max={10} />);
      wrapper.setProps({ min: 1 });
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis.options.min).toEqual(1);
    });
  });

  describe('when unmounted', () => {
    it('removes colorAxis from chart', () => {
      const data = [
        { y: 1, colorValue: 0 },
        { y: 10, colorValue: 10 }
      ];
      const Component = ({ mountColorAxis = true }) => {
        return (
          <HighchartsChart>
            <Debug />
            <Chart />
            <XAxis />
            {mountColorAxis ? (
              <ColorAxis id="testcoloraxis" min={0} max={10}></ColorAxis>
            ) : null}
            <YAxis>
              <Series
                colorAxisId="testcoloraxis"
                colorKey="colorValue"
                type="column"
                data={data}
              />
            </YAxis>
          </HighchartsChart>
        );
      };
      const WithComponent = withHighcharts(Component, Highcharts);
      const wrapper = mount(<WithComponent min={0} max={10} />);
      wrapper.setProps({ mountColorAxis: false });
      const chart = window.chart;
      const colorAxis = chart.colorAxis.find(
        c => c.options.id === 'testcoloraxis'
      );
      expect(colorAxis).not.toBeDefined();
    });
  });
});
<|endoftext|>


<commit_before>import { fromJS } from 'immutable';
import reducer from '../reducer';

describe('UPLOAD | containers | ModalStepper | reducer', () => {
  describe('default action', () => {
    it('should return the initialState', () => {
      const action = {
        type: 'TEST',
      };
      const initialState = fromJS({
        test: true,
      });

      expect(reducer(initialState, action)).toEqual(initialState);
    });
  });

  describe('ADD_FILES_TO_UPLOAD', () => {
    it('should add the files to the empty filesToUpload array and update the current step', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {
          0: { ok: true },
          1: { ok: false },
        },
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { ok: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });

    it('should add the files to the (not empty) filesToUpload array and update the current step', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {
          0: { test: true },
          1: { test: false },
        },
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });

    it('should work if the filesToUpload is empty', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {},
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('CLEAN_FILES_ERROR', () => {
    it('should not change the filesToUpload property if it is empty', () => {
      const action = {
        type: 'CLEAN_FILES_ERROR',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [],
      });

      expect(reducer(state, action)).toEqual(state);
    });

    it('should remove the errors of all files from the filesToUploadArray', () => {
      const action = {
        type: 'CLEAN_FILES_ERROR',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'error2',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('GO_TO', () => {
    it('should update the current step', () => {
      const action = {
        type: 'GO_TO',
        to: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
      });
      const expected = fromJS({
        currentStep: 'test',
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('ON_SUBMIT_EDIT_NEW_FILE', () => {
    it('should update the filesToUploadList with the fileToEdit data', () => {
      const action = {
        type: 'ON_SUBMIT_EDIT_NEW_FILE',
      };
      const state = fromJS({
        currentStep: 'edit-new',
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test',
            },
          },
          {
            originalIndex: 1,
            file: {
              test: false,
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
        fileToEdit: {
          originalIndex: 1,
          file: {
            test: true,
            otherTest: true,
          },
        },
      });
      const expected = fromJS({
        currentStep: 'edit-new',
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test',
            },
          },
          {
            originalIndex: 1,
            file: {
              test: true,
              otherTest: true,
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
        fileToEdit: null,
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('REMOVE_FILE_TO_UPLOAD', () => {
    it('should remove the file from the filesToUpload array', () => {
      const action = {
        type: 'REMOVE_FILE_TO_UPLOAD',
        fileIndex: 1,
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'error2',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },

          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('RESET_PROPS', () => {
    it('should return the initialState', () => {
      const action = { type: 'RESET_PROPS' };
      const state = { test: true };
      const expected = fromJS({
        currentStep: 'browse',
        filesToUpload: [],
        fileToEdit: null,
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_CROP_RESULT', () => {
    it('should update the fileToEditEntry with the passed data', () => {
      const action = {
        type: 'SET_CROP_RESULT',
        blob: {
          test: true,
        },
      };
      const state = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: null,
        },
      });
      const expected = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: {
            test: true,
          },
        },
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILE_ERROR', () => {
    it('should update the specified file error', () => {
      const action = {
        type: 'SET_FILE_ERROR',
        fileIndex: 1,
        errorMessage: 'size limit exceeded',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'size limit exceeded',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILE_TO_EDIT', () => {
    it('should set the fileToEdit key with the file at the passed index from the filesToUpload list', () => {
      const action = {
        type: 'SET_FILE_TO_EDIT',
        fileIndex: 1,
      };
      const state = fromJS({
        fileToEdit: null,
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test0',
            },
          },
          {
            originalIndex: 1,
            file: {
              name: 'test1',
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
      });
      const expected = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: {
            name: 'test1',
          },
        },
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test0',
            },
          },
          {
            originalIndex: 1,
            file: {
              name: 'test1',
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILES_UPLOADING_STATE', () => {
    it('should change all the isUploading keys of the filesToUpload to true', () => {
      const action = {
        type: 'SET_FILES_UPLOADING_STATE',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: 'test',
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });
});
<commit_msg>Add tests<commit_after>import { fromJS } from 'immutable';
import reducer from '../reducer';

describe('UPLOAD | containers | ModalStepper | reducer', () => {
  describe('default action', () => {
    it('should return the initialState', () => {
      const action = {
        type: 'TEST',
      };
      const initialState = fromJS({
        test: true,
      });

      expect(reducer(initialState, action)).toEqual(initialState);
    });
  });

  describe('ADD_FILES_TO_UPLOAD', () => {
    it('should add the files to the empty filesToUpload array and update the current step', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {
          0: { ok: true },
          1: { ok: false },
        },
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { ok: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });

    it('should add the files to the (not empty) filesToUpload array and update the current step', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {
          0: { test: true },
          1: { test: false },
        },
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });

    it('should work if the filesToUpload is empty', () => {
      const action = {
        type: 'ADD_FILES_TO_UPLOAD',
        filesToUpload: {},
        nextStep: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('CLEAN_FILES_ERROR', () => {
    it('should not change the filesToUpload property if it is empty', () => {
      const action = {
        type: 'CLEAN_FILES_ERROR',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [],
      });

      expect(reducer(state, action)).toEqual(state);
    });

    it('should remove the errors of all files from the filesToUploadArray', () => {
      const action = {
        type: 'CLEAN_FILES_ERROR',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'error2',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('GO_TO', () => {
    it('should update the current step', () => {
      const action = {
        type: 'GO_TO',
        to: 'test',
      };
      const state = fromJS({
        currentStep: 'browse',
      });
      const expected = fromJS({
        currentStep: 'test',
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('ON_SUBMIT_EDIT_NEW_FILE', () => {
    it('should update the filesToUploadList with the fileToEdit data', () => {
      const action = {
        type: 'ON_SUBMIT_EDIT_NEW_FILE',
      };
      const state = fromJS({
        currentStep: 'edit-new',
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test',
            },
          },
          {
            originalIndex: 1,
            file: {
              test: false,
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
        fileToEdit: {
          originalIndex: 1,
          file: {
            test: true,
            otherTest: true,
          },
        },
      });
      const expected = fromJS({
        currentStep: 'edit-new',
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test',
            },
          },
          {
            originalIndex: 1,
            file: {
              test: true,
              otherTest: true,
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
        fileToEdit: null,
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('REMOVE_FILE_TO_UPLOAD', () => {
    it('should remove the file from the filesToUpload array', () => {
      const action = {
        type: 'REMOVE_FILE_TO_UPLOAD',
        fileIndex: 1,
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'error2',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: true,
            errorMessage: 'error1',
            isUploading: false,
            originalIndex: 0,
          },

          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: true,
            errorMessage: 'error3',
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('RESET_FILE_TO_UPLOAD', () => {
    it('should set the fileToEdit key to null', () => {
      const action = {
        type: 'RESET_FILE_TO_EDIT',
      };
      const state = fromJS({
        fileToEdit: 'test',
      });
      const expected = fromJS({
        fileToEdit: null,
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('RESET_PROPS', () => {
    it('should return the initialState', () => {
      const action = { type: 'RESET_PROPS' };
      const state = { test: true };
      const expected = fromJS({
        currentStep: 'browse',
        filesToUpload: [],
        fileToEdit: null,
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_CROP_RESULT', () => {
    it('should update the fileToEditEntry with the passed data', () => {
      const action = {
        type: 'SET_CROP_RESULT',
        blob: {
          test: true,
        },
      };
      const state = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: null,
        },
      });
      const expected = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: {
            test: true,
          },
        },
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILE_ERROR', () => {
    it('should update the specified file error', () => {
      const action = {
        type: 'SET_FILE_ERROR',
        fileIndex: 1,
        errorMessage: 'size limit exceeded',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: true,
            errorMessage: 'size limit exceeded',
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILE_TO_EDIT', () => {
    it('should set the fileToEdit key with the file at the passed index from the filesToUpload list', () => {
      const action = {
        type: 'SET_FILE_TO_EDIT',
        fileIndex: 1,
      };
      const state = fromJS({
        fileToEdit: null,
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test0',
            },
          },
          {
            originalIndex: 1,
            file: {
              name: 'test1',
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
      });
      const expected = fromJS({
        fileToEdit: {
          originalIndex: 1,
          file: {
            name: 'test1',
          },
        },
        filesToUpload: [
          {
            originalIndex: 0,
            file: {
              name: 'test0',
            },
          },
          {
            originalIndex: 1,
            file: {
              name: 'test1',
            },
          },
          {
            originalIndex: 2,
            file: {
              name: 'test2',
            },
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });

  describe('SET_FILES_UPLOADING_STATE', () => {
    it('should change all the isUploading keys of the filesToUpload to true', () => {
      const action = {
        type: 'SET_FILES_UPLOADING_STATE',
      };
      const state = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: 'test',
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: false,
            originalIndex: 2,
          },
        ],
      });
      const expected = fromJS({
        currentStep: 'test',
        filesToUpload: [
          {
            abortController: new AbortController(),
            file: { ok: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 0,
          },
          {
            abortController: new AbortController(),
            file: { test: true },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 1,
          },
          {
            abortController: new AbortController(),
            file: { test: false },
            hasError: false,
            errorMessage: null,
            isUploading: true,
            originalIndex: 2,
          },
        ],
      });

      expect(reducer(state, action)).toEqual(expected);
    });
  });
});
<|endoftext|>