support-foo/web

View on GitHub
app/assets/javascripts/components/tag_button.js.jsx

Summary

Maintainability
A
2 hrs
Test Coverage
/** @jsx React.DOM */

var TagButton = React.createClass({
  getInitialState: function() {
    return {
      tags: [],
      newTag: '',
      filter: ''
    };
  },

  componentDidMount: function() {
    this.getTags();
  },

  getTags: function() {
    $.getJSON(this.props.conversation.tags_path, function(response) {
      var tags = response.tags.filter(function(tag) {
        return this.props.conversation.tags.indexOf(tag) < 0;
      }.bind(this));

      this.setState({ tags: tags });
    }.bind(this));
  },

  ignore: function(event) {
    $(event.target).parents('.command-bar-action').addClass('open');
  },

  focusInput: function(event) {
    var $input = $('input', $(event.target).closest('button').siblings('.dropdown-menu'));
    setTimeout(function() { $input.focus() }, 0);
  },

  filterTags: function(event) {
    this.setState({ filter: event.target.value });
  },

  tagConversationHandler: function(tag) {
    return function(event) {
      event.stopPropagation();
      event.preventDefault();

      var tagsPath = this.props.conversation.tags_path;
      var data = { tag: tag };

      $.ajax({
        type: 'POST',
        url: tagsPath,
        data: JSON.stringify(data),
        dataType: 'json',
        contentType: 'application/json',
        accepts: { json: 'application/json' },
        success: function(response) {
          this.addStreamItem(response.tag_event);
          this.removeTag(tag);
          this.clearTagFilter(event);
        }.bind(this)
      });
    }.bind(this);
  },

  addStreamItem: function(streamItem) {
    this.props.addStreamItemHandler(streamItem);
  },

  clearTagFilter: function(event) {
    $dropdown = $(event.target).closest('.dropdown-menu').removeClass('open');

    this.setState({ newTag: '', filter: '' });
  },

  removeTag: function(tagToRemove) {
    var tags = this.state.tags.filter(function(tag) {
      return tag != tagToRemove;
    });

    this.setState({ tags: tags });
  },

  updateNewTag: function(event) {
    this.setState({
      newTag: event.target.value,
      filter: event.target.value
    });
  },

  showNewTag: function() {
    return this.state.newTag !== '' && this.state.tags.indexOf(this.state.newTag) < 0;
  },

  renderNewTag: function() {
    if(this.showNewTag()) {
      return (
        <li>
          <a href="#" onClick={this.tagConversationHandler(this.state.newTag)}>
            {this.state.newTag}
          </a>
        </li>
      );
    }
  },

  renderFilteredTags: function() {
    return this.state.tags.map(function(tag) {
      var useFilter = this.state.filter !== '';
      var regexp = new RegExp(this.state.filter, 'gi');
      var applyFilter = useFilter && !tag.match(regexp);

      if(!applyFilter) {
        return (
          <li key={tag}>
            <a href="#" onClick={this.tagConversationHandler(tag)}>
              {tag}
            </a>
          </li>
        );
      }
    }.bind(this));
  },

  render: function() {
    return (
      <div className="btn-group command-bar-action">
        <button className="btn btn-default dropdown-toggle" data-search="tags" data-toggle="dropdown" onClick={this.focusInput}>
          Tag {' '}
          <span className="caret"></span>
        </button>
        <ul className="dropdown-menu" role="menu">
          <li>
            <input type="text" className="form-control" placeholder="Search by tag" onClick={this.ignore} onChange={this.updateNewTag} value={this.state.newTag} />
          </li>
          <li className="divider"></li>
          {this.renderNewTag()}
          {this.renderFilteredTags()}
          {(this.state.tags.length === 0) ?
            <li className="text-muted blankslate">No tags yet! Type above to create your first.</li>
          : null}
        </ul>
      </div>
    );
  }
});