18F/cg-dashboard

View on GitHub
static_src/components/create_service_instance.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
/**
 * Renders the form to create a service instance
 */
import PropTypes from "prop-types";
import React from "react";
import ReactDOM from "react-dom";
import Action from "./action.jsx";
import { Form, FormText, FormSelect, FormElement, FormError } from "./form";
import FormStore from "../stores/form_store";
import Loading from "./loading.jsx";
import OrgStore from "../stores/org_store.js";
import SpaceStore from "../stores/space_store.js";
import ServiceInstanceStore from "../stores/service_instance_store.js";
import serviceActions from "../actions/service_actions.js";
import formActions from "../actions/form_actions";
import { validateString } from "../util/validators";

const CREATE_SERVICE_INSTANCE_FORM_GUID = "create-service-form";

// Note:
// This is a temporary hardcoded solution to resolve the need
// for multiple parameters when setting up service instances.
const CF_CLI_SERVICE_DETAILS = {
  "cdn-route": "https://cloud.gov/docs/services/cdn-route/",
  "cloud-gov-identity-provider":
    "https://cloud.gov/docs/services/cloud-gov-identity-provider/",
  "cloud-gov-service-account":
    "https://cloud.gov/docs/services/cloud-gov-service-account/"
};

const propTypes = {
  error: PropTypes.object,
  service: PropTypes.object,
  servicePlan: PropTypes.object.isRequired
};

const defaultProps = {
  service: {}
};

function stateSetter() {
  return {
    createLoading: ServiceInstanceStore.createLoading,
    createdTempNotification: ServiceInstanceStore.createdTempNotification,
    spaces: SpaceStore.getAll()
  };
}

export default class CreateServiceInstance extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      errs: [],
      spaces: SpaceStore.getAll()
    };

    this.validateString = validateString().bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this._onCancelForm = this._onCancelForm.bind(this);
  }

  componentDidMount() {
    FormStore.create(CREATE_SERVICE_INSTANCE_FORM_GUID);
    SpaceStore.addChangeListener(this.handleChange);
    ServiceInstanceStore.addChangeListener(this.handleChange);
    this.scrollIntoView();
  }

  componentWillUnmount() {
    SpaceStore.removeChangeListener(this.handleChange);
    ServiceInstanceStore.removeChangeListener(this.handleChange);
  }

  scrollIntoView() {
    ReactDOM.findDOMNode(this).scrollIntoView(true);
  }

  handleChange() {
    this.setState(stateSetter());
    this.scrollIntoView();
  }

  handleSubmit(errs, values) {
    this.setState({ errs }, () => {
      const { name, space } = values;
      const instanceName = (name && name.value) || null;
      const spaceName = (space && space.value) || null;

      serviceActions.createInstance(
        instanceName,
        spaceName,
        this.props.servicePlan.guid
      );
    });
  }

  _onCancelForm(ev) {
    ev.preventDefault();
    serviceActions.createInstanceFormCancel();
  }

  get formContent() {
    const serviceName = this.serviceName;
    if (CF_CLI_SERVICE_DETAILS.hasOwnProperty(serviceName)) {
      return (
        <Form
          guid={CREATE_SERVICE_INSTANCE_FORM_GUID}
          classes={["test-create_service_instance_form"]}
          onSubmit={this.handleSubmit}
        >
          <legend>
            The
            <strong className="actions-callout-inline-block">
              {serviceName}
            </strong>{" "}
            service instance must be created using the CF CLI. Please refer to{" "}
            <a href={CF_CLI_SERVICE_DETAILS[serviceName]} target="_blank">
              {CF_CLI_SERVICE_DETAILS[serviceName]}
            </a>{" "}
            for more information.
          </legend>
        </Form>
      );
    } else {
      return (
        <Form
          guid={CREATE_SERVICE_INSTANCE_FORM_GUID}
          classes={["test-create_service_instance_form"]}
          onSubmit={this.handleSubmit}
        >
          <legend>
            Create a service instance for
            <strong className="actions-callout-inline-block">
              {this.serviceName}
            </strong>{" "}
            using
            <strong className="actions-callout-inline-block">
              {this.servicePlanName}
            </strong>{" "}
            plan.
          </legend>
          <FormText
            formGuid={CREATE_SERVICE_INSTANCE_FORM_GUID}
            classes={["test-create_service_instance_name"]}
            label="Choose a name for the service instance"
            name="name"
            validator={this.validateString}
          />
          <FormSelect
            formGuid={CREATE_SERVICE_INSTANCE_FORM_GUID}
            classes={["test-create_service_instance_space"]}
            label="Select the space for the service instance"
            name="space"
            options={this.validSpaceTargets}
            validator={this.validateString}
          />
          {this.contextualAction}
          <Action
            label="cancel"
            style="base"
            type="outline"
            clickHandler={this._onCancelForm}
          >
            Cancel
          </Action>
        </Form>
      );
    }
  }

  get serviceName() {
    return this.props.service.label || "Unknown Service Name";
  }

  get servicePlanName() {
    return this.props.servicePlan.name || "Unknown Service Plan Name";
  }

  get validSpaceTargets() {
    const currentOrgGuid = OrgStore.currentOrgGuid;
    const { spaces } = this.state;

    return spaces
      .filter(space => {
        return space.org === currentOrgGuid;
      })
      .map(space => {
        return { value: space.guid, label: space.name };
      });
  }

  get contextualAction() {
    const { createLoading, createdTempNotification } = this.state;

    let createAction = (
      <Action label="submit" type="submit">
        Create service instance
      </Action>
    );

    if (createLoading) {
      createAction = <Loading style="inline" />;
    } else if (createdTempNotification) {
      createAction = (
        <span className="status status-ok">
          Created! To bind the service instance to an app, go to an application
          page and use the services panel.
        </span>
      );
    }

    return createAction;
  }

  render() {
    let createError;

    if (this.props.error) {
      createError = <FormError message={this.props.error.description} />;
    }

    return (
      <div className="actions-large">
        {createError}
        {this.formContent}
      </div>
    );
  }
}

CreateServiceInstance.propTypes = propTypes;
CreateServiceInstance.defaultProps = defaultProps;