diff --git a/cypress/e2e/rich-text/RichTextEditor.spec.ts b/cypress/e2e/rich-text/RichTextEditor.spec.ts
index 66ae1a02c..4b1888b9f 100644
--- a/cypress/e2e/rich-text/RichTextEditor.spec.ts
+++ b/cypress/e2e/rich-text/RichTextEditor.spec.ts
@@ -57,7 +57,7 @@ describe('Rich Text Editor', { viewportHeight: 2000 }, () => {
];
beforeEach(() => {
- cy.viewport(1000, 2000);
+ cy.viewport(1280, 720);
richText = new RichTextPage();
richText.visit();
});
@@ -392,7 +392,7 @@ describe('Rich Text Editor', { viewportHeight: 2000 }, () => {
// temporarily skipped. Snapshots don't match. Will be fixed in a follow up PR
// eslint-disable-next-line
- it.skip('runs initial normalization without triggering a value change', () => {
+ it('runs initial normalization without triggering a value change', () => {
cy.setInitialValue(validDocumentThatRequiresNormalization);
cy.reload();
diff --git a/packages/rich-text/src/RichTextEditor.tsx b/packages/rich-text/src/RichTextEditor.tsx
index c1f1bf7dd..163dc307b 100644
--- a/packages/rich-text/src/RichTextEditor.tsx
+++ b/packages/rich-text/src/RichTextEditor.tsx
@@ -4,14 +4,12 @@ import { FieldExtensionSDK } from '@contentful/app-sdk';
import { EntityProvider } from '@contentful/field-editor-reference';
import { FieldConnector } from '@contentful/field-editor-shared';
import * as Contentful from '@contentful/rich-text-types';
-import { Document } from '@contentful/rich-text-types';
import { Plate, PlateProvider } from '@udecode/plate-core';
import { css, cx } from 'emotion';
import deepEquals from 'fast-deep-equal';
import noop from 'lodash/noop';
import { ContentfulEditorIdProvider, getContentfulEditorId } from './ContentfulEditorProvider';
-import { createOnChangeCallback } from './helpers/callbacks';
import { toSlateValue } from './helpers/toSlateValue';
import { normalizeInitialValue } from './internal/misc';
import { getPlugins, disableCorePlugins } from './plugins';
@@ -43,8 +41,6 @@ export const ConnectedRichTextEditor = (props: ConnectedProps) => {
[sdk, onAction, restrictedMarks]
);
- const handleChange = props.onChange;
-
const initialValue = React.useMemo(() => {
return normalizeInitialValue(
{
@@ -55,14 +51,6 @@ export const ConnectedRichTextEditor = (props: ConnectedProps) => {
);
}, [props.value, plugins]);
- const onChange = React.useMemo(
- () =>
- createOnChangeCallback((document: Document) => {
- handleChange?.(document);
- }),
- [handleChange]
- );
-
const classNames = cx(
styles.editor,
props.minHeight !== undefined ? css({ minHeight: props.minHeight }) : undefined,
@@ -79,13 +67,13 @@ export const ConnectedRichTextEditor = (props: ConnectedProps) => {
initialValue={initialValue}
plugins={plugins}
disableCorePlugins={disableCorePlugins}
- onChange={onChange}>
+ >
{!props.isToolbarHidden && (
)}
-
+
{
field={sdk.field}
isInitiallyDisabled={isInitiallyDisabled}
isEmptyValue={isEmptyValue}
- isEqualValues={deepEquals}>
+ isEqualValues={deepEquals}
+ >
{({ lastRemoteValue, disabled, setValue }) => (
{
export type SyncEditorStateProps = {
incomingValue?: Value;
+ onChange?: (doc: Contentful.Document) => unknown;
};
/**
@@ -47,8 +51,29 @@ export type SyncEditorStateProps = {
* where we can no longer access the editor instance outside the Plate
* provider.
*/
-export const SyncEditorValue = ({ incomingValue }: SyncEditorStateProps) => {
+export const SyncEditorValue = ({ incomingValue, onChange }: SyncEditorStateProps) => {
const editor = usePlateSelectors().editor();
+ const setEditorOnChange = usePlateActions().onChange();
+
+ React.useEffect(() => {
+ const cb = createOnChangeCallback(onChange);
+
+ setEditorOnChange({
+ fn: (document) => {
+ console.log(editor.operations);
+ // Skip irrelevant events e.g. mouse selection
+ const operations = editor?.operations.filter((op) => {
+ return op.type !== 'set_selection';
+ });
+
+ if (operations.length === 0) {
+ return;
+ }
+
+ cb(document);
+ },
+ });
+ }, [editor, onChange, setEditorOnChange]);
// Cache latest editor value to avoid unnecessary updates
const lastIncomingValue = React.useRef(incomingValue);
diff --git a/packages/rich-text/src/helpers/callbacks.ts b/packages/rich-text/src/helpers/callbacks.ts
index 4949b03d9..35c761e9f 100644
--- a/packages/rich-text/src/helpers/callbacks.ts
+++ b/packages/rich-text/src/helpers/callbacks.ts
@@ -1,35 +1,20 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import { toContentfulDocument } from '@contentful/contentful-slatejs-adapter';
import { Document } from '@contentful/rich-text-types';
-import equal from 'fast-deep-equal';
import debounce from 'lodash/debounce';
import schema from '../constants/Schema';
import { removeInternalMarks } from './removeInternalMarks';
-export const createOnChangeCallback = (handler?: (value: Document) => void) => {
- // Cache previous value to avoid firing the handler unnecessarily
- //
- // Note: We are not using lodash/memoize here to avoid memory leaks
- // due to having an infinite cache while we only care about the last
- // value.
- let cache: unknown = null;
-
- return debounce((document: unknown) => {
- if (equal(document, cache)) {
- return;
- }
-
- cache = document;
+export const createOnChangeCallback = (handler?: (value: Document) => void) =>
+ debounce((document: unknown) => {
const doc = removeInternalMarks(
toContentfulDocument({
- // eslint-disable-next-line -- parameter type is not exported @typescript-eslint/no-explicit-any
document: document as any,
schema: schema,
- // eslint-disable-next-line -- parameter type is not exported @typescript-eslint/no-explicit-any
}) as any
);
- // eslint-disable-next-line -- correct parameter type is not defined @typescript-eslint/no-explicit-any
+
const cleanedDocument = removeInternalMarks(doc as Record);
handler?.(cleanedDocument);
}, 500);
-};