Rich text: HtmlArea

Contents

If you want to work with rich text in Content Studio, HtmlArea is your friend. This rich text input type offers some very powerful editing capabilities and a lot of configuration options. This whole chapter is dedicated to the HtmlArea: how it works and how to use it in a headless fashion.

The tasks and examples in this chapter are based on work done in previous chapters (starting with the sandboxes chapter). If you want to follow along with the examples, make sure you’re all caught up.

Overview

A text input field with a tools ribbon just above.
Figure 1. The HtmlArea input type with the default tools selection

The HtmlArea input type gives you a multi-line input field with rich formatting options, much like what you’re used to from your favorite word processor. It supports text formatting and justification, lists, links, and even lets you insert images.

You can configure the HtmlArea to be exactly what you want by dictating what formatting tools to include or exclude (the include and exclude attributes, respectively), by what heading levels are allowed (allowedHeadings), and by giving it a default value (default).

Additionally, the HtmlArea supports the use of macros, giving you the ability to insert prefabricated rich components into your markup.

Configuration of text tools

We can use the HtmlArea’s config section to specify which rich text controls should be available when editing. Using a combination of the include and exclude tags, we can customize the input’s appearance.

By default, it includes a limited, but useful selection of tools at your disposal. Using <include>*</include> as an option is the same as including the default tools.

The HtmlArea with default configuration
<input type="HtmlArea" name="everything">
  <label>HtmlArea</label>
</input>

If you want to exclude all controls, use <exclude>*</exclude>:

The HtmlArea configured to include nothing
<input type="HtmlArea" name="nothing">
  <label>HtmlArea with exclude: *</label>
  <config>
    <exclude>*</exclude>
  </config>
</input>
An HtmlArea configured to exclude all editing tools.
Figure 2. An HtmlArea exput with <exclude>*</exclude> and a selection of tools.

Finally, if you want to exclude most tools, but not all, you can exclude all and then add back in the ones you want:

The HtmlArea configured to include a selection of controls
<input type="HtmlArea" name="something">
  <label>HtmlArea with some controls</label>
  <config>
    <exclude>*</exclude>
    <include>Format | Bold Italic Underline | JustifyBlock
    JustifyLeft JustifyCenter JustifyRight | HorizontalRule Blockquote</include>
  </config>
</input>
An HtmlArea configured to include a selection of editing tools.
Figure 3. An HtmlArea with a selection of tools and <exclude>*</exclude>

For a full overview of the available text tools and HtmlArea configuration options, visit the HtmlArea input type reference docs. Additionally, you can find a more thorough explanation of the toolbar and its tools in the rich text editor reference documentation.

Task: Animal rich text

Let’s add a rich-text description to our animals. The input should be optional and have at most a single occurrence. You can include any editor tools you want, but it must at least have the image and macro tools, as we’ll be using these later. Otherwise, configure it as you want.

Once you’ve done that, add some text to one of your animals and try out the HtmlArea.

It’ll look a little something like this:

Animal with an HtmlArea input type
<?xml version="1.0" encoding="utf-8"?>
<content-type>
  <display-name>Animal</display-name>
  <description>An animal that lives on planet Earth</description>
  <super-type>base:structured</super-type>
  <form>

    <input type="TextLine" name="otherNames">
      <label>Other names</label>
      <help-text>Other names for this species.</help-text>
      <occurrences minimum="0" maximum="0" />
    </input>

    <input name="images" type="ImageSelector">
      <label>Images</label>
      <help-text>Images of the animal</help-text>
      <occurrences minimum="0" maximum="3" /> (4)
    </input>

    <input name="description" type="HtmlArea">
      <label>Description</label>
      <help-text>
        Describe the animal. Where its habitats are, what it eats, etc.
      </help-text>
      <config>
        <exclude>*</exclude>
        <include>Format Macro Image</include>
      </config>
    </input>

  </form>
</content-type>

Images

If you have configured your HtmlArea to include the image tool, you can click the image button to insert an image. Once you select an image, you’ll get a prompt that allows you to customize the image. You can add a caption, alt text, change the image’s justification in the block, and apply predefined image styles. There’s also a pencil icon in the upper right corner that allows you to edit the image, as covered in a previous chapter.

Task: Animal pictures

Try out the image controls that Content Studio gives you by uploading a picture and seeing what you can do with it. Remember to click the pencil icon to try the advanced editing tools such as cropping and focal points.

