Skip to content

Commit

Permalink
Merge pull request #3116 from ONSdigital/EAR-2404-Bullet-Point-Format…
Browse files Browse the repository at this point in the history
…ting

EAR-2404 Pasting of rich text content
  • Loading branch information
Paul-Joel authored Jun 19, 2024
2 parents ac7f7e4 + 6f58d8a commit ff28919
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 55 deletions.
2 changes: 1 addition & 1 deletion eq-author-api/utils/createQuestionnaireIntroduction.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = (metadata) => {
additionalGuidancePanelSwitch: false,
additionalGuidancePanel: "",
description:
"<ul><li>Data should relate to all sites in England, Scotland, Wales and Northern Ireland unless otherwise stated. </li><li>You can provide info estimates if actual figures are not available.</li><li>We will treat your data securely and confidentially.</li></ul>",
"<ul><li>Data should relate to all sites in England, Scotland, Wales and Northern Ireland unless otherwise stated.</li><li>You can provide info estimates if actual figures are not available.</li><li>We will treat your data securely and confidentially.</li></ul>",
legalBasis: NOTICE_1,
// TODO: previewQuestions
previewQuestions: false,
Expand Down
1 change: 1 addition & 0 deletions eq-author/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"draft-js-block-breakout-plugin": "latest",
"draft-js-plugins-editor": "latest",
"draft-js-raw-content-state": "latest",
"draft-js-import-html": "latest",
"draftjs-filters": "^2.5.0",
"firebase": "latest",
"firebaseui": "latest",
Expand Down
96 changes: 72 additions & 24 deletions eq-author/src/components/RichTextEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import styled, { css } from "styled-components";
import Editor from "draft-js-plugins-editor";
import { EditorState, RichUtils, Modifier, CompositeDecorator } from "draft-js";
import { stateFromHTML } from "draft-js-import-html";
import "draft-js/dist/Draft.css";
import createBlockBreakoutPlugin from "draft-js-block-breakout-plugin";

Expand All @@ -27,9 +28,7 @@ import { sharedStyles } from "components/Forms/css";
import { Field, Label } from "components/Forms";
import ValidationError from "components/ValidationError";

import PasteModal, {
preserveRichFormatting,
} from "components/modals/PasteModal";
import PasteModal from "components/modals/PasteModal";

import { colors } from "../../constants/theme";

Expand Down Expand Up @@ -395,12 +394,12 @@ class RichTextEditor extends React.Component {

state = { showPasteModal: false, text: "", multiline: false };

handlePaste = (text) => {
handlePaste = (text, html) => {
if (/\s{2,}/g.test(text)) {
this.setState({
showPasteModal: true,
multiline: false,
text: text,
text: html || text,
});
} else {
this.handleChange(
Expand All @@ -418,12 +417,12 @@ class RichTextEditor extends React.Component {
return "handled";
};

handlePasteMultiline = (text) => {
handlePasteMultiline = (text, html) => {
if (/\s{2,}/g.test(text)) {
this.setState({
showPasteModal: true,
multiline: true,
text: text,
text: html || text,
});
return "handled";
} else {
Expand All @@ -436,27 +435,76 @@ class RichTextEditor extends React.Component {
const currentContent = editorState.getCurrentContent();
const currentSelection = editorState.getSelection();

let modifiedText;
let newEditorState;
let processedText = text;

// Simple HTML sanitization function
const sanitizeHtml = (html) => {
const doc = new DOMParser().parseFromString(html, "text/html");
return doc.body.innerHTML;
};

// Sanitize the input HTML
const sanitizedHtml = sanitizeHtml(processedText);

if (multiline) {
modifiedText = preserveRichFormatting(text);
} else {
modifiedText = text.replace(/\n/g, " ").trim().replace(/\s+/g, " ");
}
// Process the text to remove multiple spaces
const div = document.createElement("div");
div.innerHTML = sanitizedHtml;
const walker = document.createTreeWalker(
div,
NodeFilter.SHOW_TEXT,
null,
false
);
while (walker.nextNode()) {
walker.currentNode.nodeValue = walker.currentNode.nodeValue.replace(
/\s+/g,
" "
);
}
processedText = div.innerHTML;

// Replace the selected text with the pasted content
const newContentState = Modifier.replaceText(
currentContent,
currentSelection,
modifiedText
);
// Convert processed text from HTML to ContentState
const contentState = stateFromHTML(processedText);
const fragment = contentState.getBlockMap();

// Create a new EditorState with the updated content
const newEditorState = EditorState.push(
editorState,
newContentState,
"insert-characters"
);
// Replace the selected text with the pasted content
const newContentState = Modifier.replaceWithFragment(
currentContent,
currentSelection,
fragment
);

// Create a new EditorState with the updated content
newEditorState = EditorState.push(
editorState,
newContentState,
"insert-characters"
);
} else {
// For single line pastes, replace multiple spaces with a single space
processedText = processedText
.replace(/\n/g, " ")
.replace(/\s+/g, " ")
.trim();
const contentState = stateFromHTML(processedText);
const fragment = contentState.getBlockMap();

// Replace the selected text with the pasted content
const newContentState = Modifier.replaceWithFragment(
currentContent,
currentSelection,
fragment
);

// Create a new EditorState with the updated content
newEditorState = EditorState.push(
editorState,
newContentState,
"insert-characters"
);
}

// Set the new editor state and close the paste modal
this.setState({
Expand Down
13 changes: 0 additions & 13 deletions eq-author/src/components/modals/PasteModal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,6 @@ import PropTypes from "prop-types";

const Message = styled.div``;

export const preserveRichFormatting = (text) => {
// Replace multiple spaces and tabs with a single space
let formattedText = text.replace(/[ \t]+/g, " ");

// Split the text into lines
let lines = formattedText.split(/\r?\n/);

// Remove leading and trailing spaces from each line and join them back with newline characters
formattedText = lines.map((line) => line.trim()).join("\n");

return formattedText;
};

const ModalWrapper = styled.div`
.modal-button-container {
margin-top: 1em;
Expand Down
18 changes: 1 addition & 17 deletions eq-author/src/components/modals/PasteModal/index.test.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
import React from "react";
import { render, fireEvent } from "tests/utils/rtl";
import PasteModal, { preserveRichFormatting } from ".";
import PasteModal from ".";

import { keyCodes } from "constants/keyCodes";

const { Escape } = keyCodes;

describe("preserveRichFormatting function", () => {
it("should replace multiple spaces and tabs with a single space", () => {
const inputText = " Hello \t\t World ";
const expectedOutput = "Hello World";
const result = preserveRichFormatting(inputText);
expect(result).toBe(expectedOutput);
});

it("should remove leading and trailing spaces from each line", () => {
const inputText = " Line 1 \n Line 2 \n Line 3 ";
const expectedOutput = "Line 1\nLine 2\nLine 3";
const result = preserveRichFormatting(inputText);
expect(result).toBe(expectedOutput);
});
});

describe("PasteModal", () => {
let onConfirm, onCancel;
onConfirm = jest.fn();
Expand Down
25 changes: 25 additions & 0 deletions eq-author/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7331,6 +7331,21 @@ draft-js-block-breakout-plugin@latest:
dependencies:
immutable "~3.7.4"

draft-js-import-element@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/draft-js-import-element/-/draft-js-import-element-1.4.0.tgz#8760acbfeb60ed824a1c8319ec049f702681df66"
integrity sha512-WmYT5PrCm47lGL5FkH6sRO3TTAcn7qNHsD3igiPqLG/RXrqyKrqN4+wBgbcT2lhna/yfWTRtgzAbQsSJoS1Meg==
dependencies:
draft-js-utils "^1.4.0"
synthetic-dom "^1.4.0"

draft-js-import-html@latest:
version "1.4.1"
resolved "https://registry.yarnpkg.com/draft-js-import-html/-/draft-js-import-html-1.4.1.tgz#c222a3a40ab27dee5874fcf78526b64734fe6ea4"
integrity sha512-KOZmtgxZriCDgg5Smr3Y09TjubvXe7rHPy/2fuLSsL+aSzwUDwH/aHDA/k47U+WfpmL4qgyg4oZhqx9TYJV0tg==
dependencies:
draft-js-import-element "^1.4.0"

draft-js-plugins-editor@latest:
version "3.0.0"
resolved "https://registry.yarnpkg.com/draft-js-plugins-editor/-/draft-js-plugins-editor-3.0.0.tgz#196d1e065e2c29faebaab4ec081b734fdef294a2"
Expand All @@ -7346,6 +7361,11 @@ draft-js-raw-content-state@latest:
dependencies:
draft-js "^0.10.5"

draft-js-utils@^1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/draft-js-utils/-/draft-js-utils-1.4.1.tgz#a59c792ad621f7050292031a237d524708a6d509"
integrity sha512-xE81Y+z/muC5D5z9qWmKfxEW1XyXfsBzSbSBk2JRsoD0yzMGGHQm/0MtuqHl/EUDkaBJJLjJ2EACycoDMY/OOg==

draft-js@^0.10.5:
version "0.10.5"
resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
Expand Down Expand Up @@ -16753,6 +16773,11 @@ synchronous-promise@latest:
resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e"
integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==

synthetic-dom@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/synthetic-dom/-/synthetic-dom-1.4.0.tgz#d988d7a4652458e2fc8706a875417af913e4dd34"
integrity sha512-mHv51ZsmZ+ShT/4s5kg+MGUIhY7Ltq4v03xpN1c8T1Krb5pScsh/lzEjyhrVD0soVDbThbd2e+4dD9vnDG4rhg==

tabbable@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.0.0.tgz#7f95ea69134e9335979092ba63866fe67b521b01"
Expand Down

0 comments on commit ff28919

Please sign in to comment.