Start trial
PricingContact Us
Log InStart For Free

How-to use tinymce

How to add WYSIWYG editing to your project management platform so it rivals Jira and Asana: a tutorial

Published December 14th, 2022

All Project Management Platforms have text entry components. Every project breaks down into tasks, and the 'to do' items that make up those tasks have to be typed somewhere – that's your text entry component. But, you can’t have a good Project Management Platform without also having a great WYSIWYG.

AUTHORS:

John Rau

Marketing Manager at Tiny

Joe Robinson

Dev Advocate | Tech Writer at Tiny

Di Mace

Communications Manager at Tiny


Project management is no trivial matter, and companies are willing to invest heavily in the right platform – as long as it meets their needs. In 2021, the amount of revenue created by project management software revenue totaled $5.9 Billion, while it’s expected to grow to $20 Billion over the next 10 years. That's an annual growth rate of 13%, which shows the increasing role project management is playing in workplace processes. And there’s huge potential for even further growth.

Why a great WYSIWYG editor matters

A great WYSIWYG that can handle the anticipated growth in project management software needs to provide collaboration, better information sharing, and meet the needs of remote workers. Because the businesses that are buying Project Management Platforms (your potential customers) are looking for something that:

  • Increases their productivity
  • Streamlines communications
  • Improves project quality
  • Minimizes costs.

To capture these opportunities and better meet customer needs, look for a proven, mature WYSIWYG component to add to your software, that can be easily configured into your Project Management Platform.

What you’re building in this project management editor tutorial

This tutorial teaches you how to create an editing experience for the “task” screen, found on many popular Project Management Platforms. It’s modeled off of the “issue” screen found in the Project Management Platform, Jira.

Jira Project Management, as an example

The tutorial shows you how to create a:

  • Description Editor
  • Comments Editor
  • Build a UI

And then configure them both to have a minimal behavior which activates the editing controls only when the user wants to edit the content – providing a true inline editing experience. The completed project will be called a Task Screen Editor.

LEARN MORE

Find out the vital workflow automation trends to watch

Clicking to edit in the completed project

This style of “click to edit” editing is becoming increasingly popular amongst project management and other workflow tools. It enables the user to browse large swaths of content in a distraction-free “read mode” and then edit it in-place without having to load a new screen. Ultimately, this leads to less clicking around, higher engagement and consequently better retention.

Test the final editor 

When completed, the tutorial’s Description Editor, Comments Editor, and UI come together to form the following final project management editing experience:


What’s not covered in this tutorial (you will need to configure your app to do this as it’s beyond the scope of TinyMCE and this article):

  • Save and Cancel button behavior
  • Saving content to your database

Why bother building your own WYSIWYG project management editing experience?

Let’s tackle the two questions most frequently asked by developers and product managers alike, in the context of building their own vs buying components and assembling a state-of-the-art rich text editor within a Project Management Platform.

1. Why not build from scratch?

You can build your own project mgmt specific rich text editor (RTE) from scratch and there are numerous resources available to aid that process. However, rich text editors are exceptionally complex. A development team that’s inexperienced in developing RTEs, generally underestimates the myriad of edge cases across both browsers and functionality. Even things that are perceived to be basic, are hard. And costs blow out.

Our latest calculations* estimate the cost of building just the basic open source components of three leading rich text editors (excluding advanced features and plugins) varies between US$15M and US$28M. While the time taken ranges from 115 to 220 person-years for a single developer. There must be a better way.
(*as at July 2022)

LEARN MORE

Tiny puts a price tag on building your own core rich text editor

Read the article →

2. Why build at all? Can’t you buy what you need?

You can, which is why TinyMCE has been engineered so that it’s adaptable to you and your needs. Everything you need is provided out-of-the-box: you pick the features you want from the plugin list, and add them to the core editor. All you do is assemble the components (either through APis or coding), customize parts (if you desire) and draw on the decades-long experience of its builders in rich text editing.

There's even use case specific starter configs and demos, to make it easier for you to get started, along with detailed documentation and support.

LEARN MORE

Find out more about the Buy vs Build debate, in our Whitepaper

Deciding to upgrade vs assembling-and-building

If you already have an app or website, there’s no need to start from scratch. TinyMCE is a flexible WYSIWYG that's easy to integrate. Regardless of your use-case, TinyMCE can easily replace whatever rich text editor you’re currently using, or take the place of a textarea, textbox, or other planned text entry component on a page.

There are pluses and minuses to taking an assemble and upgrade approach, but it can have a very positive effect on your speed-to-market.

Ideally, an upgrade to your current platform shouldn’t present long term difficulties or issues with ongoing maintenance problems. That’s the last thing your team needs: another dependency to maintain. When using TinyMCE through the cloud, Tiny Cloud automatically updates to the latest functionality when new versions are released. (It’s one of the methods we use to reduce development pressure.)

If you’re aiming to upgrade your software, the starter config code is available throughout the tutorial, to use (or just review) as you need. Or, if you’re using a particular framework, TinyMCE is designed to integrate into a variety of frameworks and use cases.

Essentials to start building your project management editor

This tutorial guides and provides the tools needed to construct a WYSIWYG editor suitable for project management, that uses the core TinyMCE rich text editor, upgraded to include advanced features. The tutorial contains:

  • Background explanations
  • Step-by-step procedures
  • Working code you can test.

The step-by-step explanations show you how to configure TinyMCE to create a description editor, and the working code shows how the addition of plugins results in a comments editor and UI that rivals Asana and Jira.

Before proceeding, we’ve assumed that you know:

  • Basic HTML, CSS, and JavaScript
  • How to set up index.html files for demo purposes
  • Internet browsing using popular browsers like Chrome, Safari, or Firefox
  • Running a local server with a command like:
python3 -m http.server 8000

Background Part 1

Building a project management style Description Editor

This part of the tutorial covers the basic elements that come together to create a WYSIWYG Description Editor for your task screen.

Description Editor complete

While the final editing experience we’re building has several features, it’s best to start with the description field – the foundational element of the task screen – and build on it, Why? Because the description field defines the ‘issue’ or work to be done.

To build the Description Editor, this tutorial contains instructions on how to:

  • Add plugins
  • Refine the editing experience

It also explains the Inline mode, which gives you the experience of editing part of the actual web page in situ, and also shows how the TinyMCE editor automatically resizes according to content length.

You then expand the Description Editor, with TinyMCE’s creativity features. By setting up plugins such as Emoticons, Image, Editimage, Media, Mediaembed, and Codesample, you allow customers to better express themselves.

Lastly, Part 1 shows how there are small and effective adjustments you can make to TinyMCE to further increase productivity. It’s the exact reason why companies buy a project management platform – to get complex work done quickly and efficiently. These steps demonstrate how to set up the Link plugin, and how to configure Spell Checker Pro, Languages, Spelling Autocorrect, Link Checker, Autolink, Powerpaste, Lists, Table, Advanced Tables, Advanced Code, and Checklist plugins.

READ MORE ON PROJECT MANAGEMENT PRACTICES

Thrive in the new Project Economy with the best technology

Read the article →

Background Part 2

Adding a Comments Editor using a common config

Did you know that project management software can save project managers an average of 10 minutes per day? Added together, that’s an astonishing 153 hours per year! And what if, your Project Management Platform, could help them find even more time?

Efficient collaboration helps projects move faster and smoother. That’s what Part 2 unlocks. It shows you how to set up the Comments Editor (not to be confused with the TinyMCE Comments feature), which gives users a mechanism to discuss the task in detail, using rich text comments.

In this part, we specify the common config – a set of configuration options that both the Description Editor (Part 1) and Comments Editor (Part 2) use. This lets you maintain one set of common configuration options thereby cutting down on excess code and removing configuration duplication.

Comments Editor working in the browser

Background Part 3

Building a UI

When you load a task in a Project Management Platform, the default view should be read-only so that the users can quickly scan and consume the contents of the task screen. Part 3 explains how to hide the editor controls by default on the UI, which provides a distraction-free reading experience that best helps your users browse tasks.

When the editor is clicked, the editing controls reappear, thus allowing for fast editing without needing to load a new screen. This UI subtlety contributes to workforce efficiency gains, reduces distraction and adds to the overall satisfaction with your project management tool.

The following demo shows the completed WYSIWYG project management editing experience, which you can explore:

Part 1 Build

How to build a Description Editor

TL;DR

Part 1 explains the steps to make the essential part of your project management task screen – the Description Editor.

After building the Description Editor, you’ll have an editor that can be used to input and describe tasks and work to be done. These refinements show how the Description Editor can provide a valued task management component.

Step 1: Getting started

These are the foundational elements. When setting up in the Description Editor, they act as the primary means of defining tasks within your project management app.

Plugins and config options to get started

Toolbar

Menubar

Placeholder

Toolbar Persist

Inline Editing

To get started, you’ll also need to set up a TinyMCE API key. This key is free, and gives you 14 days access to TinyMCE’s Premium plugins, as well as premium Icons and Skin Packs. You can use a Google or a GitHub account to sign in if you need, and using an API key also keeps errors about the domain name out of the text area.

You can sign up directly here, or check one of the pricing plans.

NOTE: If you already have a Project Management Platform that you’re aiming to upgrade by using TinyMCE, take a minute to register your domain as an approved domain in your account. The first steps are to create a folder and a file within your developer environment.

a. Menubar


What the Menubar does

The Menubar, while useful, is better concealed in the Description Editor. This keeps the overall interface uncluttered, and easier to use.

menubar: false,

b. Inline Editing


What Inline Editing does

When set to inline mode, the Description Editor experience is further streamlined, allowing users to edit content “inline” (or in situ) in the page without having to load another screen to go into edit mode. Inline mode also allows the editor to resize automatically, based on the task content length described in the editor.

inline: true,

c. The Placeholder option


What the Placeholder option does

The Placeholder option as a cue for writing a task. Adding content to the editor when it loads helps prompt the end user to start writing their task description.

placeholder: "Add a description",

d. The Toolbar Persist option


What the Toolbar Persist option does

Toolbar Persist keeps the toolbar visible in the inline editor, even when it loses focus. This is important later on, when we add the JavaScript and CSS functionality to enable and disable the editor.

toolbar_persist: true,

How to configure the essential Description Editor options

1. In your development environment, set up an index.html file.

2. To that file add some introductory HTML:

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
    <title>TinyMCE Workflow Starter Config</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
      tinymce.init({ });
    </script>
 </head>
   <body>
    <main>
      <textarea></textarea>
    </main>
   </body>
</html>

3. Include the TinyMCE CDN link, with your API key:

 <script src="https://cdn.tiny.cloud/1/your-api-key/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>

4. Add the plugins option to the tinymce.init script:

plugins: "advcode advtable autocorrect autolink checklist codesample editimage emoticons image link linkchecker lists media mediaembed powerpaste table tinymcespellchecker"

5. Change the Description Editor to inline mode. This setting allows the editor to resize automatically. An editor of variable size adds a small but significant productivity increase:

plugins: "advcode advtable autocorrect autolink checklist codesample editimage emoticons image link linkchecker lists media mediaembed powerpaste table tinymcespellchecker",
menubar: false,
inline: true,

6. Adjust the basic HTML to initialize TinyMCE on an element other than the textarea element. The following HTML uses a series of CSS selectors for styling the editor:

<main>
  <label class="editor-label">Description</label>
  <div class="editor-wrap" id="editor-description-wrap">
    <div class="editor-toolbar" id="editor-description-toolbar"></div>
    <div class="editor-content" id="editor-description-content">
      <p>Hello world!</p>
      <p>Here is some content for our description editor.</p>
    </div>
  </div>
</main>

7. Add the toolbar to provide access to the plugins, and set the toolbar persist option to “true”:

menubar: false,
inline: true,
toolbar_persist: true,
toolbar: "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons | table codesample hr blockquote | code ",

8. Set up the Placeholder option to help prompt the task description writing:

toolbar_persist: true,
toolbar: "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons | table codesample hr blockquote | code ",
placeholder: "Add a description",

9. Include the following CSS in the head of your page to style the contents of both editors. Since they are both loaded in inline mode, the editor contents inherit any CSS styles defined at the page level. The following is an example, and you can change the style to suit your application design:

    <script>
        <style>
            body {
                margin: 2rem 0.5rem;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
            }
            main {
                max-width: 720px;
                margin: auto;
            }
            /* Below are the content styles, in this case they are global */
            /* and not namespaced to `.editor-content` */
            a,
            a:link {
                color: blue;
            }
            a:visited {
                color: purple;
            }
            a:hover {
                color: green;
            }
            a:active {
                color: red;
            }
            h1 {
                font-size: 1.75rem;
                font-weight: strong;
            }
            h2 {
                font-size: 1.5rem;
                font-weight: strong;
            }
            h3 {
                font-size: 1rem;
                font-weight: strong;
            }
            hr {
                margin: 1.5rem 0;
                padding: 0.5rem 0;
                border: 0;
            }
            hr::after {
                content: "";
                display: block;
                border-width: 0 0 1px 0;
                border-color: #ccc;
                border-style: solid;
            }
            img {
                max-width: 100%;
                height: auto;
            }
        </style>

10. Save the changes, and then load the Description Editor in your browser.

The following demo shows the first part of the Description Editor completed:

Step 2: Improving communication in the Description Editor

Did you know that team communication improves by 52% when an organization adopts project management software? Using project specific software ensures better information sharing amongst users and also provides (now essential) support for your remote workers across the globe. And the easier you make communication and expression for your customers, the faster they can complete their tasks.

The following plugins improve communication

Emoticons

Media

Image

Code Sample

Enhanced Image Editing

Code

Enhanced Media Embed

a. Images


What the Image plugin does

It’s not easy to enforce strict image formatting, but the TinyMCE Image plugin provides an easy solution. The plugin lets you insert an image into the task description, and blocks certain kinds of images. Essentially, you can control exactly the kinds of image, by file type, that customers can upload.

While that upload process isn’t within the scope of this tutorial, read this article on how to set up file uploads usingTiny Drive, a cloud file management solution.

Why would you need to optimize it?

Use the images_file_types option to restrict the images types that can enter the Description Editor through file upload. By default, the file types allowed are:

  • jpeg
  • jpg
  • jpe
  • jfi
  • jif
  • jfif
  • png
  • gif
  • bmp
  • webp

One of these image types is not something you EVER want to allow into the task description. That would be the bmp file type (AKA bitmaps). They’re known for being large, clunky and slow to load, so make sure you disallow this vintage file type.

How to set it up

1. Include the image_file_types option in the configuration:

image_file_types: '',

2. Include the files that you want accepted:

image_file_types: 'jpeg jpg png gif',

3. Save the changes. Attempts to upload these file formats are now blocked.

b. Enhanced Image Editing


What the Image plugin does

Enhanced Image Editing adds a contextual toolbar for editing images,to the text area. You can configure the toolbar for image rotation and image flipping – vertical or horizontal. The plugin also provides several options for working with Cross-Origin Resource Sharing (CORS) security to allow the plugin to edit images from another domain.

CORS is the ability for a server to permit a source to load content in the browser.

Find out More

Why would you need to optimize it?

