Skip to content

Commit

Permalink
#3081: Update richTextEditor mandatory h1
Browse files Browse the repository at this point in the history
This makes sure that if the richTextEditor contains h1 tags marked
with the mandatory class, they can be edited but the tags itself
can not be deleted. Also, when the content is saved, the resulting
html also still has the mandatory class for the h1 tag (this was not
the case before).
  • Loading branch information
maradragan committed Jun 25, 2020
1 parent 06a38fc commit b036bb8
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 27 deletions.
9 changes: 9 additions & 0 deletions client/src/components/RichTextEditor.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@
.Draftail-block--unstyled {
margin: 0.75rem 0;
}

h1:empty {
content: 'Please enter a heading here';
background-color: red;
}
.mandatory {
background-color: green;
content: 'Please enter a heading here';
}
39 changes: 35 additions & 4 deletions client/src/components/RichTextEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,17 @@ const importerConfig = {

return null
},
htmlToBlock: nodeName => {
htmlToBlock: (nodeName, node) => {
if (
nodeName === "h1" ||
node.attributes?.classname?.nodeValue === "mandatory"
) {
return {
type: BLOCK_TYPE.HEADER_ONE,
data: { mandatory: true }
}
}

if (nodeName === "hr" || nodeName === "img") {
// "atomic" blocks is how Draft.js structures block-level entities.
return "atomic"
Expand All @@ -119,6 +129,13 @@ const importerConfig = {

const exporterConfig = {
blockToHTML: block => {
if (block.type === BLOCK_TYPE.HEADER_ONE && block.data.mandatory) {
return {
start: '<h1 class="mandatory">',
end: "</h1>"
}
}

if (block.type === BLOCK_TYPE.BLOCKQUOTE) {
return <blockquote />
}
Expand Down Expand Up @@ -174,7 +191,14 @@ class RichTextEditor extends Component {
}

render() {
const { className, value, onChange, onHandleBlur, template } = this.props
const {
className,
value,
onChange,
onHandleBlur,
template,
templateHTML
} = this.props
const { sideToolbarPlugin } = this.state
const { SideToolbar } = sideToolbarPlugin
return (
Expand All @@ -196,7 +220,13 @@ class RichTextEditor extends Component {
stateSaveInterval={100}
plugins={[sideToolbarPlugin, newlinePlugin, readonlyBlockPlugin]}
handlePastedText={() => true}
rawContentState={value ? fromHTML(value) : template || null}
rawContentState={
value
? fromHTML(value)
: templateHTML
? fromHTML(templateHTML)
: template || null
}
showUndoControl
showRedoControl
spellCheck
Expand Down Expand Up @@ -230,7 +260,8 @@ RichTextEditor.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
onHandleBlur: PropTypes.func,
template: PropTypes.object
template: PropTypes.object,
templateHTML: PropTypes.string
}

export default RichTextEditor
5 changes: 0 additions & 5 deletions client/src/components/editor/plugins/newlinePlugin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { preventHandleOnReadonly } from "components/editor/plugins/readonlyBlockPlugin"
import { ContentState, EditorState, Modifier, convertFromHTML } from "draft-js"

const HTML_REGEX = new RegExp(/<[a-z][\s\S]*>/i)
Expand Down Expand Up @@ -27,10 +26,6 @@ const createNewLines = (newLines, nextState) => {

const newlinePlugin = () => ({
handlePastedText(text, html, editorState, { setEditorState }) {
const isHandled = preventHandleOnReadonly(editorState) === "handled"
if (isHandled) {
return "handled"
}
const nextState = editorState
if (!HTML_REGEX.test(html)) {
const newLines = text.match(NEW_LINE_REGEX)
Expand Down
44 changes: 27 additions & 17 deletions client/src/components/editor/plugins/readonlyBlockPlugin.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
export const preventHandleOnReadonly = editorState => {
const selectionState = editorState.getSelection()
const anchorKey = selectionState.getAnchorKey()
const contentState = editorState.getCurrentContent()
const currentContentBlock = contentState.getBlockForKey(anchorKey)
const currentContentBlockData = currentContentBlock.getData().toObject()
if (currentContentBlockData.mandatory) {
return "handled"
const createReadonlyBlockPlugin = config => {
// TODO: rename plugin
const blockStyleFn = contentBlock => {
const contentBlockData = contentBlock.getData().toObject()
if (contentBlockData.mandatory) {
// Add mandatory class for content blocks marked as mandatory
return "mandatory"
}
}
return "not-handled"
}

const createReadonlyBlockPlugin = config => {
const handleKeyCommand = (command, editorState) => {
return preventHandleOnReadonly(editorState)
}
const handleBeforeInput = (chars, editorState) => {
return preventHandleOnReadonly(editorState)
if (["backspace", "delete"].includes(command)) {
const selectionState = editorState.getSelection()
const anchorKey = selectionState.getAnchorKey()
const contentState = editorState.getCurrentContent()
const currentContentBlock = contentState.getBlockForKey(anchorKey)
const currentContentBlockData = currentContentBlock.getData().toObject()
const currentContentBlockText = currentContentBlock.getText()
if (
currentContentBlockData.mandatory &&
currentContentBlockText.length === 1
) {
// Prevent deleting the block itself
return "handled"
}
}

return "not-handled"
}
return {
handleKeyCommand: handleKeyCommand,
handleBeforeInput: handleBeforeInput
blockStyleFn: blockStyleFn,
handleKeyCommand: handleKeyCommand
}
}

Expand Down
10 changes: 9 additions & 1 deletion client/src/pages/reports/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,12 @@ const BaseReportForm = ({
</div>
)
const isFutureEngagement = Report.isFuture(values.engagementDate)
const reportTextTemplateHTML =
'<h1 class="mandatory" style={ placeholder: "aaa" }>Key details 1</h1>'
const reportTextTemplate = {
blocks: [
{
data: { mandatory: true },
data: { mandatory: true, class: "mandatory" },
text: "Step 1",
type: BLOCK_TYPE.HEADER_ONE
},
Expand All @@ -519,6 +521,11 @@ const BaseReportForm = ({
data: {},
text: "Step 3",
type: BLOCK_TYPE.HEADER_ONE
},
{
data: { class: "mandatory" },
text: "",
type: BLOCK_TYPE.HEADER_ONE
}
],
entityMap: {}
Expand Down Expand Up @@ -982,6 +989,7 @@ const BaseReportForm = ({
setFieldTouched("reportText", true, false)
}}
template={reportTextTemplate}
templateHTML={reportTextTemplateHTML}
/>
}
/>
Expand Down

0 comments on commit b036bb8

Please sign in to comment.