Draft.js and colorful Quick Quotes


OnePageCRM is constantly evolving.

Over the last number of years, we have started to slowly incorporate React into some parts of our application. It wasn’t an easy transition, and we have overcome a number of obstacles integrating this new view rendering framework into our existing application without a full rewrite. However, we’re happy to say that all new features are now using React.

Since we already implemented React for text editing functionality, we decided to pick the Draft.js library. Draft.js is a library for building rich text editors. It allows you to build your editor for your needs - whether you’re just looking to support a few inline text styles or building a complex text editor for composing long-form articles. In Draft.js, everything is customizable – it provides the building blocks so that you have full control over the user interface.

Every time a user in OnePageCRM composes a new email message, he uses the Draft.js library.

Beside text editing, users are allowed to format the text in different ways - customize fonts, size, style and color. Also, they are given options to insert images, links, files and so on. It is our custom rich text editor built using Draft.js which enables us to offer these options.

Another cool feature built in React is Quick Quotes. It allows users to quickly send quotes directly from within OnePageCRM using deal items and their own email client. The original release had the quote text with one plain text color block, however our latest update allows users to customize the colour of the Quote block to quickly catch the reader’s attention. Using Draft.js I was able to customize this color component and here’s how I did it…


Implementation

The right way to achieve this is to set blockStyleFn callback for the editor. It allows to specify classes that should be applied to blocks at the render time. In context of Draft.js and QuickQuotes, the block is a piece of text - one line with text or even an empty line.

In our case it looks like this:


blockStyleFn: (contentBlock) =>
  type = contentBlock.getType()
  colorName = ReactModules.settings.quoteBlockColor
  if type is 'qq-block'
    colorClass = 'qq-block-' + colorName
    type + ' ' + colorClass # for example will return "qq-block qq-block-yellow"


And then in CSS we need to define these classes:


.draft-editor {
    .qq-block {
        padding-left: 10px;
    }
    .qq-block-grey {
        background: #F6F5F5;
    }
    .qq-block-yellow {
        background: #FFFFE0;
    }
    .qq-block-green {
        background: #EDFFEB;
    }
}


As a result, the Quick Quotes block now has a color. Even if the user wants to edit or add new lines, the color still persists. Also, styling allows us to add a little padding to the left-hand side, that makes the quote look even better.

You can read more about block styling in Draft.js here.


Solving performance problems

Whilst coding the color update, I also rewrote some other parts of the Quick Quotes feature. I noticed that the insertion of a large list of deal items (more than 1,000) makes the page unresponsive and this definitely needed to be fixed. I started to investigate the problem and, after some ‘digging’ and lots of code review, I found the problem.

This is the code before:


# here we re-assemble Quick Quotes blocks (lines) to configure and join together
for block in blocks
  contentState = editorState.getCurrentContent()
  selection = editorState.getSelection()
  rawObject = Draft.convertToRaw(editorState.getCurrentContent())

  enterObject =
    depth: block.depth
    key: block.key
    text: block.text
    type: block.type
    entityRanges: []
    inlineStyleRanges: []

  rawObject.blocks.push(enterObject)
  newContentState = Draft.convertFromRaw(rawObject)

  editorState = Draft.EditorState.push(editorState, newContentState, 'insert-fragment')
  editorState = Draft.EditorState.moveSelectionToEnd(editorState)


The solution here is to initialize editorState in the beginning and update its content only in the end:


editorState = Draft.EditorState.createEmpty()
rawObject = Draft.convertToRaw(editorState.getCurrentContent())

# process each line of QuickQuotes
for block in blocks
  enterObject =
    depth: block.depth
    key: block.key
    text: block.text
    type: block.type
    entityRanges: []
    inlineStyleRanges: []

  rawObject.blocks.push(enterObject)

newContentState = Draft.convertFromRaw(rawObject)
editorState = Draft.EditorState.push(editorState, newContentState, 'insert-fragment')
editorState = Draft.EditorState.moveSelectionToEnd(editorState)
editorState.getCurrentContent() # return content


It looks obvious, and it is! This change helped to make the insertion of Quick Quotes fast again.


What I learned…

Draft.js is a powerful tool. It allows to you make complex stuff work quite smoothly. We used its capabilities to improve the Quick Quotes functionality and give the user the option to customize the quote block color. Draft.js is a library that is not easy to master, and I found there was a lack of ready to use solutions available online. I quickly realized that you must have a deeper understanding of how Draft.js works, and its functionality will help you to solve problems by yourself.


Here are some resources I found very useful in my time using Draft.js:

  1. Draft.js official docs https://draftjs.org/docs/getting-started
  2. Very good explanation articles on https://learn-draftjs.now.sh (GitHub repository)
  3. A configurable rich text editor built with Draft.js https://www.draftail.org
  4. More resources about Draft.js collected in one place https://github.com/nikgraf/awesome-draft-js



Image references: OnePageCRM and Jakpost.travel

Author image

Victor Pukman

Software Engineer at OnePageCRM. Enjoys developing features which help people to solve real problems. Experience in Ruby and CoffeeScript. In his spare time, Victor plays music and enjoys exploring new places.