Rich Text Editor - revaturelabs/revoverflow-frontend GitHub Wiki

Draft-JS

For the text input in this application for submitting questions and answers, we wanted to make sure we provided some form of support for rich text formatting. Especially when considering that some questions/answers could be long and complex, it was important to allow users to add styles to their bodies of text for emphasis, organization, etc. The solution we ended up working with is Draft.js. Below is an excerpt from its website.

Draft.js is a framework for building rich text editors in React, powered by an immutable model and abstracting over cross-browser differences.

Draft.js allows you to build any type of rich text input, whether you're only looking to support a few inline text styles or building a complex text editor for composing long-form articles.

Draft.js is an open source project from Facebook, leverage this link to their home page for more information.

Persisting Data

Persisting data was one of the main areas for this component where research was necessary. Among all of the editor options out there, one of the reasons we chose to work with Draft.js is because they provide utility functions to aid in this process. These utility functions will convert the content of the editor to and from a raw JS object for storage in your database. We chose to then stringify the JSON and store the string. But for more information on the utility functions used, visit this link for Draft.js data conversion.

Adding styles

Adding styles to this editor was a fairly simple process. There is some help provided on the official draftjs website, but our work was done with some help from additional resources to achieve proper functionality. Parts one and two of this guide for Getting started with Draft.js were pretty useful to us for getting started with this. Below I'll add a couple of code snippets from our project and their function.

This is an example of a function used to add bold styling to text. We created a function similar to this for the styles we wanted to include, and then we later mapped each style to a designated button.

const onBoldClick = (event: any) => {
        event.preventDefault();
        onChange(RichUtils.toggleInlineStyle(editorState, 'BOLD'));
}

Both of these functions below are slightly different ways to do the same thing: conditionally style a button based on if its styling was currently selected. So for example, changing the color of the bold button each time it is clicked to indicate bold styling being on or off.

const buttonVariant = (name: string) => {
    const currentInLineStyle = editorState.getCurrentInlineStyle();
    if (currentInLineStyle.has(name)) {
        return true;
    } else {
        return false;
    }
}
const blockbuttonVariant = (name: string) => {
    const currentInLineStyle = RichUtils.getCurrentBlockType(editorState);
    if (currentInLineStyle === name) {
        return true;
    } else {
        return false;
    }
}

Below is the portion of code where we made an array of properties that we would map to buttons to be placed above the text editor. This example is of the inline styles that we used.

const buttons = [
    { function: onBoldClick, name: <FormatBoldIcon />, style: 'BOLD' },
    { function: onItalicClick, name: <FormatItalicIcon />, style: 'ITALIC' },
    { function: onUnderlineClick, name: <FormatUnderlinedIcon />, style: 'UNDERLINE' },
    { function: onStrikethroughClick, name: <StrikethroughSIcon />, style: 'STRIKETHROUGH' },
    { function: onCodeClick, name: <CodeIcon />, style: 'CODE' }]

Finally, below you'll see the code used to map the above properties to buttons for inline styles. We conditionally rendered one of two span elements containing each button based on if a button was selected or not, note the "variant" attribute.

{buttons.map(b =>
    buttonVariant(b.style) ?
        <span key={b.style} className={classes.buttonInternal}>
            <Button key={b.style} onMouseDown={b.function} variant='contained' color='primary' size='small' >{b.name}</Button>
        </span>
        :
        <span key={b.style} className={classes.buttonInternal}>
            <Button key={b.style} onMouseDown={b.function} size='small' color='secondary' variant='contained' >{b.name} </Button>
        </span>)}
⚠️ **GitHub.com Fallback** ⚠️