React-Quill does not preserve HTML response as is - Messes up the layout

Viewed 109

EDIT: We replaced Quill with TinyMCE and solved all our issues. We can successfully insert HTML response (as is) into tinyMCE's state as well as modify it!

We're experiencing a styling issue. We're building a mail templating application and the user can create/modify their templates. We also provide preexisting templates for our users.

The issue we're experiencing is the following.

1- We send the user a mail template. The initial/original response sent to the user is the following HTML

<html>
  <head>
    <title>FINROTA</title>
    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">
  </head>
  <body bgcolor=\"#FFFFFF\" leftmargin=\"0\" topmargin=\"0\" marginwidth=\"0\" marginheight=\"0\" style=\"font-family: Verdana, Geneva, Tahoma, sans-serif\">
    <table width=\"670\" border=\"0\" align=\"center\" cellpadding=\"0\" cellspacing=\"0\">
      <tr>
        <td bgcolor=\"#FFFFFF\">
          <table bgcolor=\"#FFFFFF\" width=\"575\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
            <!-- Header -->
            <tr>
              <td bgcolor=\"#FFFFFF\">
                <table width=\"575\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"border-bottom: 1px solid #F3F3F3;\">
                  <tr>
                    <td style=\"height: 94px;text-align: center;\">
                      <a href=\"https://portal.finrota.com/\">
                        <img width=\"120\" height=\"30\" style=\" display: initial;padding-top: 20px;\" src=\"https://cdnecozum.com/static/images/mailing/header.png\" alt=\"finrota-logo\" />
                      </a>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
            <!-- /Header -->
            <!-- Body -->
            <tr>
              <td style=\"padding-bottom: 64px\">
                <table bgcolor=\"#FFFFFF\" width=\"575\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                  <!-- Logo -->
                  <tr>
                    <td style=\"padding-top: 50px;text-align: center;\">
                      <img width=\"112\" height=\"110\" src=\"https://*****.com/static/images/mailing/pay3.png\" alt=\"top-images\" />
                    </td>
                  </tr>
                  <!-- /Logo -->
                  <!-- SubTitle -->
                  <tr>
                    <td style=\"text-align: center;padding-top: 35px;\">
                      <p style=\"font-size: 14px;font-weight: 600;color: #101010;\"> Ödeme iade işleminiz gerçekleşmiştir. </p>
                    </td>
                  </tr>
                  <!-- /SubTitle -->
                  <!-- Content -->
                  <tr>
                    <td style=\"padding-top: 54px;\">
                      <p style=\"font-size: 14px;font-weight: 600;color: #101010;\"> İşlem Bilgileri </p>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 8px;\">
                      <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                        <tr style=\"height: 54px;background-color: #FFF7F2;\">
                          <td style=\"width: 50%; padding-left: 25px;\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> İşlem No </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{TransactionNumber}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%; padding-left: 25px;\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> İşlem Tarihi </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{TransactionDate}} </p>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 36px;\">
                      <p style=\"font-size: 14px;font-weight: 600;color: #101010;\"> Ödeme Bilgileri </p>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 8px;\">
                      <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                        <tr style=\"height: 54px;border-radius: 10px;background-color: #FFF7F2;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Ödeme Şekli </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> Kredi Kartı </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Ödeme Tutarı </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{PureAmount}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;border-radius: 10px;background-color: #FFF7F2;padding-left: 20px\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Ödenen Tutar </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{ProcessAmount}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Taksit Sayısı </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{Installment}} Taksit </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;border-radius: 10px;background-color: #FFF7F2;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Taksit Tutarı </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{InstallmentRate}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Açıklama </p>
                          </td>
                        </tr>
                        <tr style=\"height: 35px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{Description}} </p>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 36px;\">
                      <p style=\"font-size: 14px;font-weight: 600;color: #101010;\"> Kart Bilgileri </p>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 8px;\">
                      <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                        <tr style=\"height: 54px;border-radius: 10px;background-color: #FFF7F2;\">
                          <td style=\"width: 50%;padding-left: 25px;\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Kart Numarası </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{CreditCardNumber}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> Kart Sahibi Adı-Soyadı </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{CardHolderName}} </p>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 36px;\">
                      <p style=\"font-size: 14px;font-weight: 600;color: #101010;\"> İade Bilgileri </p>
                    </td>
                  </tr>
                  <tr>
                    <td style=\"padding-top: 8px;\">
                      <table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                        <tr style=\"height: 54px;border-radius: 10px;background-color: #FFF7F2;\">
                          <td style=\"width: 50%;padding-left: 25px;\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> İade Tutarı </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{RefundAmount}} </p>
                          </td>
                        </tr>
                        <tr style=\"height: 54px;\">
                          <td style=\"width: 50%;padding-left: 25px\">
                            <p style=\"font-size: 12px;color: #6F6F6F;\"> İade Tarihi </p>
                          </td>
                          <td style=\"width: 50%;text-align: right;padding-right: 25px;\">
                            <p style=\"font-size: 12px;font-weight: 600;color: #101010;\"> {{RefundDate}} </p>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
            <!-- /Content -->
            <!-- /Body -->
            <!-- Footer -->
            <tr>
              <td>
                <table bgcolor=\"#FF671D\" width=\"670\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                  <tr align=\"center\">
                    <td>
                      <table width=\"575\" align=\"center\" height=\"163\" cellpadding=\"0\" cellspacing=\"0\" style=\"color: #FFFFFF; padding-bottom: 20px; border-bottom: 1px solid #FFA477;\">
                        <tr align=\"center\" height=\"80\">
                          <td style=\"font-size: 14px; font-weight: bolder;\">Netahsilat Müşteri Hizmetleri</td>
                        </tr>
                        <tr width=\"275\">
                          <td>
                            <table style=\"color: #FFFFFF; font-size: 12px; padding-bottom:10px;\" width=\"575\" align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">
                              <tr>
                                <td style=\"padding-left: 120px;\"> Sistem Destek E-Mail: </td>
                                <td style=\"padding-right: 65px; font-weight: bold;\"> netahsilatdestek <span>@</span>finrota <span>.</span>com </td>
                              </tr>
                              <tr height=\"50\">
                                <td style=\"padding-left: 120px;\"> Telefon: </td>
                                <td style=\"padding-right: 65px; font-weight: bold;\"> 0 <span>(212)</span> 256 60 00 </td>
                              </tr>
                              <tr>
                                <td style=\"padding-left: 120px;\"> Fax: </td>
                                <td style=\"padding-right: 65px; font-weight: bold;\"> 0 <span>(212)</span> 256 54 00 </td>
                              </tr>
                            </table>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                  <tr>
                    <td>
                      <table width=\"40%\" align=\"center\" border=\"0\" height=\"80\" cellpadding=\"0\" cellspacing=\"0\">
                        <tr align=\"center\" style=\"height: 80px;\">
                          <td style=\"width: 25%;\">
                            <a href=\"https://twitter.com/EcozumAS\">
                              <img width=\"32\" height=\"32\" src=\"https://cdnecozum.com/static/images/mailing/twitter.png\" alt=\"twitter\" />
                            </a>
                          </td>
                          <td style=\"width: 25%;\">
                            <a href=\"https://www.linkedin.com/company/ecozum/mycompany/\">
                              <img width=\"32\" height=\"32\" src=\"https://cdnecozum.com/static/images/mailing/linkedin.png\" alt=\"linkedin\" />
                            </a>
                          </td>
                          <td style=\"width: 25%;\">
                            <a href=\"https://www.facebook.com/EcozumAS/\">
                              <img width=\"32\" height=\"32\" src=\"https://cdnecozum.com/static/images/mailing/facebook.png\" alt=\"facebook\" />
                            </a>
                          </td>
                          <td style=\"width: 25%;\">
                            <a href=\"https://www.instagram.com/EcozumAS/\">
                              <img width=\"32\" height=\"32\" src=\"https://cdnecozum.com/static/images/mailing/instagram.png\" alt=\"instagram\" />
                            </a>
                          </td>
                        </tr>
                      </table>
                    </td>
                  </tr>
                </table>
              </td>
            </tr>
            <!-- /Footer -->
          </table>
        </td>
      </tr>
    </table>
  </body>