The Enhanced Image Editing plugin provides image editing controls. While rotating an image is important, the image editing shouldn’t be a distraction. The configuration provided for this project, gives an efficient editing experience by including only the image rotate control. This keeps the focus on communicating the task description effectively, without getting distracted.

NOTE: The Object resizing option is not directly a configuration option for the Enhanced Image Editing plugin, however it does relate to image content. Setting this plugin to the ‘false’ value deactivates the resizing handles on images (as well as tables and media objects). Setting this option to ‘false’ reduces time spent adjusting images by fractions. Instead, the image shrinks to fit the editor if it’s larger than the editor, or the image will appear in its original dimensions.

How to set it up

1. Add the editimage toolbar option to the Description Editor configuration:

editimage_toolbar:

2. Set the options to only allow image editing for left and right rotation:

editimage_toolbar: "rotateleft rotateright"

3. Add the object resizing option to the tinymce.init configuration, and set it to false:

object_resizing: false

4. Save the changes

c. Enhanced Media Embed


What is the Enhanced Media Embed plugin?

The plugin gives users the ability to insert third party content into the Project Management Platform editor. This includes media such as Youtube Videos, or other embedded content, which saves time for users – they don’t need to generate embed code, insert the code into the editor nor spend time testing the embed code to make sure it works.

Why would you need to optimize it?

The plugin doesn’t need further configuration, but there is one major consideration: if your Project Management Platform needs to generate static views outside of the editor, then you’ll need to add some specific CSS content.

How to set it up

1. Add the following CSS into your Project Management Platform to ensure that the embed content displays correctly:

.ephox-summary-card {
  border: 1px solid #AAA;
  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
  padding: 10px;
  overflow: hidden;
  margin-bottom: 1em;
}

.ephox-summary-card a {
  text-decoration: none;
  color: inherit;
}

.ephox-summary-card a:visited {
  color: inherit;
}

.ephox-summary-card-title {
  font-size: 1.2em;
  display: block;
}

.ephox-summary-card-author {
  color: #999;
  display: block;
  margin-top: 0.5em;
}

.ephox-summary-card-website {
  color: #999;
  display: block;
  margin-top: 0.5em;
}

.ephox-summary-card-thumbnail {
  max-width: 180px;
  max-height: 180px;
  margin-left: 2em;
  float: right;
}

.ephox-summary-card-description {
  margin-top: 0.5em;
  display: block;
}

2. Save the changes

d. Code Sample


What is the Code Sample plugin?

This plugin handles embedding syntax, by providing highlighted code snippets into the Description Editor. It also adds a button on the toolbar for opening a dialog window that can accept raw code. The specific language highlighting is available in the dialog window, from a drop down list of options.

Why would you need to optimize it?

By offering a way to quickly insert code blocks directly into the task descriptions, it makes life easier for technology-focused users. The plugin helps users quickly convey technical details of the task to other team members, efficient communication is essential to successful project management.

By default, the plugin comes with formatting for 10 programming languages:

  • HTML/XML
  • JavaScript
  • CSS
  • PHP
  • Ruby
  • Python
  • Java
  • C
  • C#
  • C++

There is also a solution if the specific language you need isn’t on the default list. The Code Sample plugin uses PrismJS for syntax highlighting, which supports over 247 languages. Odds are, you can find the language that you need.

In this example, we’ll set up the Code Sample plugin for a web development team – including formatting for HTML, JavaScript, CSS, LESS, PHP, SQL and Python. If your dev teams use other stacks, configure the plugin differently.

How to set it up

To configure the Code Sample plugin:

1. Add the codesample global PrismJS option to the TinyMCE config, and set the value to ‘true’:

codesample_global_prismjs: true

2. Create an array called codesample languages:

codesample_global_prismjs: true,
codesample_languages: [],

3. Add the initial code languages you want to the codesample array using the text and value format. Here is the HTML and XML language as an example:

codesample_global_prismjs: true,
       codesample_languages: [
        { text: 'HTML/XML', value: 'markup' },
],

4. Add a reference to the PrimsJS CDN in the head section of your HTML content:

<title>Workflow Description Editor Config</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markup.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-markup-templating.min.js"></script>

5. Add additional code to the codesample array as needed:

 codesample_global_prismjs: true,
       codesample_languages: [
        { text: 'HTML/XML', value: 'markup' },
        { text: 'JavaScript', value: 'javascript' },
        { text: 'CSS', value: 'css' },
        { text: 'LESS', value: 'less' },
        { text: 'PHP', value: 'php' },
        { text: 'SQL', value: 'sql' },
        { text: 'Python', value: 'python'},
      ],

6. When you update the array, remember to add the CDN script to the top of the HTML:

 <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-javascript.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-css.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-less.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-php.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-sql.min.js"></script>

7. Save the changes

Step 3: Maximizing productivity with the Description Editor

Poor project performance has a tangible, bottom line impact, with 9.9% of every dollar spent on a project wasted by poor performance.

But what is poor performance? When the measured outcome of a project is lower than the expected target. While the aim of most product management software is to help you improve performance, there are also small optimizations you can make within the WYSIWYG editor that can further help your results. The following plugin optimizations work quietly in the background to augment the content creation experience.

Maximize productivity

Spell Checker Pro

PowerPaste

Advanced Code

Spelling Autocorrect

Lists

Checklists

Link Checker

Tables

Autolink

Advanced Tables

a. Link Checker


What is the Link Checker plugin?

This plugin validates URLs as you type them into the Description Editor. It highlights any invalid URLs in red, and gives the option to edit the link, remove the link, or try to open the link in a separate tab through a context menu that opens when interacting with the flagged URL.

Why would you need to optimize it?

The “add link” dialog box that allows users to specify the link target. That is, whether the link opens in a new tab or in the current tab. To prevent any chance of confusion, specify the link target to “_blank” to make sure it always opens links in a new tab by default. This helps the end user keep the task they’re looking at in the Description Editor open.

How to set it up

1. Add the link default target option in the Description Editor config.

link_default_target: "",

2. Set it to _blank to ensure that links open in a new tab by default:

link_default_target: "_blank",

3. Save the changes

b. Spell Checker Pro


What is the Spell Checker Pro plugin?

The Spell Checker Pro Plugin looks for spelling errors and highlights them as-you-type. It’s a capability that saves users time by immediately bringing typos and mistakes to their attention. Spell Checker Pro uses English US dictionaries as the default language, and there are other options if you require multi-language spell checking.

Why would you need to optimize it?

TinyMCE’s Spell Checker Pro can simultaneously spell check across (up to) 38 languages in the same document. This makes project management across cultures easier by giving your Description Editor experience an additional, useful feature for geographically distributed remote teams. It also allows you to build custom dictionaries, to ensure global brand terminology is maintained. User’s can select the languages to check and, if you want more choice there are additional configurations you can make to adjust how the plugin activates.

How to set it up

1. Add the spellchecker language option to the TinyMCE init script:

spellchecker_language: " ",

2. Set the language options for the Description Editor as the value for the spellchecker language option. The following example adds English only:

spellchecker_language: "en",

3. Configure the plugin so that spell checking is active by default with the spellchecker active plugin. Set this value to “true”:

spellchecker_language: "en",
spellchecker_active: true

4. Save the changes.

Check on the optimizations made for productivity in the complete demo:

Step 4: Encouraging collaboration

Project management software saves team members an average of 20 minutes of work time per day. That’s an average of 498 hours saved across the entire year. And what contributes to that time saving? Effective collaboration. The TinyMCE Mentions plugin provides a way for users to get in contact quickly, and it enhances the collaboration experience.

What is the Mentions plugin?

Every day on social networks, the @mention keeps people in contact. Now this functionality has moved into the work world. The Mentions plugin helps users to draw attention to an urgent task in the Description Editor, by tagging someone and getting tasks completed (hopefully faster).

How to set it up

