renderValue()

in public/js/components/FormFields/FormFieldArrayWrapper.js [34:140]


  renderValue(value, i) {

    const updateFn = (newValue) => {
      if (value === undefined) {
        //It's the first update to a new item - add it to props and remove from newItems
        this.setState({
          newItems: this.state.newItems.length > 1 ? this.state.newItems.slice(1) : []
        });

        const update = this.props.fieldValue ? this.props.fieldValue.concat([newValue]) : [newValue];
        this.props.onUpdateField(update);
      } else {
        //Find the value in the array to change
        const newFieldValue = this.props.fieldValue.map((oldValue) => {
          return value === oldValue ? newValue : oldValue;
        });

        this.props.onUpdateField(newFieldValue);
      }
    };

    const removeFn = (removeIndex) => {
      if (this.props.fieldValue && this.props.fieldValue.length > removeIndex) {
        const newFieldValue = this.props.fieldValue.filter((value, currentIndex) => {
          return currentIndex !== removeIndex;
        });

        this.props.onUpdateField(newFieldValue);
      } else {
        //It must be a new item
        this.setState({
          newItems: this.state.newItems.length > 1 ? this.state.newItems.slice(1) : []
        });
      }
    };

    const moveInArrayFn = (arr, fromIndex, toIndex) => {
      arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);
    };
    
    const moveFn = (currentIndex, newIndex) => {

      this.setState({
          childrenVisible: false
      }, () => {
          /* we only allow to move if item has been updated and indices are within array bounds */
          if (value !== undefined && this.props.fieldValue && this.props.fieldValue.length > currentIndex && currentIndex >= 0 && this.props.fieldValue.length > newIndex && newIndex >= 0) {
              const arr = this.props.fieldValue.slice();
              moveInArrayFn(arr, currentIndex, newIndex);
              this.props.onUpdateField(arr);
          }
          
          // Atom Workshop used to use Scribe for its rich text editors, but we've now moved to ProseMirror. The app was built in
          // the context described in the following paragraph. Our ProseMirror implementation should work without this workaround,
          // but the logic doesn't degrade the experience in the application, so I'm leaving that logic undisturbed, along with
          // this explanation:
          //
          // When reordering array elements & the child is a scribe editor, we have no way of passing information
          // down to scribe to force a re-render, as scribe cannot handle update changes once it has been
          // initialised. To trigger the visual change without a browser refresh, we need to unmount
          // and remount the child scribe component. However as we only render the root React component
          // with the DOM, and every other downstream component is rendered via React, there is no way to
          // remove the children components. Therefore we use this flag in state to identify whether
          // they should be visible or not. We hide the component until the updates have passed downstream,
          // and then the further change in state below will trigger a re-render & the lifecycle methods of any children.

          this.setState({
            childrenVisible: true
          });

        });
    };


    const hydratedChildren = React.Children.map(this.props.children, (child) => {
      return React.cloneElement(child, {
        key: `${this.props.fieldName}-${i}`,
        fieldName: `${this.props.fieldName}-${i}`,
        fieldValue: value,
        fieldErrors: this.props.fieldErrors,
        formRowClass: 'form__row form__row--flex',
        onUpdateField: updateFn
      });
    });

    const renderMoveBtns = () => {
      if (i !== 0 && (i+1) < this.props.fieldValue.length) {
        return <div>
              <button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i-1) ) } >Move up</button>
              <button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i+1) ) } >Move down </button>
            </div>;
      } else if (i === 0) {
        return <div><button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i+1) ) } >Move down </button></div>;
      } else {
        return <div><button className="btn form__field-btn form__field--move-btn" type="button" onClick= { moveFn.bind(this, i, (i-1) ) } >Move up</button></div>;
      }
    };

    return (
      <div className={this.props.fieldClass ? this.props.fieldClass : null}>
        {this.props.numbered ? <span className="form__field-number">{`${i + 1}. `}</span> : false }
        {hydratedChildren}
        {renderMoveBtns()}
        <button className="btn form__field-btn btn--red" type="button" onClick={removeFn.bind(this, i)}>Delete</button>
      </div>
    );
  }