Skip to content

Commit

Permalink
fix: Remove custom tags for rich text elements (#101)
Browse files Browse the repository at this point in the history
* Add testing utility to programmatically update doc w/ html

* Add test for deserialisation of HTML

* Remove custom tag for rte
  • Loading branch information
jonathonherbert authored Sep 7, 2021
1 parent 4c046ca commit 25c71c8
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 58 deletions.
39 changes: 24 additions & 15 deletions cypress/helpers/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,29 @@ export const addImageElement = (values: Record<string, unknown> = {}) => {
});
};

type ElementFields = {
altTextValue?: string;
captionValue?: string;
srcValue?: string;
codeValue?: string;
useSrcValue?: string;
optionValue?: string;
restrictedTextValue?: string;
customDropdownValue?: string;
mainImageValue?: {
assets: string;
mediaId?: string;
mediaApiUri?: string;
};
};

export const setDocFromHtml = (fields: ElementFields) => {
cy.window().then((win: WindowType) => {
const { htmlToDoc } = win.PM_ELEMENTS;
htmlToDoc(getSerialisedHtml(fields));
});
};

export const getSerialisedHtml = ({
altTextValue = "",
captionValue = "<p></p>",
Expand All @@ -106,21 +129,7 @@ export const getSerialisedHtml = ({
mediaId: undefined,
mediaApiUri: undefined,
},
}: {
altTextValue?: string;
captionValue?: string;
srcValue?: string;
codeValue?: string;
useSrcValue?: string;
optionValue?: string;
restrictedTextValue?: string;
customDropdownValue?: string;
mainImageValue?: {
assets: string;
mediaId?: string;
mediaApiUri?: string;
};
}): string => {
}: ElementFields): string => {
const mainImageFields =
mainImageValue.mediaId || mainImageValue.mediaApiUri
? `&quot;mediaId&quot;:&quot;${
Expand Down
13 changes: 13 additions & 0 deletions cypress/tests/ImageElement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
getSerialisedHtml,
italicShortcut,
selectDataCy,
setDocFromHtml,
typeIntoElementField,
visitRoot,
} from "../helpers/editor";
Expand Down Expand Up @@ -143,6 +144,12 @@ describe("ImageElement", () => {
})
);
});

it("should deserialise content from HTML into the appropriate node in the document", () => {
const values = { altTextValue: "Alt text" };
setDocFromHtml(values);
assertDocHtml(getSerialisedHtml(values));
});
});

describe("Text field", () => {
Expand Down Expand Up @@ -211,6 +218,12 @@ describe("ImageElement", () => {
typeIntoElementField("src", "Src text");
assertDocHtml(getSerialisedHtml({ srcValue: "Src text" }));
});

it("should deserialise content from HTML into the appropriate node in the document", () => {
const values = { srcValue: "Src text" };
setDocFromHtml(values);
assertDocHtml(getSerialisedHtml(values));
});
});

describe("Checkbox field", () => {
Expand Down
9 changes: 9 additions & 0 deletions demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,13 @@ window.PM_ELEMENTS = {
insertElement: insertElement,
docToHtml: () =>
firstEditor ? docToHtml(serializer, firstEditor.state.doc) : "",
htmlToDoc: (html: string) => {
const node = htmlToDoc(parser, html);
firstEditor?.updateState(
EditorState.create({
doc: node,
plugins: firstEditor.state.plugins,
})
);
},
};
1 change: 1 addition & 0 deletions demo/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export type WindowType = {
view: EditorView;
insertElement: typeof insertElement;
docToHtml: () => string;
htmlToDoc: (html: string) => void;
};
};
79 changes: 36 additions & 43 deletions src/plugin/nodeSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,19 @@ export const getNodeSpecForField = (
fieldName: string,
field: FieldDescription
): NodeSpec => {
const nodeName = getNodeNameFromField(fieldName, elementName);

switch (field.type) {
case "text":
return {
[getNodeNameFromField(fieldName, elementName)]: {
[nodeName]: {
content:
field.isMultiline && !field.isCode ? "(text|hard_break)*" : "text*",
toDOM: getDefaultToDOMForContentNode(elementName, fieldName),
toDOM: getDefaultToDOMForContentNode(nodeName),
parseDOM: [
{
tag: "div",
getAttrs: (dom: Element) => {
const domFieldName = dom.getAttribute(fieldNameAttr);
if (
domFieldName !== getNodeNameFromField(fieldName, elementName)
) {
return false;
}

return {};
},
getAttrs: createGetAttrsForTextNode(nodeName),
preserveWhitespace: field.isCode ? "full" : false,
},
],
Expand All @@ -115,24 +108,25 @@ export const getNodeSpecForField = (
case "richText": {
const nodeSpec = field.nodeSpec ?? {};
return {
[getNodeNameFromField(fieldName, elementName)]: {
[nodeName]: {
...nodeSpec,
content: nodeSpec.content ?? "paragraph+",
toDOM:
nodeSpec.toDOM ??
getDefaultToDOMForContentNode(elementName, fieldName),
toDOM: nodeSpec.toDOM ?? getDefaultToDOMForContentNode(nodeName),
parseDOM: nodeSpec.parseDOM ?? [
{ tag: getTagForNode(elementName, fieldName) },
{
tag: "div",
getAttrs: createGetAttrsForTextNode(nodeName),
},
],
},
};
}
case "checkbox":
return {
[getNodeNameFromField(fieldName, elementName)]: {
[nodeName]: {
atom: true,
toDOM: getDefaultToDOMForLeafNode(elementName, fieldName),
parseDOM: getDefaultParseDOMForLeafNode(elementName, fieldName),
toDOM: getDefaultToDOMForLeafNode(nodeName),
parseDOM: getDefaultParseDOMForLeafNode(nodeName),
attrs: {
fields: {
default: field.defaultValue,
Expand All @@ -142,10 +136,10 @@ export const getNodeSpecForField = (
};
case "dropdown":
return {
[getNodeNameFromField(fieldName, elementName)]: {
[nodeName]: {
atom: true,
toDOM: getDefaultToDOMForLeafNode(elementName, fieldName),
parseDOM: getDefaultParseDOMForLeafNode(elementName, fieldName),
toDOM: getDefaultToDOMForLeafNode(nodeName),
parseDOM: getDefaultParseDOMForLeafNode(nodeName),
attrs: {
fields: {
default: field.defaultValue,
Expand All @@ -155,10 +149,10 @@ export const getNodeSpecForField = (
};
case "custom":
return {
[getNodeNameFromField(fieldName, elementName)]: {
[nodeName]: {
atom: true,
toDOM: getDefaultToDOMForLeafNode(elementName, fieldName),
parseDOM: getDefaultParseDOMForLeafNode(elementName, fieldName),
toDOM: getDefaultToDOMForLeafNode(nodeName),
parseDOM: getDefaultParseDOMForLeafNode(nodeName),
attrs: {
fields: {
default: { value: field.defaultValue },
Expand All @@ -169,38 +163,40 @@ export const getNodeSpecForField = (
}
};

const getDefaultToDOMForContentNode = (
elementName: string,
fieldName: string
) => () =>
const createGetAttrsForTextNode = (nodeName: string) => (dom: Element) => {
const domFieldName = dom.getAttribute(fieldNameAttr);

if (domFieldName !== nodeName) {
return false;
}

return undefined;
};

const getDefaultToDOMForContentNode = (nodeName: string) => () =>
[
"div",
{
[fieldNameAttr]: getNodeNameFromField(fieldName, elementName),
[fieldNameAttr]: nodeName,
},
0,
] as const;

const getDefaultToDOMForLeafNode = (elementName: string, fieldName: string) => (
node: Node
) => [
const getDefaultToDOMForLeafNode = (nodeName: string) => (node: Node) => [
"div",
{
[fieldNameAttr]: getNodeNameFromField(fieldName, elementName),
[fieldNameAttr]: nodeName,
fields: JSON.stringify(node.attrs.fields),
"has-errors": JSON.stringify(node.attrs.hasErrors),
},
];

const getDefaultParseDOMForLeafNode = (
elementName: string,
fieldName: string
) => [
const getDefaultParseDOMForLeafNode = (nodeName: string) => [
{
tag: "div",
getAttrs: (dom: Element) => {
const domFieldName = dom.getAttribute(fieldNameAttr);
if (domFieldName !== getNodeNameFromField(fieldName, elementName)) {
if (domFieldName !== nodeName) {
return false;
}

Expand All @@ -213,9 +209,6 @@ const getDefaultParseDOMForLeafNode = (
},
];

const getTagForNode = (elementName: string, fieldName: string) =>
`element-${elementName}-${fieldName}`.toLowerCase();

export const createNodesForFieldValues = <
S extends Schema,
FDesc extends FieldDescriptions<string>
Expand Down

0 comments on commit 25c71c8

Please sign in to comment.