Setting up mentions requires specific configuration – beyond this tutorial’s scope – which is why a specific guide explains how to set up mentions. Check on the guide: How to get started with TinyMCE Mentions.

The complete Description Editor

And the following is the complete code for the Description Editor:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>
            // 1.1 Set up the description editor
            tinymce.init({
                selector: "#editor-description-content",
                plugins: "codesample editimage emoticons image media mediaembed powerpaste code link autolink tinymcespellchecker autocorrect linkchecker lists checklist table advtable advcode",
                menubar: false,
                inline: true,
                toolbar_persist: true,
                toolbar: "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons | table codesample hr blockquote | code ",
                placeholder: "Add a description",

                // 1.2 Improve communication
                image_file_types: "jpeg jpg png gif",
                editimage_toolbar: "rotateleft rotateright",
                object_resizing: false,
                codesample_global_prismjs: true,
                codesample_languages: [
                    {
                        text: "HTML/XML",
                        value: "markup"
                    },
                    {
                        text: "JavaScript",
                        value: "javascript"
                    },
                    {
                        text: "CSS",
                        value: "css"
                    },
                    {
                        text: "LESS",
                        value: "less"
                    }, {
                        text: "PHP",
                        value: "php"
                    }, {
                        text: "SQL",
                        value: "sql"
                    }, {
                        text: "Python",
                        value: "python"
                    }
                ],

                // 1.3 Maximise Productivity
                link_default_target: "_blank",
                spellchecker_language: "en",
                spellchecker_active: true
            });
        </script>
        <style>
            body {
                margin: 2rem 0.5rem;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, sans-serif;
            }

            main {
                max-width: 720px;
                margin: auto;
            }

            .editor-wrap.enabled {
                border-radius: 3px;
                border: 1px solid #ccc;
            }

            .editor-content {
                transition: min-height 0.25s, padding-bottom 0.25s;
                min-height: 0;
                outline: none;
                border-radius: 1px;
                transition: box-shadow 0.15s, background-color 0.15s;
            }

            .editor-content > *:first-child {
                margin-top: 0;
            }

            .editor-content > *:last-child {
                margin-bottom: 0;
            }

            .editor-wrap#editor-description-wrap:hover:not(.enabled) .editor-content {
                background-color: #efefef;
                box-shadow: 0 0 0 6px #efefef;
            }

            .editor-wrap.enabled .editor-content {
                min-height: 55px;
                padding: 1rem;
            }

            .editor-toolbar {
                padding: 3px;
                display: none;
                border-bottom: 1px solid #ccc;
                z-index: 1;
            }

            .editor-wrap.enabled .editor-toolbar {
                display: block;
            }

            .editor-footer {
                margin: 1rem;
                display: none;
            }

            .editor-wrap.enabled .editor-footer {
                display: block;
            }

            .editor-save-btn {
                background: #207ab7;
                font-size: 16px;
                font-weight: bold;
                color: #fff;
                border: none;
                outline: none;
                height: 34px;
                line-height-step: 34px;
                padding: 0 16px;
                border-radius: 4px;
                margin: 0;
                -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                -webkit-user-select: none;
            }

            .editor-cancel-btn {
                background: #dfe3ec;
                font-size: 16px;
                font-weight: bold;
                color: #17224f;
                border: none;
                outline: none;
                height: 34px;
                line-height-step: 34px;
                padding: 0 16px;
                border-radius: 4px;
                margin: 0;
                -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                -webkit-user-select: none;
            }

            .editor-label {
                font-size: 19px;
                margin: 2rem 0 1rem;
                display: block;
            }

            .comment {
                display: flex;
            }

            .avatar {
                background-color: #ff9999;
                color: #17224f;
                font-size: 18px;
                font-weight: bold;
                border-radius: 100px;
                width: 42px;
                height: 42px;
                line-height: 42px;
                text-align: center;
                margin-right: 1rem;
            }

            .editor-wrap#editor-comment-wrap {
                border: 1px solid #ccc;
                border-radius: 3px;
                flex-grow: 1;
            }

            .editor-wrap#editor-comment-wrap .editor-content {
                padding: 1rem;
            }

            /* Below are the content styles, in this case they are global */
            /* and not namespaced to `.editor-content` */
            a,
            a:link {
                color: blue;
            }
            a:visited {
                color: purple;
            }
            a:hover {
                color: green;
            }
            a:active {
                color: red;
            }

            h1 {
                font-size: 1.75rem;
                font-weight: strong;
            }

            h2 {
                font-size: 1.5rem;
                font-weight: strong;
            }

            h3 {
                font-size: 1rem;
                font-weight: strong;
            }

            hr {
                margin: 1.5rem 0;
                padding: 0.5rem 0;
                border: 0;
            }

            hr::after {
                content: '';
                display: block;
                border-width: 0 0 1px 0;
                border-color: #ccc;
                border-style: solid;
            }

            img {
                max-width: 100%;
                height: auto;
            }

            /* Remove borders from toolbar - delete this rule if using premium skins and icons */
            .editor-toolbar .tox.tox-tinymce-inline .tox-editor-header {
                border: none;
            }

            /* Indent placeholder in custom-styled comment editor */
            #editor-comment-wrap .mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {
                left: inherit;
            }
        </style>

    </head>
    <body>
        <main>
            <label class="editor-label">Description</label>
            <div class="editor-wrap" id="editor-description-wrap">
                <div class="editor-toolbar" id="editor-description-toolbar"></div>
                <div class="editor-content" id="editor-description-content"></div>
            </div>
        </main>
    </body>
</html>

Part 2 Build

How to create a Comments Editor using a common config

TL;DR

A Comments Editor (not to be confused with TinyMCE’s Comments plugin) lets your users discuss a task in your Project Management Platform.

In order to follow DRY (Don’t Repeat Yourself) and keep code easy to maintain, we’ll use a common config, which contains configuration options common to both editors.

Important: While this tutorial shows you how to create a WYSIWYG editor that’s perfect for adding comments, it does not show you how to save the comments or post them back to your task screen.

The Comments Editor

1. The common config


Why build a common config?

When you need multiple editor instances (such as a Description Editor and a Comments Editor) which share some common settings (for example what plugins that are enabled), you can create one “common config” to manage those common configuration options. Then, when you initialize each editor, you only have to specify configuration options specific to that editor (such as the HTML selector), and load the rest of the config options from the common config.

Here’s how it works on a conceptual level:

The general common configuration

And here’s how it will work with our Description Editor and Comments Editor:

The Workflow common configuration

How to set up the common config

1. At the moment, there is a single TinyMCE configuration set up for the Description Editor.

//1.1 Set up the description editor
tinymce.init({
  selector: "#editor-description-content",
  plugins:
    "codesample editimage emoticons image media mediaembed powerpaste code link autolink tinymcespellchecker autocorrect linkchecker lists checklist table advtable advcode",
  menubar: false,
  inline: true,
  toolbar_persist:true,
  toolbar:
    "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons | table codesample hr blockquote | code ",
  placeholder: "Add a description",

  //1.2 Improve communication
  image_file_types: "jpeg jpg png gif",
  editimage_toolbar: "rotateleft rotateright",
  object_resizing: false,
  codesample_global_prismjs: true,
  codesample_languages: [
    { text: "HTML/XML", value: "markup" },
    { text: "JavaScript", value: "javascript" },
    { text: "CSS", value: "css" },
    { text: "LESS", value: "less" },
    { text: "PHP", value: "php" },
    { text: "SQL", value: "sql" },
    { text: "Python", value: "python" }
  ],

  //1.3 Maximise Productivity
  link_default_target: "_blank",
  spellchecker_language: "en",
  spellchecker_active: true
});

