GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Written by thoughtbot

Copycopter: Introducing a Simpler Way to Edit Copy

Around four months ago we launched our service for managing copy text in web applications: Copycopter. If you're already riding the choppa, you've probably noticed steady improvements in the user interface. Today, we rolled out another change to make things nicer for non-technical users that want to edit copy text: a simpler editor.

What you see is what you get

We've done a lot to keep Copycopter simple to use. However, we also didn't want to limit developers and designers by enforcing a limited language like textile, so we stuck to good old HTML. The draw back of this approach is that nontechnical users were occasionally confused by the markup in the copy text they wanted to edit. Worse, it was easy for such a user to break the page by accidentally changing the HTML to be invalid. We didn't think it was acceptable for every user to be one "<" away from a broken page, so we changed this:

HTML editor

To this:

Simple editor

If you'd rather see HTML, don't worry - we're not leaving anyone out in the cold. One click will bring you back to the HTML editor you know and love:

Switch editor mode

We used the excellent jwysiwyg library to create our simple editor. Getting the editor up and running was easy, but making our integration tests work with the editor to ensure that it continued to work was a bit of a challenge.

TDDWYSIWYG

We used Cucumber to make sure that users could apply formatting to blurbs:

@javascript
Scenario: Apply formatting to blurbs
  Given the following blurb exists:
    | project         | key      | draft content |
    | name: Breakfast | test.key | <p>hello</p>  |
  When I go to the edit blurb page for "test.key" on "Breakfast"
  And I apply the "bold" editor function to "ell"
  And I apply the "italic" editor function to "el"
  And I press "Save Blurb"
  And I go to the blurbs index for the "Breakfast" project
  Then I should see "<p>h<b><i>el</i>l</b>o</p>"

To apply editor functions, we use this step:

When /^I apply the "([^"]+)" editor function to "([^"]+)"$/ do |function, selection|
  steps %{
    When I select "#{selection}" from the editor
    And I click the "#{function}" editor button
    And I unfocus the editor
  }
end

The secret sauce there is being able to select text. We used capybara-webkit to run Javascript scenarios in Copycopter, so we can use the DOM range and selection API:

When /^I select "([^"]+)" from the editor$/ do |text|
  within_editor do
    page.execute_script(<<-JS)
      var xpath = "//*/child::text()[contains(., '#{text}')]";
      var node = document.evaluate(xpath, document, null, XPathResult. ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
      if (!node)
        throw("Couldn't find text to select");
      var start = node.data.indexOf('#{text}');
      var end = start + #{text.size};
      var selection = window.getSelection();
      var range = window.document.createRange();
      range.setStart(node, start);
      range.setEnd(node, end);
      selection.removeAllRanges();
      selection.addRange(range);
    JS
  end
end

The "within_editor" method selects the iframe created by jwysiwyg, which is possible because of Aaron Gibralter's contributions to capybara-webkit. We have several other scenarios for switching modes, handling newlines, and so on.

If you want to see our new editor in action, you should check out Copycopter!