Implementing App Locations: JSON RTE

The JSON Rich Text Editor Plugins lets you add/create custom plugins to extend the functionality of your JSON Rich Text Editor as per your needs. You can use third-party applications to interact with your JSON Rich Text Editor content.

Use Case

With prebuilt plugins such as Highlight, Info Panel, and Word Count, you can enhance the JSON Rich Text Editor (JSON RTE) field. Instead of using separate custom fields to interact with the content within the JSON RTE, create and add plugins to your JSON RTE for real-time adaptiveness.

Exercise 8

For this exercise you will need to navigate to a different location in the project directory to work on the JSON RTE plugin code. Once you complete the exercise a compiled file with the plugin will be placed in the public folder of the main project and will be served as defined in the JSON RTE location path definition. 

In this exercise you will create a plugin that will capitalize the text in the JSON RTE, similar to the logic implemented for the Field Modifier location.

  • First navigate into the [project-root]/rte/src folder, that's where the core of the work will be done for this exercise.

  • Next, in that folder, create a new file under the rte/src folder, and name it academy.tsx

  • Copy the following code into the file contents:

import <span>{</span> cloneDeep <span>}</span> from <span>"lodash"</span><span>;</span>
 
<span>export</span> <span>const</span> onClickHandler <span>=</span> <span>(</span>rte<span>:</span> any<span>)</span> <span>=&gt;</span> <span>{</span>
<span>const</span> getNode <span>=</span> rte<span>?</span>.<span>getNode</span><span>(</span><span>[</span><span>0</span><span>]</span><span>)</span><span>;</span>
let rteData<span>:</span> any <span>=</span> <span>[</span><span>]</span><span>;</span>
 
<span>const</span> findRteObj <span>=</span> <span>(</span>obj<span>:</span> any<span>)</span> <span>=&gt;</span> <span>{</span>
<span>for</span> <span>(</span>let key in obj<span>)</span> <span>{</span>
<span>if</span> <span>(</span>typeof obj<span>[</span>key<span>]</span> <span>===</span> <span>"object"</span> <span>&amp;&amp;</span> obj<span>[</span>key<span>]</span> <span>!</span><span>==</span> null<span>)</span> <span>{</span>
<span>if</span> <span>(</span>obj<span>[</span>key<span>]</span><span>?</span>.<span>type</span> <span>===</span> <span>"p"</span><span>)</span> <span>{</span>
rteData.<span>push</span><span>(</span>obj<span>[</span>key<span>]</span><span>)</span><span>;</span>
<span>}</span> <span>else</span> <span>{</span>
findRteObj<span>(</span>obj<span>[</span>key<span>]</span><span>)</span><span>;</span>
<span>}</span>
<span>}</span>
<span>}</span>
<span>}</span><span>;</span>
 
findRteObj<span>(</span>getNode<span>)</span><span>;</span>
 
<span>const</span> path <span>=</span> rteData<span>?</span>.<span>map</span><span>(</span><span>(</span>path<span>:</span> any<span>)</span> <span>=&gt;</span> <span>{</span>
<span>return</span> rte<span>?</span>.<span>getPath</span><span>(</span>path<span>)</span><span>;</span>
<span>}</span><span>)</span><span>;</span>
 
<span>const</span> rteDataCopy <span>=</span> cloneDeep<span>(</span>rteData<span>)</span><span>;</span>
 
rteDataCopy.<span>forEach</span><span>(</span><span>(</span>element<span>:</span> any<span>)</span> <span>=&gt;</span> <span>{</span>
element<span>?</span>.<span>children</span><span>?</span>.<span>forEach</span><span>(</span><span>(</span>j<span>:</span> any<span>)</span> <span>=&gt;</span> <span>{</span>
j.<span>text</span> <span>=</span> j<span>?</span>.<span>text</span><span>?</span>.<span>toUpperCase</span><span>(</span><span>)</span><span>;</span>
<span>}</span><span>)</span><span>;</span>
<span>}</span><span>)</span><span>;</span>
 
<span>for</span> <span>(</span>let i <span>=</span> <span>0</span><span>;</span> i <span>&lt;</span> rteDataCopy<span>?</span>.<span>length</span><span>;</span> i<span>++</span><span>)</span> <span>{</span>
rte<span>?</span>.<span>deleteNode</span><span>(</span><span>{</span> at<span>:</span> path<span>[</span>i<span>]</span> <span>}</span><span>)</span><span>;</span>
rte.<span>insertNode</span><span>(</span>rteDataCopy<span>[</span>i<span>]</span>, <span>{</span>
at<span>:</span> path<span>[</span>i<span>]</span>,
<span>}</span><span>)</span><span>;</span>
<span>}</span>
<span>}</span><span>;</span>
 

  • Save your academy.tsx file and make sure that your application is still running with no errors.

  • Tip: in case you might have experienced any issues or your application is erroring, the final code is provided in the rte/src/solution-1.txt file, which contents you can copy and paste into the rte/src/academy.tsx file.

  • Next navigate to the rte/src/plugin.tsx and add the following two import statements:

  • import { Icon } from "@contentstack/venus-components";
    import React from "react";
    import { onClickHandler } from "./academy";

  • On that same file, replace the return { } statement with the following code snippet:

  • <span>if</span> <span>(</span><span>!</span>RTE<span>)</span> <span>return</span><span>;</span>
     
    <span>const</span> Academy <span>=</span> RTE<span>(</span><span>"academy"</span>, <span>(</span><span>)</span> <span>=&gt;</span> <span>{</span>
    <span>return</span> <span>{</span>
    title<span>:</span> <span>"Capitalize"</span>,
    icon<span>:</span> <span>&lt;</span>Icon icon<span>=</span><span>"Settings"</span> <span>/</span><span>&gt;</span>,
    <span>}</span><span>;</span>
    <span>}</span><span>)</span><span>;</span>
     
    <span>//@ts-ignore</span>
    Academy.<span>on</span><span>(</span><span>"exec"</span>, async <span>(</span>rte<span>:</span> RTE<span>)</span> <span>=&gt;</span> <span>{</span>
    <span>try</span> <span>{</span>
    onClickHandler<span>(</span>rte<span>)</span><span>;</span>
    <span>}</span> <span>catch</span> <span>(</span>e<span>)</span> <span>{</span>
    console.<span>error</span><span>(</span><span>"Error"</span>, e<span>)</span><span>;</span>
    <span>}</span>
    <span>}</span><span>)</span><span>;</span>
     
    <span>return</span> <span>{</span>
    Academy,
    <span>}</span><span>;</span>
     

  • Save your plugin.tsx file and make sure that your application is still running with no errors.

  • Tip: in case you might have experienced any issues or your application is erroring, the final code is provided in the rte/src/solution-2.txt file, which contents you can copy and paste into the rte/src/plugin.tsx file.

  • Now, for the plugin to be served you will need to build the code and deploy it to the public folder of the main application. If you still have the boilerplate app running, stop it. You can do so, by pressing CNTRL+C in the terminal where the app is running.

  • Once the application has stopped you will need to execute the following commands from your terminal:

      Navigate to the root location of the rte plugin code: [project-root]/rte/src:

      cd [project-root]/rte/src

      First, install the dependencies by running the following command:

      npm install

      Second, build your plugin by running the following command:

      npm run build-academy

      Navigate back to the root of the main boilerplate app project [project-root]/src

      cd [project-root]/src

      Start the boilerplate app by running:

      npm start

      You should see something like this if you browse http://localhost:3000/json-rte.js

    BuildingMarketApps_L13_img-1.png
  • Next, we will need to add a JSON RTE field to our Sample Content Type and activate the plugin so we can see it in action. To do that take the following steps:

    • Using the left navigation bar, click on the Content Models section

    • BuildingMarketApps_L13_img-2.png
    • The select the Sample Content Type content type you created during the entry sidebar exercise. Alternatively you can use other Content Type, these steps work for any content type.

    • Add a JSON Rich Text Editor field to your content type

      BuildingMarketApps_L13_img-3.png
    • Choose the Sample App plugin

    • BuildingMarketApps_L13_img-4.png
    • Save the content type and open an entry based on it. You can go to the Entries section as described on the previous Module where you created the entry sidebar location extension.

    • BuildingMarketApps_L13_img-5.png
    • Once in the editor view of the entry, navigate to the JSON RTE field, type some content and select it. For example type in Marketplace App Example.

      BuildingMarketApps_L13_img-6.png

      Once you have some text selected, click on the toolbar "settings" icon. The text should be capitalized:

      BuildingMarketApps_L13_img-7.png

  • Tip: make sure you go over the entire code in the file to get a better understanding on how the logic is implemented.

    If you want to learn more about JSON RTE plugins, please visit our documentation: Create JSON RTE Plugins

    Congratulations! You just finished implementing the last UI Location supported by Marketplace Apps.

    In the next module you will get access to some best practices and useful tips to develop your Marketplace Apps.

    Video: Implementing JSON RTE Plugin

    The following video walks you through the previous exercise steps so you can review and follow along: