<commit_before>"use strict";
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import ModalComponent from '../../libs/bootstrap/modal';
import { FormMixin, Input } from '../../libs/bootstrap/form';
import { RestMixin } from '../rest';
import { external_url_map } from '../globals';

var curator = require('../curator');
var _ = require('underscore');

/**
 * Modal dialog box for adding disease (either by ID or free text)
 */
const DiseaseModal = module.exports.DiseaseModal = React.createClass({
    mixins: [FormMixin, RestMixin],

    propTypes: {
        addDiseaseModalBtn: PropTypes.object.isRequired,
        diseaseId: PropTypes.string,
        diseaseTerm: PropTypes.string,
        diseaseOntology: PropTypes.string,
        diseaseDescription: PropTypes.string,
        diseaseFreeTextConfirm: PropTypes.bool,
        phenotypes: PropTypes.array,
        synonyms: PropTypes.array,
        passDataToParent: PropTypes.func, // function to call upon pressing the Save button
        addDiseaseModalBtnLayoutClass: PropTypes.string
    },

    getInitialState() {
        return {
            diseaseId: this.props.diseaseId, // Value for disease id input field (types of 'text' or 'textarea')
            diseaseTerm: this.props.diseaseTerm, // Name of the disease
            diseaseOntology: this.props.diseaseOntology, // Orphanet, DOID, OMIM, NCIt, etc.
            diseaseDescription: this.props.diseaseDescription, // Description/definition of the disease
            phenotypes: this.props.phenotypes, // HPO IDs
            synonyms: this.props.synonyms, // Disease synonyms
            diseaseFreeTextConfirm: this.props.diseaseFreeTextConfirm, // User confirmation of entering free text for disease
            hasFreeTextDiseaseDescription: false, // True if disease description for free text is present
            hasFreeTextDiseasePhenotypes: false, // True if phenotypes for free text is present
            queryResourceDisabled: true, // Flag to disable the get OLS data button
            queryResourceBusy: false, // Flag to indicate the input button's 'busy' state
            resourceFetched: false, // Flag to indicate that a response from the resource has been obtained
            tempResource: {}, // Temporary object to hold the resource response
            submitResourceDisabled: true, // Flag to disable the modal save button
            submitResourceBusy: false // Flag to indicate that the modal's submit button is in a 'busy' state (creating local db entry)
        };
    },

    componentDidMount() {
        if (this.state.diseaseId && this.refs['diseaseId']) {
            this.refs['diseaseId'].setValue(this.state.diseaseId);
            this.setState({submitResourceDisabled: false});
        }
        if (this.state.diseaseTerm && this.refs['diseaseTerm']) {
            this.refs['diseaseTerm'].setValue(this.state.diseaseTerm);
            this.setState({submitResourceDisabled: false});
        }
        if (this.state.diseaseTerm && this.refs['diseaseId']) {
            this.renderResourceResult();
        }
        if (this.state.diseaseFreeTextConfirm) {
            // Set boolean state on 'required' prop for free text disease description
            if (this.state.diseaseDescription && this.state.diseaseDescription.length) {
                this.setState({hasFreeTextDiseaseDescription: true});
            } else {
                this.setState({hasFreeTextDiseaseDescription: false});
            }
            // Set boolean state on 'required' prop for free text disease phenotypes
            if (this.state.phenotypes && this.state.phenotypes.length) {
                this.setState({hasFreeTextDiseasePhenotypes: true});
            } else {
                this.setState({hasFreeTextDiseasePhenotypes: false});
            }
        }
    },

    componentWillReceiveProps(nextProps)  {
        if (nextProps.diseaseId) {
            this.setState({
                diseaseId: nextProps.diseaseId, 
                submitResourceDisabled: false,
                queryResourceDisabled: false
            });
        }
        if (nextProps.diseaseTerm) {
            this.setState({
                diseaseTerm: nextProps.diseaseTerm,
                submitResourceDisabled: false,
                queryResourceDisabled: false
            });
        }
        if (nextProps.diseaseOntology) {
            this.setState({diseaseOntology: nextProps.diseaseOntology});
        }
        if (nextProps.diseaseDescription) {
            this.setState({diseaseDescription: nextProps.diseaseDescription, hasFreeTextDiseaseDescription: true});
        }
        if (nextProps.diseaseFreeTextConfirm) {
            this.setState({diseaseFreeTextConfirm: nextProps.diseaseFreeTextConfirm});
        }
        if (nextProps.phenotypes) {
            this.setState({phenotypes: nextProps.phenotypes}, () => {
                if (this.state.phenotypes.length) {
                    this.setState({hasFreeTextDiseasePhenotypes: true});
                }
            });
        }
        if (nextProps.synonyms) {
            this.setState({synonyms: nextProps.synonyms});
        }
    },

    // Called when the modal form's cancel button is clicked. Just closes the modal like
    // nothing happened.
    cancelForm: function(e) {
        // Changed modal cancel button from a form input to a html button
        // as to avoid accepting enter/return key as a click event.
        // Removed hack in this method.
        this.handleModalClose('cancel');
    },

    /************************************************************************************************/
    /* Resetting the formErrors for selected input and other states was not needed previously       */
    /* because the previous MixIn implementation allowed the actuator (button to show the modal)    */
    /* to be defined outside of this component and closing the modal would delete this component    */
    /* from virtual DOM, along with the states.                                                     */
    /* The updated/converted implementation (without MixIn) wraps the actuator in the modal         */
    /* component and thus this component always exists in the virtual DOM as long as the actuator   */
    /* needs to be rendered in the UI. As a result, closing the modal does not remove the component */
    /* and the modified states are retained.                                                        */
    /* The MixIn function this.props.closeModal() has been replaced by this.child.closeModal(),     */
    /* which is way to call a function defined in the child component from the parent component.    */
    /* The reference example is at: https://jsfiddle.net/frenzzy/z9c46qtv/                          */
    /************************************************************************************************/
    handleModalClose(trigger) {
        let errors = this.state.formErrors;
        errors['diseaseId'] = '';
        errors['diseaseTerm'] = '';
        if (!this.state.submitResourceBusy) {
            if (trigger && trigger === 'cancel') {
                this.setState({
                    formErrors: errors,
                    diseaseId: this.props.diseaseId,
                    diseaseTerm: this.props.diseaseTerm,
                    diseaseFreeTextConfirm: this.props.diseaseFreeTextConfirm,
                    queryResourceDisabled: this.state.diseaseId ? false : true,
                    resourceFetched: false,
                    tempResource: {}
                });
            }
            this.child.closeModal();
        }
    },

    // Called when the value in the disease id input field is changed
    handleDiseaseIdChange(e) {<commit_msg>:racehorse: Improve states handling when closing the disease modal<commit_after>"use strict";
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import ModalComponent from '../../libs/bootstrap/modal';
import { FormMixin, Input } from '../../libs/bootstrap/form';
import { RestMixin } from '../rest';
import { external_url_map } from '../globals';

var curator = require('../curator');
var _ = require('underscore');

/**
 * Modal dialog box for adding disease (either by ID or free text)
 */
const DiseaseModal = module.exports.DiseaseModal = React.createClass({
    mixins: [FormMixin, RestMixin],

    propTypes: {
        addDiseaseModalBtn: PropTypes.object.isRequired,
        diseaseId: PropTypes.string,
        diseaseTerm: PropTypes.string,
        diseaseOntology: PropTypes.string,
        diseaseDescription: PropTypes.string,
        diseaseFreeTextConfirm: PropTypes.bool,
        phenotypes: PropTypes.array,
        synonyms: PropTypes.array,
        passDataToParent: PropTypes.func, // function to call upon pressing the Save button
        addDiseaseModalBtnLayoutClass: PropTypes.string
    },

    getInitialState() {
        return {
            diseaseId: this.props.diseaseId, // Value for disease id input field (types of 'text' or 'textarea')
            diseaseTerm: this.props.diseaseTerm, // Name of the disease
            diseaseOntology: this.props.diseaseOntology, // Orphanet, DOID, OMIM, NCIt, etc.
            diseaseDescription: this.props.diseaseDescription, // Description/definition of the disease
            phenotypes: this.props.phenotypes, // HPO IDs
            synonyms: this.props.synonyms, // Disease synonyms
            diseaseFreeTextConfirm: this.props.diseaseFreeTextConfirm, // User confirmation of entering free text for disease
            hasFreeTextDiseaseDescription: false, // True if disease description for free text is present
            hasFreeTextDiseasePhenotypes: false, // True if phenotypes for free text is present
            queryResourceDisabled: true, // Flag to disable the get OLS data button
            queryResourceBusy: false, // Flag to indicate the input button's 'busy' state
            resourceFetched: false, // Flag to indicate that a response from the resource has been obtained
            tempResource: {}, // Temporary object to hold the resource response
            submitResourceDisabled: true, // Flag to disable the modal save button
            submitResourceBusy: false // Flag to indicate that the modal's submit button is in a 'busy' state (creating local db entry)
        };
    },

    componentDidMount() {
        if (this.state.diseaseId && this.refs['diseaseId']) {
            this.refs['diseaseId'].setValue(this.state.diseaseId);
            this.setState({submitResourceDisabled: false});
        }
        if (this.state.diseaseTerm && this.refs['diseaseTerm']) {
            this.refs['diseaseTerm'].setValue(this.state.diseaseTerm);
            this.setState({submitResourceDisabled: false});
        }
        if (this.state.diseaseTerm && this.refs['diseaseId']) {
            this.renderResourceResult();
        }
        if (this.state.diseaseFreeTextConfirm) {
            // Set boolean state on 'required' prop for free text disease description
            if (this.state.diseaseDescription && this.state.diseaseDescription.length) {
                this.setState({hasFreeTextDiseaseDescription: true});
            } else {
                this.setState({hasFreeTextDiseaseDescription: false});
            }
            // Set boolean state on 'required' prop for free text disease phenotypes
            if (this.state.phenotypes && this.state.phenotypes.length) {
                this.setState({hasFreeTextDiseasePhenotypes: true});
            } else {
                this.setState({hasFreeTextDiseasePhenotypes: false});
            }
        }
    },

    componentWillReceiveProps(nextProps)  {
        if (nextProps.diseaseId) {
            this.setState({
                diseaseId: nextProps.diseaseId, 
                submitResourceDisabled: false,
                queryResourceDisabled: false
            });
        }
        if (nextProps.diseaseTerm) {
            this.setState({
                diseaseTerm: nextProps.diseaseTerm,
                submitResourceDisabled: false,
                queryResourceDisabled: false
            });
        }
        if (nextProps.diseaseOntology) {
            this.setState({diseaseOntology: nextProps.diseaseOntology});
        }
        if (nextProps.diseaseDescription) {
            this.setState({diseaseDescription: nextProps.diseaseDescription, hasFreeTextDiseaseDescription: true});
        }
        if (nextProps.diseaseFreeTextConfirm) {
            this.setState({diseaseFreeTextConfirm: nextProps.diseaseFreeTextConfirm});
        }
        if (nextProps.phenotypes) {
            this.setState({phenotypes: nextProps.phenotypes}, () => {
                if (this.state.phenotypes.length) {
                    this.setState({hasFreeTextDiseasePhenotypes: true});
                }
            });
        }
        if (nextProps.synonyms) {
            this.setState({synonyms: nextProps.synonyms});
        }
    },

    // Called when the modal form's cancel button is clicked. Just closes the modal like
    // nothing happened.
    cancelForm: function(e) {
        // Changed modal cancel button from a form input to a html button
        // as to avoid accepting enter/return key as a click event.
        // Removed hack in this method.
        this.handleModalClose('cancel');
    },

    /************************************************************************************************/
    /* Resetting the formErrors for selected input and other states was not needed previously       */
    /* because the previous MixIn implementation allowed the actuator (button to show the modal)    */
    /* to be defined outside of this component and closing the modal would delete this component    */
    /* from virtual DOM, along with the states.                                                     */
    /* The updated/converted implementation (without MixIn) wraps the actuator in the modal         */
    /* component and thus this component always exists in the virtual DOM as long as the actuator   */
    /* needs to be rendered in the UI. As a result, closing the modal does not remove the component */
    /* and the modified states are retained.                                                        */
    /* The MixIn function this.props.closeModal() has been replaced by this.child.closeModal(),     */
    /* which is way to call a function defined in the child component from the parent component.    */
    /* The reference example is at: https://jsfiddle.net/frenzzy/z9c46qtv/                          */
    /************************************************************************************************/
    handleModalClose(trigger) {
        let errors = this.state.formErrors;
        errors['diseaseId'] = '';
        errors['diseaseTerm'] = '';
        if (!this.state.submitResourceBusy) {
            if (trigger && trigger === 'cancel') {
                this.setState({
                    formErrors: errors,
                    diseaseId: this.props.diseaseId,
                    diseaseTerm: this.props.diseaseTerm,
                    diseaseDescription: this.props.diseaseDescription,
                    diseaseOntology: this.props.diseaseOntology,
                    phenotypes: this.props.phenotypes,
                    synonyms: this.props.synonyms,
                    diseaseFreeTextConfirm: this.props.diseaseFreeTextConfirm,
                    queryResourceDisabled: this.state.diseaseId ? false : true,
                    resourceFetched: false,
                    tempResource: {}
                });
            }
            this.child.closeModal();
        }
    },

    // Called when the value in the disease id input field is changed
    handleDiseaseIdChange(e) {
{'fraction_non_alphanumeric': 0.11369216241737488, 'fraction_numerical': 0.00037771482530689327, 'mean_word_length': 2.2008865605480556, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3514099', 'n_tokens_mistral': 4049, 'n_tokens_neox': 3844, 'n_words': 1112}