</html>

2- We then take this html response and set the react-quill 'state' with it

import { textEditorOptions } from "lib/constants";
import React, { forwardRef, useImperativeHandle, useRef } from "react";
import ReactQuill from "react-quill";

const editorOptions = {
    toolbar: false,
    clipboard: {
      matchVisual: false
    }
  },

  TemplateEditor = forwardRef(({ formParamForQuill, setFormParamForQuill, shouldHideToolbar, data, stateData, update, communicationType }, ref) => {

    const [quillState, setQuillState] = React.useState({ description: data || "" }),
      editorRef = useRef(),

      handleQuillChange = val => {

        setQuillState(() => ({ description: val }));
        update(`${communicationType}`, { ...stateData[`${communicationType}`], body: val });

      };

    useImperativeHandle(ref, () => ({ editorRef }));

    React.useEffect(() => {

      if (formParamForQuill.token !== null && formParamForQuill.explanation !== null)
        setFormParamForQuill({ token: null, explanation: null, ref: null });

    }, [formParamForQuill]);

    return (
      <>
        <div className="communication-template-quill">
          <ReactQuill
            ref={editorRef}
            modules={!shouldHideToolbar ? textEditorOptions : editorOptions}
            value={typeof quillState.description === "string" ? quillState.description : quillState.description.join("")}
            onChange={value => handleQuillChange(value)}
          />
        </div>
      </>
    );
  });

export default TemplateEditor;

'data' passed onto the editor component as a prop is the initial HTML response attached above. However, when the quill state is set with this 'data', quill state then becomes this (state modified by react-quill (attached below)), which does not preserve the original HTML thus leaving us with a messed up layout.

<p>FINROTA <a href=\"https://**.**.com/\" rel=\"noopener noreferrer\" target=\"_blank\">
    <img src=\"https://*****.com/static/images/mailing/header.png\" alt=\"finrota-logo\" height=\"30\" width=\"120\">
  </a>
  <img src=\"https://*****.com/static/images/mailing/pay.png\" alt=\"top-images\" height=\"128\" width=\"123\"> Sayın <strong style=\"color: rgb(16, 16, 16);\">{{fullName}},</strong>
</p>
<p>Ödeme sırasında alınan hata nedeniyle ödemeniz gerçekleşmemiştir.</p>
<p>İşlem Bilgileri</p>
<p>İşlem No</p>
<p>{{transactionNumber}}</p>
<p>İşlem Tarihi</p>
<p>{{transactionDate}}</p>
<p>Ödeme Bilgileri</p>
<p>Ödeme Şekli</p>
<p>Kredi Kartı</p>
<p>Ödeme Tutarı</p>
<p>{{pureAmount}}</p>
<p>Ödenen Tutar</p>
<p>{{processAmount}}</p>
<p>Taksit Sayısı</p>
<p>{{installment}} Taksit</p>
<p>Taksit Tutarı</p>
<p>{{installmentRate}}</p>
<p>Açıklama</p>
<p>{{description}}</p>
<p>Hata Mesajı</p>
<p>{{errorMessage}}</p>
<p>Kart Bilgileri</p>
<p>Kart Numarası</p>
<p>{{creditCardNumber}}</p>
<p>Kart Sahibi Adı-Soyadı</p>
<p>{{cardHolderName}}</p>
<p>
  <strong>** ** **</strong> Sistem Destek E-Mail: <strong> **@**.com </strong> Telefon: <strong> 0(**) ** ** 00 </strong> Fax: <strong> 0(**) ** ** 00 </strong>
  <a href=\"https://twitter.com/*****\" rel=\"noopener noreferrer\" target=\"_blank\">
    <img src=\"https://******.com/static/images/mailing/twitter.png\" alt=\"twitter\" height=\"32\" width=\"32\">
  </a>
  <a href=\"https://www.linkedin.com/company/*****/mycompany/\" rel=\"noopener noreferrer\" target=\"_blank\">
    <img src=\"https://*****.com/static/images/mailing/linkedin.png\" alt=\"linkedin\" height=\"32\" width=\"32\">
  </a>
  <a href=\"https://www.facebook.com/*****/\" rel=\"noopener noreferrer\" target=\"_blank\">
    <img src=\"https://*****.com/static/images/mailing/facebook.png\" alt=\"facebook\" height=\"32\" width=\"32\">
  </a>
  <a href=\"https://www.instagram.com/EcozumAS/\" rel=\"noopener noreferrer\" target=\"_blank\">
    <img src=\"https://*****.com/static/images/mailing/instagram.png\" alt=\"instagram\" height=\"32\" width=\"32\">
  </a>
</p>

How could the original HTML be preserved ? How do we overcome this styling issue ?

1 Answers

Short answer

No, you can't just throw any markup into a WYSIWYG editor and expect it to be able to perfectly work with the structure. These editors work with a subset of HTML.


Emails usually use ancient styling techniques. Even if Quill is able to correctly parse each tag and attribute, the result would likely be terrible to work with while editing. Emails often put everything in a table and need to add inline styles to work in older clients.

You can get part of the way there by only injecting the actual visible body content into Quill, not an entire HTML document.

The document head is not something that you could edit in a WYSIWYG editor. As you can see Quill just removes all tags it doesn't "know about" from around the content. Then it adds p tags around orphaned text. Hence you see the document title in the output as a paragraph.

1- We send the user a mail template

You only need to send the user the part of the template they can edit. The outer HTML is something that can be added by your server when the mail is finally sent.

The second problem is that the email content seems to be from a pre-existing application that formatted everything as tables. Also it contains a bunch of inline styles.

Quill is not a sophisticated HTML parser. (I mean, come on, it doesn't even remove the head tag) Neither is it very good at outputting HTML.

So if you want to convert this text to an email with that specific mark up, you'll have to do this conversion yourself from the tags and style attributes returned by Quill's content.

You'll probably also have to do a one time effort to convert these old templates into a format Quill can work with. Or write a function that can convert between the formats.

You could also try quil-better-table. It might improve Quill's ability to pick up table markup in the input.

If you're sending your emails with PHP, you could try quil-delta-parser or php-quil-renderer. That way you can send Quill's "deltas" to your back end, and transform them to the right HTML at save time, or even when the email is sent.

You can install both with Composer.

composer require nadar/quill-delta-parser

or

composer require deanblackborough/php-quill-renderer

Both libraries seem flexible enough to make it output the styles you want based on which attributes are in Quill's delta.

In any case it's probably very challenging if not impossible to make Quill understand and return the exact same markup of the template you post. Much easier to just take whatever it outputs and transform it afterwards.

Related