All files / src/ui TextEditor.js

97.3% Statements 36/37
100% Branches 0/0
91.67% Functions 11/12
100% Lines 34/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 881x 1x 1x 1x 1x 1x       1x   258x                       129x 129x   129x 129x   129x 129x 129x 129x 129x 129x   129x     129x 129x     129x     129x         129x     129x 387x 3x   129x       129x       130x                           9474x 129x   129x     1x  
import React, {Component} from 'react';
import PropTypes from 'prop-types/prop-types';
import {connect} from 'react-redux';
import {UnControlled as CodeMirror} from 'react-codemirror2';
import SHARED from '../shared';
import {say} from '../utils';
 
// CodeMirror APIs that we need to disallow
// NOTE(Emmanuel): we should probably block 'on' and 'off'...
const unsupportedAPIs = ['startOperation', 'endOperation', 'operation'];
 
class TextEditor extends Component {
  static propTypes = {
    cmOptions: PropTypes.object,
    parser: PropTypes.object.isRequired,
    initialCode: PropTypes.string.isRequired,
    onBeforeChange: PropTypes.func,
    onMount:PropTypes.func.isRequired,
    setAnnouncer: PropTypes.func.isRequired,
    api: PropTypes.object,
  }
 
  handleEditorDidMount = ed => {
    const wrapper = ed.getWrapperElement();
    wrapper.setAttribute('aria-label', 'Text Editor');
 
    const scroller = ed.getScrollerElement();
    scroller.setAttribute('role', 'presentation');
 
    const announcements = document.createElement('span');
    announcements.setAttribute('role', 'log');
    announcements.setAttribute('aria-live', 'assertive');
    wrapper.appendChild(announcements);
    this.props.setAnnouncer(announcements);
    say("Text Mode Enabled", 500);
    
    SHARED.cm = ed;
 
    // reconstitute any marks and render them
    setTimeout( () => {
      SHARED.recordedMarks.forEach(m => SHARED.cm.markText(m.from, m.to, m.options));
    }, 250);
 
    this.props.onMount(ed);
 
    // export methods to the object interface
    Object.assign(this.props.api, this.buildAPI(ed));
  }
 
  // override default CM methods, or add our own
  buildAPI() {
    const api = {};
    // show which APIs are unsupported
     // show which APIs are unsupported
    unsupportedAPIs.forEach(f =>
      api[f] = () => {
        throw `The CM API '${f}' is not supported in CodeMirrorBlocks`;
      });
    return api;
  }
 
  componentDidMount() {
    SHARED.parser = this.props.parser;
  }
 
  render() {
    return (
      // we add a wrapper div to maintain a consistent DOM with BlockEditor
      // see DragAndDropEditor.js for why the DND context needs a wrapper
      <div> 
        <CodeMirror
          value={this.props.initialCode}
          onBeforeChange={this.props.onBeforeChange}
          options={this.props.cmOptions}
          editorDidMount={this.handleEditorDidMount} />
      </div>
    );
  }
}
 
const mapStateToProps = _state => ({});
const mapDispatchToProps = dispatch => ({
  dispatch,
  setAnnouncer: announcer => dispatch({type: 'SET_ANNOUNCER', announcer}),
});
 
export default connect(mapStateToProps, mapDispatchToProps)(TextEditor);