2. Change to a common config by introducing the “commonConfig” variable, and move the plugins into it:

let commonConfig = {
  
  plugins: "codesample editimage emoticons image media mediaembed powerpaste code link autolink tinymcespellchecker autocorrect linkchecker lists checklist table advtable advcode",

}

3. Move the menubar, inline, and toolbar_persist options into the common config:

let commonConfig = {
  plugins: "codesample editimage emoticons image media mediaembed powerpaste code link autolink tinymcespellchecker autocorrect linkchecker lists checklist table advtable advcode",
  menubar: false,
  inline: true,
  toolbar_persist: true,

}

4. Move the object_resizing option from the communications group into the common config, and the spellchecker options from the productivity group:

let commonConfig = {
  plugins:
    "advcode advtable autocorrect autolink checklist codesample editimage emoticons image link linkchecker lists media mediaembed powerpaste table tinymcespellchecker",
  menubar: false,
  inline: true,
  toolbar_persist: true,
  object_resizing: false,

  //1.2 Improve communication
  object_resizing: false,

  //1.3 Maximise Productivity
  spellchecker_language: "en",
  spellchecker_active: true
};

5. Check on the Description Editor tinymce init script. It should now resemble this configuration:

tinymce.init({
  selector: "#editor-description-content",
  toolbar:
    "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons table codesample hr blockquote | code ",
  fixed_toolbar_container: "#editor-description-toolbar",
  placeholder: "Add a description",
  
  //1.2 Improve communication
  image_file_types: "jpeg jpg png gif",
  editimage_toolbar: "rotateleft rotateright",
  codesample_global_prismjs: true,
  codesample_languages: [
    { text: "HTML/XML", value: "markup" },
    { text: "JavaScript", value: "javascript" },
    { text: "CSS", value: "css" },
    { text: "LESS", value: "less" },
    { text: "PHP", value: "php" },
    { text: "SQL", value: "sql" }
  ],

});

6. Add to the Description Editor JavaScript the following content: a reference to the editor-description-toolbar id in the editor HTML, and a reference to the the new common config:

tinymce.init({
  selector: "#editor-description-content",
  toolbar:
    "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons table codesample hr blockquote | code ",
  fixed_toolbar_container: "#editor-description-toolbar", //reference the toolbar id
  placeholder: "Add a description",
  
  //1.2 Improve communication
  image_file_types: "jpeg jpg png gif",
  editimage_toolbar: "rotateleft rotateright",
  codesample_global_prismjs: true,
  codesample_languages: [
    { text: "HTML/XML", value: "markup" },
    { text: "JavaScript", value: "javascript" },
    { text: "CSS", value: "css" },
    { text: "LESS", value: "less" },
    { text: "PHP", value: "php" },
    { text: "SQL", value: "sql" }
  ],

//Include the Common Config reference


  ...commonConfig
});

7. Save the changes, and check on the description editor:

The Description Editor working with a common config

2. Creating the Comments Editor


Now that the common config is setup and the Description Editor is loading it successfully, you can create the Comments Editor. As a reminder, the Comments Editor provides a WYSIWYG interface where users can discuss tasks and ask questions. It allows users to comment on task descriptions, and helps to move the project along at a speedier pace.

1. Add the Comments Editor tinymce.init content:

tinymce.init({
  selector: "#editor-comment-content",

2. Include the toolbar plugins list, and the fixed toolbar container option:

tinymce.init({
  selector: "#editor-comment-content",
  toolbar:
    "bold italic forecolor backcolor | numlist bullist | link image emoticons codesample blockquote ",
  fixed_toolbar_container: "#editor-comment-toolbar"

3. Include the placeholder option to prompt comments:

placeholder: "Add a comment...",

4. Add a reference to the new common config:

  ...commonConfig
});

5. Add some HTML to the editor for the comments content:

   <label class="editor-label">Comments</label>
    <div class="comment">
      <div class="avatar">FD</div>
      <div class="editor-wrap" id="editor-comment-wrap">
        <div class="editor-content" id="editor-comment-content"></div>
        <div class="editor-toolbar" id="editor-comment-toolbar"></div>
        <footer class="editor-footer">
        </footer>
      </div>
    </div>

6. Save the changes, and check on the new Comments Editor:

The Comments Editor and Description Editor working with the common config

The complete Comments Editor

And the following is the complete code:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>
            let commonConfig = {
                plugins: "advcode advtable autocorrect autolink checklist codesample editimage emoticons image link linkchecker lists media mediaembed powerpaste table tinymcespellchecker",
                menubar: false,
                inline: true,
                toolbar_persist: true,
                object_resizing: false,

                // 1.2 Improve communication
                object_resizing: false,

                // 1.3 Maximise Productivity
                spellchecker_language: "en",
                spellchecker_active: true
            };

            // Description Editor
            tinymce.init({
                selector: "#editor-description-content",
                toolbar: "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons table codesample hr blockquote | code ",
                fixed_toolbar_container: "#editor-description-toolbar",
                placeholder: "Add a description",

                // 1.2 Improve communication
                image_file_types: "jpeg jpg png gif",
                editimage_toolbar: "rotateleft rotateright",
                codesample_global_prismjs: true,
                codesample_languages: [
                    {
                        text: "HTML/XML",
                        value: "markup"
                    },
                    {
                        text: "JavaScript",
                        value: "javascript"
                    },
                    {
                        text: "CSS",
                        value: "css"
                    },
                    {
                        text: "LESS",
                        value: "less"
                    }, {
                        text: "PHP",
                        value: "php"
                    }, {
                        text: "SQL",
                        value: "sql"
                    }
                ],

                setup: (editor) => {
                    editor.on("focus", () => {
                        document.getElementById("editor-description-wrap").classList.add("enabled");
                    });
                },

                ... commonConfig
            });

            // Comments Editor
            tinymce.init({
                selector: "#editor-comment-content",
                toolbar: "bold italic forecolor backcolor | numlist bullist | link image emoticons codesample blockquote ",
                fixed_toolbar_container: "#editor-comment-toolbar",
                placeholder: "Add a comment...",

                setup: (editor) => {
                    editor.on("focus", () => {
                        document.getElementById("editor-comment-wrap").classList.add("enabled");
                    });
                },

                ... commonConfig
            });
        </script>
        <style>
            body {
                margin: 2rem 0.5rem;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
            }

            main {
                max-width: 720px;
                margin: auto;
            }

            .editor-label {
                font-size: 19px;
                margin: 2rem 0 1rem;
                display: block;
                font-weight: bold;
                color: #333;
            }

            /* Below are the content styles, in this case they are global */
            /* and not namespaced to `.editor-content` */
            a,
            a:link {
                color: blue;
            }
            a:visited {
                color: purple;
            }
            a:hover {
                color: green;
            }
            a:active {
                color: red;
            }

            h1 {
                font-size: 1.75rem;
                font-weight: strong;
            }

            h2 {
                font-size: 1.5rem;
                font-weight: strong;
            }

            h3 {
                font-size: 1rem;
                font-weight: strong;
            }

            hr {
                margin: 1.5rem 0;
                padding: 0.5rem 0;
                border: 0;
            }

            hr::after {
                content: "";
                display: block;
                border-width: 0 0 1px 0;
                border-color: #ccc;
                border-style: solid;
            }

            img {
                max-width: 100%;
                height: auto;
            }

            .editor-label {
                font-size: 19px;
                margin: 2rem 0 1rem;
                display: block;
                font-weight: bold;
                color: #333;
            }

            .comment {
                display: flex;
            }

            .avatar {
                background-color: #ff9999;
                color: #17224f;
                font-size: 18px;
                font-weight: bold;
                border-radius: 100px;
                width: 42px;
                height: 42px;
                line-height: 42px;
                text-align: center;
                margin-right: 1rem;
            }

            .editor-wrap#editor-comment-wrap {
                border: 1px solid #ccc;
                border-radius: 3px;
                flex-grow: 1;
            }

            .editor-wrap#editor-comment-wrap .editor-content {
                padding: 1rem;
            }
        </style>

    </head>
    <body>
        <main>
            <label class="editor-label">Description</label>
            <div class="editor-wrap" id="editor-description-wrap">
                <div class="editor-toolbar" id="editor-description-toolbar"></div>
                <div class="editor-content" id="editor-description-content">
                    <p>Hello world!</p>
                    <p>Here is some content for our description editor.</p>
                </div>
            </div>

            <label class="editor-label">Comments</label>
            <div class="comment">
                <div class="avatar">FD</div>
                <div class="editor-wrap" id="editor-comment-wrap">
                    <div class="editor-content" id="editor-comment-content"></div>
                    <div class="editor-toolbar" id="editor-comment-toolbar"></div>
                    <footer class="editor-footer"></footer>
                </div>
            </div>
        </main>
    </body>
</html>

Part 3 Build

How to create a UI for the Project Management Platform

TL;DR

A great User Interface (UI) adds to the overall efficiency of a WYSIWYG project management editing experience.

Adding specific JavaScript and CSS to make the editor unobtrusive, enhances an effective UI, while adding code, along with some demo HTML, demonstrates how effective it can be.

Gains in productivity and efficiency are the key areas targeted by a Project Management Platform, and many of these time-saving options come from the WYSIWYG editing experience. By adding some UI subtlety around the WYSIWYG, it contributes further gains, as well as improving the level of satisfaction that your users experience when using your project management software.

Creating a UI

editor.on()

fixed_toolbar_container

a. editor.on() function


What the editor.on() function does

It’s a function used for binding a callback to an editor event. In this case, the editor event is the user clicking in the text area. The callback function retrieves the Description Editor by its ID value, and then adds a new class to it. In this case, the custom class that shows the editor when the text area gets focus from the user clicking on it.

How to set it up

1. Add the setup editor option, and place the editor.on() function to the Description Editor init script, and set the editor event to “focus”:

 setup: (editor) => {
        editor.on('focus', () => {
        });
      },

2. Expand the function to include the document.getElementById method. This targets the Description Editor class.

 setup: (editor) => {
        editor.on('focus', () => {
          document.getElementById('editor-description-wrap')
        });
      },

3. Add the class that’ll be written into the Description Editor element when the editor focus event happens:

 setup: (editor) => {
        editor.on('focus', () => {
          document.getElementById('editor-description-wrap').classList.add('enabled');
        });
      },

4. Save the changes.

5. Continue to the Comments Editor, and include a copy of the function within the Comments Editor configuration:

 setup: (editor) => {
          editor.on('focus', () => {
            document.getElementById('editor-description-wrap').classList.add('enabled');
          });
        },

6. Change the ID in the function to target the comments Id in place of the description id:

 setup: (editor) => {
          editor.on('focus', () => {
            document.getElementById('editor-comment-wrap').classList.add('enabled');
          });
        },

7. Save the changes.

b. fixed_toolbar_container option


This allows the editor to render in an HTML element, and appear in a fixed position on the page. Bear in mind that this option only works when the TinyMCE editor is using inline mode.

How to set it up

1. In the Description Editor configuration, add the fixed toolbar container option into the configuration:

fixed_toolbar_container: '',

2. Set the vale of the option to target the ID that the Description Editor uses:

fixed_toolbar_container: '#editor-description-toolbar',

3. Save the changes

4. For the Comments Editor, include the fixed toolbar container within the configuration.

fixed_toolbar_container: ''

5. Set the value to target the Comments Editor ID:

fixed_toolbar_container: '#editor-comment-toolbar'

6. Save the changes

Adding CSS and adding JavaScript

Configuring the project management JavaScript

To configure the custom JavaScript, start with the button components for saving task descriptions and comments:

1. Include the button HTML into the Project Management Platform. This button HTML adds a Save button, and a Cancel button to the Description Editor

    <footer class="editor-footer">
        <button type="button" id="save-description-btn" onclick="save('editor-description-wrap')" class="editor-save-btn">Save</button>
        <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
    </footer>

2. Add the buttons to the Comments Editor also:

    <footer class="editor-footer">
        <button type="button" id="save-description-btn" onclick="alert('Your own custom Save function')" class="editor-save-btn">Save</button>
        <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
    </footer>

3. Add some JavaScript inside the demo script tags, not inside the Description or Comment configuration, that will close and disable the editor when the save button receives a click event:

    function save(id) {
      document.getElementById(id).classList.remove('enabled');
    }

4. Save the changes

Note: These buttons do not actually save and cancel content – they are placeholders. In production, the actual save and cancel functionality would need to be planned and implemented.

Configuring the project management CSS

1. Add a hover effect for the editors by configuring the following CSS:

    .editor-wrap.enabled {
        border-radius: 3px;
        border: 1px solid #ccc;
    }

    .editor-content {
        transition: min-height 0.25s, padding-bottom 0.25s;
        min-height: 0;
        outline: none;
        border-radius: 1px;
        transition: box-shadow 0.15s, background-color 0.15s;
    }

    .editor-content > *:first-child {
        margin-top: 0;
    }

    .editor-content > *:last-child {
        margin-bottom: 0;
    }

    .editor-wrap#editor-description-wrap:hover:not(.enabled) .editor-content {
        background-color: #efefef;
        box-shadow: 0 0 0 6px #efefef;
    }

    .editor-wrap.enabled .editor-content {
        min-height: 55px;
        padding: 1rem;
    }

2. Include CSS that works with the JavaScript installed in the preceding steps, hiding the toolbar by default on both editors, and only showing them when they receive focus:

    .editor-toolbar {
        padding: 3px;
        display: none;
        border-bottom: 1px solid #ccc;
        z-index: 1;
    }

    .editor-wrap.enabled .editor-toolbar {
        display: block;
    }

3. Add some CSS to hide the save and cancel placeholder buttons by default on both editors, showing them only when the editor has focus:

    .editor-footer {
        margin: 1rem;
        display: none;
    }

    .editor-wrap.enabled .editor-footer {
        display: block;
    }

4. Include CSS that changes the position of the placeholder text within the editors:

    .editor-wrap.enabled .editor-footer {
        display: block;
    }

    #editor-comment-wrap .mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before,
    #editor-description-wrap .mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {
        left: inherit;
    }

5. Add CSS that removes borders from the toolbar. This CSS is not necessary if you’re using a premium skin and icon pack:

    editor-toolbar .tox.tox-tinymce-inline .tox-editor-header {
        border: none;
    }

6. Save the changes, and check on the final results. This content uses the demo content from the TinyMCE solutions page:

Changing HTML sample content for your Project Management Platform

Add some additional HTML demo content to see how the project works with different tasks and comments:

<main>
    <label class="editor-label">Description</label>
    <div class="editor-wrap" id="editor-description-wrap">
        <div class="editor-toolbar" id="editor-description-toolbar"></div>
        <div class="editor-content" id="editor-description-content">
            <p>In the admin panel, create a screen to identify employee records with malformed email addresses, so HR teams can update them.</p>
            <p>
                <strong>SQL query provided by DB Admin:</strong>
            </p>
            <pre class="language-sql">
                <code>
                    SELECT
                    FirstName, LastName, Team, Email
                    FROM
                    Employee
                    where NOT REGEXP_LIKE(Email, &lsquo;[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}&rsquo;, &lsquo;i&rsquo;);
                </code>
            </pre>
            <p>
                <strong>Sub-tasks:</strong>
            </p>
            <ul class="tox-checklist">
                <li class="tox-checklist--checked">Tech spec</li>
                <li class="tox-checklist--checked">Database query</li>
                <li>Implement screen</li>
                <li>QA/Testing</li>
            </ul>
            <p>
                <strong>Key Stakeholders:</strong>
            </p>
            <table style="border-collapse: collapse; width: 100%;" border="1">
                <colgroup>
                    <col style="width: 33.2362%;">
                    <col style="width: 33.2362%;">
                    <col style="width: 33.2362%;">
                </colgroup>
                <tbody>
                    <tr>
                        <td>
                            <strong>Stakeholder</strong>
                        </td>
                        <td>
                            <strong>Job Title</strong>
                        </td>
                        <td>
                            <strong>Role</strong>
                        </td>
                    </tr>
                    <tr>
                        <td>James Carr</td>
                        <td>Product Manager</td>
                        <td>Accountable</td>
                    </tr>
                    <tr>
                        <td>Valerie Mack</td>
                        <td>Full Stack Engineer</td>
                        <td>Responsible</td>
                    </tr>
                    <tr>
                        <td>Tim Bird</td>
                        <td>Customer Success</td>
                        <td>Consult</td>
                    </tr>
                </tbody>
            </table>
        </div>
        <footer class="editor-footer">
            <button type="button" id="save-description-btn" onclick="save('editor-description-wrap')" class="editor-save-btn">Save</button>
            <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
        </footer>
    </div>

    <label class="editor-label">Comments</label>
    <div class="comment">
        <div class="avatar">FD</div>
        <div class="editor-wrap" id="editor-comment-wrap">
            <div class="editor-content" id="editor-comment-content"></div>
            <div class="editor-toolbar" id="editor-comment-toolbar"></div>
            <footer class="editor-footer">
                <button type="button" id="save-description-btn" onclick="alert('Your own custom Save function')" class="editor-save-btn">Save</button>
                <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
            </footer>
        </div>
    </div>
</main>

The complete project management Task Screen Editor

With the CSS and JavaScript content completed, the following demo shows the preceding HTML example running within the Description and Comments Editors to form the completed Task Screen Editor for a Project Management Platform:

