What is the best way to customize a rich_text_area (aka trix) toolbar in rails?

1.2k Views Asked by At

Let's say I want to add a button that toggles red colored text on and off.

I've done the basic action_text setup as described on rubyonrails.org, however, I made the following change to my application.js file in order to add a button called RED to the toolbar and style it appropriately:

// app/javascript/packs/application.js

var Trix = require("trix")
require("@rails/actiontext")

const {lang} = Trix.config;

Trix.config.toolbar = {
  getDefaultHTML() { return `\
<div class="trix-button-row">
  <span class="trix-button-group trix-button-group--text-tools" data-trix-button-group="text-tools">
    <button type="button" class="trix-button trix-button--icon trix-button--icon-bold" data-trix-attribute="bold" data-trix-key="b" title="${lang.bold}" tabindex="-1">${lang.bold}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-italic" data-trix-attribute="italic" data-trix-key="i" title="${lang.italic}" tabindex="-1">${lang.italic}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-strike" data-trix-attribute="strike" title="${lang.strike}" tabindex="-1">${lang.strike}</button>
    <button type="button" class="trix-button" data-trix-attribute="red">RED</button>
  </span>
  <span class="trix-button-group trix-button-group--block-tools" data-trix-button-group="block-tools">
    <button type="button" class="trix-button trix-button--icon trix-button--icon-quote" data-trix-attribute="quote" title="${lang.quote}" tabindex="-1">${lang.quote}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-code" data-trix-attribute="code" title="${lang.code}" tabindex="-1">${lang.code}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-bullet-list" data-trix-attribute="bullet" title="${lang.bullets}" tabindex="-1">${lang.bullets}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-number-list" data-trix-attribute="number" title="${lang.numbers}" tabindex="-1">${lang.numbers}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-decrease-nesting-level" data-trix-action="decreaseNestingLevel" title="${lang.outdent}" tabindex="-1">${lang.outdent}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-increase-nesting-level" data-trix-action="increaseNestingLevel" title="${lang.indent}" tabindex="-1">${lang.indent}</button>
  </span>
  <span class="trix-button-group trix-button-group--file-tools" data-trix-button-group="file-tools">
    <button type="button" class="trix-button trix-button--icon trix-button--icon-attach" data-trix-action="attachFiles" title="${lang.attachFiles}" tabindex="-1">${lang.attachFiles}</button>
  </span>
  <span class="trix-button-group-spacer"></span>
  <span class="trix-button-group trix-button-group--history-tools" data-trix-button-group="history-tools">
    <button type="button" class="trix-button trix-button--icon trix-button--icon-undo" data-trix-action="undo" data-trix-key="z" title="${lang.undo}" tabindex="-1">${lang.undo}</button>
    <button type="button" class="trix-button trix-button--icon trix-button--icon-redo" data-trix-action="redo" data-trix-key="shift+z" title="${lang.redo}" tabindex="-1">${lang.redo}</button>
  </span>
</div>
<div class="trix-dialogs" data-trix-dialogs>
  <div class="trix-dialog trix-dialog--link" data-trix-dialog="href" data-trix-dialog-attribute="href">
    <div class="trix-dialog__link-fields">
      <input type="url" name="href" class="trix-input trix-input--dialog" placeholder="${lang.urlPlaceholder}" aria-label="${lang.url}" required data-trix-input>
      <div class="trix-button-group">
        <input type="button" class="trix-button trix-button--dialog" value="${lang.link}" data-trix-method="setAttribute">
        <input type="button" class="trix-button trix-button--dialog" value="${lang.unlink}" data-trix-method="removeAttribute">
      </div>
    </div>
  </div>
</div>\
`; }
};

Trix.config.textAttributes.red = {
  style: { color: "red" },
  parser: function(element){
    return element.style.color === "red"
  }
 }

At this point, the red styling will work as expected when creating or updating the form content, but the red styling stops working when I try to display the content outside of a form - in other words, <%= form.rich_text_area :content %> will correctly show red text, but <%= @message.content %> will not.

I did some research and noticed that action_text seems to scrub style attributes out of rendered HTML by default in ActionText::ContentHelper. I've tried whitelisting the style attribute and have succeeded but only by putting the following into a config/initializers file:

# config/initializers/action_text_config.rb

ActionText::ContentHelper.allowed_attributes << "style"

module ActionText
  class Content
  end
end

My questions are:

  1. Is there a better way to do this?
  2. If I try to manually debug by placing <%= ActionText::ContentHelper.allowed_attributes.inspect %> inside my view, why do I not find the style attribute that I added in my initializers file (even though <%= @message.content %> now correctly displays)?
  3. Why does rails want this weird empty module and class definition in my initializers file in order to make this work? Apparently it does something, because when I comment it out, the red text stops working in <%= @message.content %>.

I'm clearly missing something and would appreciate any help on this.

I'm using ruby 2.7.1, rails 6.0.3.2, and trix 1.2.3

0

There are 0 best solutions below