We looked at editing images in the previous chapter, but the HtmlArea also offers you something called "image styles", which we’ll have a look at now.

  1. Insert an image. Use the "insert image" button in the toolbar.

    The rich text toolbar with the image tool circled by a blue circle.
    Figure 4. The toolbar with the image tool highlighted
    You can also use ctrl+L to insert an image.
  2. You’ll be greeted by the "insert image" dialog:

    The insert image dialog, showing the image, an image styles dropdown, caption, and alt text fields
    Figure 5. The HtmlArea insert image dialog. Photo by Kurt Cotoaga 🦁
  3. If you try and access the "image styles" dropdown, you’ll find that it’s only got two options and neither of them seem to do much. To make this more exciting, let’s also create a custom image style.

    In a new file, src/main/resources/site/styles.xml, place the following simple style configuration:

    A very basic styles configuration
    <styles>
      <image name="conteditor-style-grayscale">
        <display-name>Grayscale</display-name>
        <aspect-ratio>1:1</aspect-ratio> (1)
        <filter>hsbcolorize(0x000000); grayscale(); border(6, 0x000000)</filter> (2)
      </image>
    </styles>
    1 The aspect ratio controls how the image is displayed. 1:1 makes the image square.
    2 The filters add a grayscale effect and then a border
    The styles reference docs have more information on how you can style your images, so go check it out for a deeper dive.
  4. With this style picked up by XP, you’ll have the "Grayscale" option available in the image styles dropdown. Select it to apply it to the image.

    The insert image dialog showing the results of applying the image styles to the image
    Figure 6. An image with an image style applied

Macros

Macros enable you to insert rich components into your text. When the page is rendered, the macro call is resolved and replaced. When the macro is resolved, it will either add text to your page or styles or scripts. See the macro reference docs for a more in-depth explanation of how this works. In this section, we’ll install a macro from an external application (Panel Macros), and look at how to use it. We’ll also see how macros behave in a headless context.

Task: Panel Macros

The Panel Macros package comes with a set of macros that let you put text differently colored items with icons.

The Panel Macros package in action, showing differently colored panels.
Figure 7. The Panel Macros in action

To use these macros, we first need to install the Panel Macros.

  1. Navigate to the Applications page (via the XP menu).

  2. Press "Install"

  3. Search for and install the "Panel Macros" app.

  4. Edit your site (My First Site). In the applications dropdown, find Panel Macros, and add it.

  5. Save and exit.

To add a macro to your HtmlArea, the easiest (and recommended) way is to use the macro tool.

The rich text toolbar with the macro button circled by a blue circle.
Figure 8. The toolbar with the macro tool highlighted

Open up an animal and try it out! The below example will use the "reindeer" created earlier.

  1. In the "Description" field, click the "insert macro" tool.

  2. Select one of the panels. If you have no preference, try the "Info panel".

  3. Add a header and some text to it. For reindeer, try:

    Reindeer macro data
    Header

    Habitats

    Text

    Reindeer are native to Arctic, sub-Arctic, tundra, boreal, and mountainous regions of northern Europe, Siberia, and North America.

    A form with 'header' and 'description' fields. Used to set macro parameters.
    Figure 9. The insert macro form with reindeer data
  4. Try out the preview and see your macro in action

    The text we specified in the macro preview form inside a light blue box with an icon attached
    Figure 10. The macro preview function

For more information on macros, including how to create your very own macros, refer back to the macro reference docs from before.

Headless

When you fetch rich text content via GraphQL, the response is formatted as unstyled, semantic HTML. It’s up to you to style the content when displaying it on the client. There are some exceptions, however:

  • Image styles (as applied in the editor) are returned with the image.

  • Macros use a special element <editor-macro>, which you need to replace with the processed output of the macro on the client.

Images and styles

When you add a an image to a rich text input, you can fetch it via GraphQL using the processedHtml sub selection of your rich text field. Let’s look at how that works. To start us off, this query finds the piece of content that we added the image to (the "Lion" 🦁) and extracts its processed HTML from its description.

This simple query just fetches the processed HTML of the HTML area:

Fetching rich text
{
  guillotine {
    query(contentTypes:"com.example.myproject:animal",
      query: "displayName = 'Lion'") {
      ... on com_example_myproject_Animal {
        data {
          description {
            processedHtml
          }
        }
      }
    }
  }
}

In the returned HTML, you can see the image with styles applied. If you extract the src value and add it after localhost:8080 (or any other instance), you should find the image with styles applied.

The query result for the processed HTML
{
  "data": {
    "guillotine": {
      "query": [
        {
          "data": {
            "description": {
              "processedHtml": "<div>\n<div>\n<div contenteditable=\"false\" tabindex=\"-1\">\n<figure class=\"captioned editor-align-justify editor-style-grayscale\" data-widget=\"image\"><img alt=\"A yawning lion cub\" src=\"/site/default/draft/my-first-site/_/image/ef8605b1-6966-4011-bbfe-65c4c2e9ec86:d5d96909fc488356fd24e67763cbb1868b3f77c3/block-768-768/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29\" data-image-ref=\"103d21b9-de86-4a9c-ab3d-82859a2ccf31\" style=\"width:100%\" />\n<figcaption>Soooo sleepy 😪</figcaption>\n</figure>\n</div>\n</div>\n</div>\n\n<p>&nbsp;</p>\n"
            }
          }
        }
      ]
    }
  }
}

But wait, there’s more! You can also get the image in different sizes (using the srcset attribute (MDN)) by adding a processHtml argument to description:

Fetching rich text with srcset
{
  guillotine {
    query(contentTypes:"com.example.myproject:animal",
      query: "displayName = 'Lion'") {
      ... on com_example_myproject_Animal {
        data {
          description(processHtml: {imageWidths: [200, 500]}) {
            processedHtml
          }
        }
      }
    }
  }
}

That’ll get you a result that looks like this:

Result with srcset
{
  "data": {
    "guillotine": {
      "query": [
        {
          "data": {
            "description": {
              "processedHtml": "<div>\n<div>\n<div contenteditable=\"false\" tabindex=\"-1\">\n<figure class=\"captioned editor-align-justify editor-style-grayscale\" data-widget=\"image\"><img alt=\"A yawning lion cub\" src=\"/site/default/draft/my-first-site/_/image/ef8605b1-6966-4011-bbfe-65c4c2e9ec86:d5d96909fc488356fd24e67763cbb1868b3f77c3/block-768-768/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29\" data-image-ref=\"e5be0ead-e803-4af6-a6c4-7edc004038b3\" srcset=\"/site/default/draft/my-first-site/_/image/ef8605b1-6966-4011-bbfe-65c4c2e9ec86:d5d96909fc488356fd24e67763cbb1868b3f77c3/block-200-200/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29 200w,/site/default/draft/my-first-site/_/image/ef8605b1-6966-4011-bbfe-65c4c2e9ec86:d5d96909fc488356fd24e67763cbb1868b3f77c3/block-500-500/lion-cub.jpg?filter=hsbcolorize%280x000000%29%3B+grayscale%28%29%3B+border%286%2C+0x000000%29 500w\" style=\"width:100%\" />\n<figcaption>Soooo sleepy 😪</figcaption>\n</figure>\n</div>\n</div>\n</div>\n\n<p>&nbsp;</p>\n"
            }
          }
        }
      ]
    }
  }
}

If reading escaped HTML in a JSON string isn’t your favorite thing, the important part of the above result is that we’ve gotten a srcset attribute for our image, which means that the browser will pick the size of the image that best suits the current situation (based on screen sizes etc.), and use a smaller image when possible. This means fewer bytes across the wire and faster load times!

And finally, what if we want to extract styling info? In our case, the styling is done on the server, but in case you want to fetch and use that for some reason, we can do that too:

Fetching image styles from an HtmlArea
{
  guillotine {
    query(contentTypes:"com.example.myproject:animal",
      query: "displayName = 'Lion'") {
      ... on com_example_myproject_Animal {
        data {
          description {
            images {
              style {
                name
                aspectRatio
                filter
              }
            }
          }
        }
      }
    }
  }
}

You can extract the properties that you specified in your styles.xml previously:

The result of fetching image styles
{
  "data": {
    "guillotine": {
      "query": [
        {
          "data": {
            "description": {
              "images": [
                {
                  "style": {
                    "name": "editor-style-grayscale",
                    "aspectRatio": "1:1",
                    "filter": "hsbcolorize(0x000000); grayscale(); border(6, 0x000000)"
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Macros

In headless contexts, you don’t get macros fully rendered. Instead, XP inserts <editor-macro> elements into the output of the HtmlArea. Each <editor-macro> has a data-macro-ref attribute that contains a unique reference. Use this reference to match it up with the macro data.

When working headlessly, macros contain only data and not any form of markup. In other words: the client needs to construct the markup and styling itself. Thus, the preview you see in Content Studio does not reflect what you get in a headless context. You would have to implement the markup and styling yourself.

Let’s try and fetch the macro you just added to your animal:

Query that fetches all macro info from an HtmlArea
{
  guillotine {
    query(contentTypes:"com.example.myproject:animal",
      query: "displayName = 'Reindeer'") {
      ... on com_example_myproject_Animal {
        data {
          description {
            processedHtml
            macrosAsJson
          }
        }
      }
    }
  }
}
Macro query result
{
  "data": {
    "guillotine": {
      "query": [
        {
          "data": {
            "description": { (1)
              "processedHtml": "<p><editor-macro data-macro-name=\"info\" data-macro-ref=\"b226bdbb-a781-44e8-9402-dfc5d086af41\">Reindeer are native to Arctic, sub-Arctic, tundra, boreal, and mountainous regions of northern Europe, Siberia, and North America.</editor-macro></p>\n",
              "macrosAsJson": [ (2)
                {
                  "ref": "b226bdbb-a781-44e8-9402-dfc5d086af41",
                  "name": "info",
                  "descriptor": "com.enonic.app.panelmacros:info",
                  "config": {
                    "info": {
                      "body": "Reindeer are native to Arctic, sub-Arctic, tundra, boreal, and mountainous regions of northern Europe, Siberia, and North America.",
                      "header": "Habitats"
                    }
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}
1 The description now contains <editor-macro> tags in its processedHtml property.
2 Using the macrosAsJson property, you can get the type and content of the macros.

Contents