Check out the complete code sample:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>
            // TinyMCE Workflow Starter Config
            // Quick start video - https://www.youtube.com/watch?v=ApN8R43PKvA

            // Since we are using more than one editor, we can create common config
            // options to be shared between both editor instances
            let commonConfig = {

                // Tip - To keep TinyMCE lean, only include the plugins you need
                plugins: "advcode advtable autocorrect autolink checklist codesample editimage emoticons image link linkchecker lists media mediaembed powerpaste table tinymcespellchecker",

                // Hide the menubar to keep the editor minimal
                // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#menubar
                menubar: false,

                // Enable inline mode
                // https://www.tiny.cloud/docs/tinymce/6/use-tinymce-inline/
                inline: true,

                // Make the inline toolbar not disappear when the editor blurs
                // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#toolbar_persist
                toolbar_persist: true,

                // Customize the look and feel of the UI using premium skins and icons.
                // https://www.tiny.cloud/docs/tinymce/6/editor-icons/
                // https://www.tiny.cloud/docs/tinymce/6/editor-skin/
                //
                // The icons and skin options are disabled by default in this config, but can
                // be enabled by uncommenting the lines below. In order for them to load properly,
                // you must be on a premium plan or trial, and load TinyMCE from the cloud or be
                // running a fully self-hosted deployment.
                //
                // icons: 'thin',
                // skin: 'naked',

                // Remove the manual image resizing as we're using percentage widths
                // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#toolbar_persist
                object_resizing: false,

                // Set the spellchecker language and make it active on load
                // https://www.tiny.cloud/docs/tinymce/6/introduction-to-tiny-spellchecker/#spellchecker_active
                spellchecker_language: 'en',
                spellchecker_active: true

            }

            // Here is the init function for the first "Description" editor
            tinymce.init({

                // Select the element(s) to add TinyMCE to using any valid CSS selector
                selector: "#editor-description-content",

                // This option allows you to specify the buttons and the order that they
                // will appear on TinyMCE's toolbar
                // https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/
                toolbar: "blocks | bold italic forecolor backcolor | numlist bullist checklist | link image emoticons table codesample hr blockquote | code ",

                // Render the toolbar in this container
                // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#fixed_toolbar_container
                fixed_toolbar_container: '#editor-description-toolbar',

                // Set a placeholder to be shown when there is no content in the editor
                // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#placeholder
                placeholder: "Add a description",

                // Strip down the Enhanced Image Editing toolbar to basic manipulations
                // https://www.tiny.cloud/docs/tinymce/6/editimage/#toolbar-buttons
                editimage_toolbar: "rotateleft rotateright",

                codesample_global_prismjs: true,
                codesample_languages: [
                    {
                        text: 'HTML/XML',
                        value: 'markup'
                    },
                    {
                        text: 'JavaScript',
                        value: 'javascript'
                    },
                    {
                        text: 'CSS',
                        value: 'css'
                    },
                    {
                        text: 'LESS',
                        value: 'less'
                    }, {
                        text: 'PHP',
                        value: 'php'
                    }, {
                        text: 'SQL',
                        value: 'sql'
                    },
                ],

                setup: (editor) => {
                    // Apply a custom class to the wrapper element when the editor gets focus
                    // https://www.tiny.cloud/docs/advanced/events/
                    editor.on('focus', () => {
                        document.getElementById('editor-description-wrap').classList.add('enabled');
                    });
                },

                // Load the common configuration options specified earlier
                ... commonConfig
            });

            // Here is the init function for the second "Comment" editor
            tinymce.init({

                // Select the element(s) to add TinyMCE to using any valid CSS selector
                selector: "#editor-comment-content",

                // This option allows you to specify the buttons and the order that they
                // will appear on TinyMCE's toolbar.
                // https://www.tiny.cloud/docs/tinymce/6/toolbar-configuration-options/
                toolbar: "bold italic forecolor backcolor | numlist bullist | link image emoticons codesample blockquote ",

                // Set a placeholder to display a text when there are no content in the editor
                // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#placeholder
                placeholder: "Add a comment...",

                // Render the toolbar in this container.
                // https://www.tiny.cloud/docs/tinymce/6/menus-configuration-options/#fixed_toolbar_container
                fixed_toolbar_container: '#editor-comment-toolbar',

                setup: (editor) => {
                    // Apply a custom class to the wrapper element when the editor gets focus
                    // https://www.tiny.cloud/docs/advanced/events/
                    editor.on('focus', () => {
                        document.getElementById('editor-comment-wrap').classList.add('enabled');
                    });
                },

                // Load the common configuration options specified earlier
                ... commonConfig
            });

            // Save button behavior to disable the editor
            function save(id) {
                document.getElementById(id).classList.remove('enabled');
            }

            // Next step: Check out Tiny Drive for easy cloud storage of your users'
            // images and media. Integrates seamlessly with TinyMCE.
            // https://www.tiny.cloud/drive/
        </script>
        <style>
            body {
                margin: 2rem 0.5rem;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', Helvetica, Arial, sans-serif;
            }

            main {
                max-width: 720px;
                margin: auto;
            }

            .editor-wrap.enabled {
                border-radius: 3px;
                border: 1px solid #ccc;
            }

            .editor-content {
                transition: min-height 0.25s, padding-bottom 0.25s;
                min-height: 0;
                outline: none;
                border-radius: 1px;
                transition: box-shadow 0.15s, background-color 0.15s;
            }

            .editor-content > *:first-child {
                margin-top: 0;
            }

            .editor-content > *:last-child {
                margin-bottom: 0;
            }

            .editor-wrap#editor-description-wrap:hover:not(.enabled) .editor-content {
                background-color: #efefef;
                box-shadow: 0 0 0 6px #efefef;
            }

            .editor-wrap.enabled .editor-content {
                min-height: 55px;
                padding: 1rem;
            }

            .editor-toolbar {
                padding: 3px;
                display: none;
                border-bottom: 1px solid #ccc;
                z-index: 1;
            }

            .editor-wrap.enabled .editor-toolbar {
                display: block;
            }

            .editor-footer {
                margin: 1rem;
                display: none;
            }

            .editor-wrap.enabled .editor-footer {
                display: block;
            }

            .editor-save-btn {
                background: #207ab7;
                font-size: 16px;
                font-weight: bold;
                color: #fff;
                border: none;
                outline: none;
                height: 34px;
                line-height-step: 34px;
                padding: 0 16px;
                border-radius: 4px;
                margin: 0;
                -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                -webkit-user-select: none;
            }

            .editor-cancel-btn {
                background: #dfe3ec;
                font-size: 16px;
                font-weight: bold;
                color: #17224f;
                border: none;
                outline: none;
                height: 34px;
                line-height-step: 34px;
                padding: 0 16px;
                border-radius: 4px;
                margin: 0;
                -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
                -webkit-user-select: none;
            }

            .editor-label {
                font-size: 19px;
                margin: 2rem 0 1rem;
                display: block;
            }

            .comment {
                display: flex;
            }

            .avatar {
                background-color: #ff9999;
                color: #17224f;
                font-size: 18px;
                font-weight: bold;
                border-radius: 100px;
                width: 42px;
                height: 42px;
                line-height: 42px;
                text-align: center;
                margin-right: 1rem;
            }

            .editor-wrap#editor-comment-wrap {
                border: 1px solid #ccc;
                border-radius: 3px;
                flex-grow: 1;
            }

            .editor-wrap#editor-comment-wrap .editor-content {
                padding: 1rem;
            }

            /* Below are the content styles, in this case they are global */
            /* and not namespaced to `.editor-content` */
            a,
            a:link {
                color: blue;
            }
            a:visited {
                color: purple;
            }
            a:hover {
                color: green;
            }
            a:active {
                color: red;
            }

            h1 {
                font-size: 1.75rem;
                font-weight: strong;
            }

            h2 {
                font-size: 1.5rem;
                font-weight: strong;
            }

            h3 {
                font-size: 1rem;
                font-weight: strong;
            }

            hr {
                margin: 1.5rem 0;
                padding: 0.5rem 0;
                border: 0;
            }

            hr::after {
                content: '';
                display: block;
                border-width: 0 0 1px 0;
                border-color: #ccc;
                border-style: solid;
            }

            img {
                max-width: 100%;
                height: auto;
            }

            /* Remove borders from toolbar - delete this rule if using premium skins and icons */
            .editor-toolbar .tox.tox-tinymce-inline .tox-editor-header {
                border: none;
            }

            /* Indent placeholder in custom-styled comment editor */
            #editor-comment-wrap .mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before {
                left: inherit;
            }
        </style>

    </head>
    <body>
        <main>
            <label class="editor-label">Description</label>
            <div class="editor-wrap" id="editor-description-wrap">
                <div class="editor-toolbar" id="editor-description-toolbar"></div>
                <div class="editor-content" id="editor-description-content">
                    <p>In the admin panel, create a screen to identify employee records with malformed email addresses, so HR teams can update them.</p>
                    <p>
                        <strong>SQL query provided by DB Admin:</strong>
                    </p>
                    <pre class="language-sql">
                        <code>
                            SELECT
                            FirstName, LastName, Team, Email
                            FROM
                            Employee
                            where NOT REGEXP_LIKE(Email, &lsquo;[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}&rsquo;, &lsquo;i&rsquo;);
                        </code>
                    </pre>
                    <p>
                        <strong>Sub-tasks:</strong>
                    </p>
                    <ul class="tox-checklist">
                        <li class="tox-checklist--checked">Tech spec</li>
                        <li class="tox-checklist--checked">Database query</li>
                        <li>Implement screen</li>
                        <li>QA/Testing</li>
                    </ul>
                    <p>
                        <strong>Key Stakeholders:</strong>
                    </p>
                    <table style="border-collapse: collapse; width: 100%;" border="1">
                        <colgroup>
                            <col style="width: 33.2362%;">
                            <col style="width: 33.2362%;">
                            <col style="width: 33.2362%;">
                        </colgroup>
                        <tbody>
                            <tr>
                                <td>
                                    <strong>Stakeholder</strong>
                                </td>
                                <td>
                                    <strong>Job Title</strong>
                                </td>
                                <td>
                                    <strong>Role</strong>
                                </td>
                            </tr>
                            <tr>
                                <td>James Carr</td>
                                <td>Product Manager</td>
                                <td>Accountable</td>
                            </tr>
                            <tr>
                                <td>Valerie Mack</td>
                                <td>Full Stack Engineer</td>
                                <td>Responsible</td>
                            </tr>
                            <tr>
                                <td>Tim Bird</td>
                                <td>Customer Success</td>
                                <td>Consult</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                <footer class="editor-footer">
                    <button type="button" id="save-description-btn" onclick="save('editor-description-wrap')" class="editor-save-btn">Save</button>
                    <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
                </footer>
            </div>
        </div>

        <label class="editor-label">Comments</label>
        <div class="comment">
            <div class="avatar">FD</div>
            <div class="editor-wrap" id="editor-comment-wrap">
                <div class="editor-content" id="editor-comment-content"></div>
                <div class="editor-toolbar" id="editor-comment-toolbar"></div>
                <footer class="editor-footer">
                    <button type="button" id="save-description-btn" onclick="alert('Your own custom Save function')" class="editor-save-btn">Save</button>
                    <button type="button" onclick="alert('Your own custom Cancel function')" class="editor-cancel-btn">Cancel</button>
                </footer>
            </div>
        </div>
    </body>
</html>

A WYSIWYG Task Screen Editor that improves productivity

Now that your project management WYSIWYG editing experience is set up and working, it’s ready to integrate into your app. The next steps are to investigate and add further functionality to the “Cancel” and “Save” buttons. For instance, you can configure them with your app’s development framework to save the contents of the Description Editor and Comments Editor to your database.

If you’re looking for more information on Project Management and Workflow software and TinyMCE, read more on the Project Management solutions page. For a direct query, contact us to find the answer.

Get your FREE TinyMCE API key today for your CRM

Joe Robinson

Dev Advocate | Tech Writer at Tiny

Technical and creative writer, editor, and a TinyMCE advocate. An enthusiast for teamwork, open source software projects, and baking. Can often be found puzzling over obscure history, cryptic words, and lucid writing.

Di Mace

Marketing Communications Manager

Messaging strategist and copywriter whose passion lies in working with brands like Tiny, that have deep-seated values and embrace the power of their story. She gets a kick out of solving problems, loves learning new things and making stuff, every day. When she’s not thinking through clever copy lines or clarifying value propositions, she’s knitting amazing socks for everyone she knows.

John Rau

Marketing Manager at Tiny

A former developer, John works on the Marketing team at Tiny. When he's not spreading the word about TinyMCE, he enjoys taking things apart and *trying* to put them back together (including his house and anything else that looks interesting).

Related Articles

  • How-to Use TinyMCEOct 10th, 2024

    How to Use AI Prompts for Content Creation in TinyMCE

Join 100,000+ developers who get regular tips & updates from the Tiny team.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Tiny logo

Stay Connected

SOC2 compliance badge

Products

TinyMCEDriveMoxieManager
© Copyright 2024 Tiny Technologies Inc.

TinyMCE® and Tiny® are registered trademarks of Tiny Technologies, Inc.