@contentful/field-editor-rich-text 3.8.1 → 3.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,6 +27,7 @@ const _noop = _interop_require_default(require("lodash/noop"));
27
27
  const _ContentfulEditorProvider = require("./ContentfulEditorProvider");
28
28
  const _callbacks = require("./helpers/callbacks");
29
29
  const _toSlateValue = require("./helpers/toSlateValue");
30
+ const _misc = require("./internal/misc");
30
31
  const _plugins = require("./plugins");
31
32
  const _RichTextEditorstyles = require("./RichTextEditor.styles");
32
33
  const _SdkProvider = require("./SdkProvider");
@@ -78,32 +79,33 @@ function _interop_require_wildcard(obj, nodeInterop) {
78
79
  return newObj;
79
80
  }
80
81
  const ConnectedRichTextEditor = (props)=>{
81
- const id = (0, _ContentfulEditorProvider.getContentfulEditorId)(props.sdk);
82
- const plugins = _react.useMemo(()=>(0, _plugins.getPlugins)(props.sdk, props.onAction ?? _noop.default, props.restrictedMarks), [
83
- props.sdk,
84
- props.onAction,
85
- props.restrictedMarks
82
+ const { sdk , onAction , restrictedMarks } = props;
83
+ const id = (0, _ContentfulEditorProvider.getContentfulEditorId)(sdk);
84
+ const plugins = _react.useMemo(()=>(0, _plugins.getPlugins)(sdk, onAction ?? _noop.default, restrictedMarks), [
85
+ sdk,
86
+ onAction,
87
+ restrictedMarks
86
88
  ]);
87
89
  const handleChange = props.onChange;
88
- const isFirstRender = _react.useRef(true);
89
- const value = (0, _toSlateValue.toSlateValue)(props.value);
90
+ const initialValue = _react.useMemo(()=>{
91
+ return (0, _misc.normalizeInitialValue)({
92
+ plugins,
93
+ disableCorePlugins: _plugins.disableCorePlugins
94
+ }, (0, _toSlateValue.toSlateValue)(props.value));
95
+ }, [
96
+ props.value,
97
+ plugins
98
+ ]);
90
99
  const onChange = _react.useMemo(()=>(0, _callbacks.createOnChangeCallback)((document)=>{
91
- if (!isFirstRender.current && handleChange) {
92
- handleChange(document);
93
- }
100
+ handleChange?.(document);
94
101
  }), [
95
102
  handleChange
96
103
  ]);
97
- const firstInteractionHandler = _react.useCallback(()=>{
98
- isFirstRender.current = false;
99
- }, [
100
- isFirstRender
101
- ]);
102
104
  const classNames = (0, _emotion.cx)(_RichTextEditorstyles.styles.editor, props.minHeight !== undefined ? (0, _emotion.css)({
103
105
  minHeight: props.minHeight
104
106
  }) : undefined, props.isDisabled ? _RichTextEditorstyles.styles.disabled : _RichTextEditorstyles.styles.enabled, props.isToolbarHidden && _RichTextEditorstyles.styles.hiddenToolbar);
105
107
  return _react.createElement(_SdkProvider.SdkProvider, {
106
- sdk: props.sdk
108
+ sdk: sdk
107
109
  }, _react.createElement(_ContentfulEditorProvider.ContentfulEditorIdProvider, {
108
110
  value: id
109
111
  }, _react.createElement("div", {
@@ -111,8 +113,7 @@ const ConnectedRichTextEditor = (props)=>{
111
113
  "data-test-id": "rich-text-editor"
112
114
  }, _react.createElement(_platecore.PlateProvider, {
113
115
  id: id,
114
- initialValue: value,
115
- normalizeInitialValue: true,
116
+ initialValue: initialValue,
116
117
  plugins: plugins,
117
118
  disableCorePlugins: _plugins.disableCorePlugins,
118
119
  onChange: onChange
@@ -121,15 +122,12 @@ const ConnectedRichTextEditor = (props)=>{
121
122
  }, _react.createElement(_Toolbar.default, {
122
123
  isDisabled: props.isDisabled
123
124
  })), _react.createElement(_SyncEditorValue.SyncEditorValue, {
124
- incomingValue: value
125
+ incomingValue: initialValue
125
126
  }), _react.createElement(_platecore.Plate, {
126
127
  id: id,
127
128
  editableProps: {
128
129
  className: classNames,
129
- readOnly: props.isDisabled,
130
- onKeyDown: firstInteractionHandler,
131
- onChange: firstInteractionHandler,
132
- onClick: firstInteractionHandler
130
+ readOnly: props.isDisabled
133
131
  }
134
132
  })))));
135
133
  };
@@ -12,6 +12,9 @@ _export(exports, {
12
12
  createPlateEditor: function() {
13
13
  return createPlateEditor;
14
14
  },
15
+ normalizeInitialValue: function() {
16
+ return normalizeInitialValue;
17
+ },
15
18
  withoutNormalizing: function() {
16
19
  return withoutNormalizing;
17
20
  },
@@ -32,6 +35,7 @@ _export(exports, {
32
35
  }
33
36
  });
34
37
  const _platecore = _interop_require_wildcard(require("@udecode/plate-core"));
38
+ const _transforms = require("./transforms");
35
39
  function _getRequireWildcardCache(nodeInterop) {
36
40
  if (typeof WeakMap !== "function") return null;
37
41
  var cacheBabelInterop = new WeakMap();
@@ -74,6 +78,16 @@ function _interop_require_wildcard(obj, nodeInterop) {
74
78
  const createPlateEditor = (options = {})=>{
75
79
  return _platecore.createPlateEditor(options);
76
80
  };
81
+ const normalizeInitialValue = (options, initialValue)=>{
82
+ const editor = createPlateEditor(options);
83
+ if (initialValue) {
84
+ editor.children = initialValue;
85
+ }
86
+ (0, _transforms.normalize)(editor, {
87
+ force: true
88
+ });
89
+ return editor.children;
90
+ };
77
91
  const withoutNormalizing = (editor, fn)=>{
78
92
  return _platecore.withoutNormalizing(editor, fn);
79
93
  };
@@ -8,7 +8,6 @@ Object.defineProperty(exports, "createTestEditor", {
8
8
  return createTestEditor;
9
9
  }
10
10
  });
11
- const _platecore = require("@udecode/plate-core");
12
11
  const _internal = require("../internal");
13
12
  const _plugins = require("../plugins");
14
13
  const _randomId = require("./randomId");
@@ -19,10 +18,11 @@ const createTestEditor = (options)=>{
19
18
  validation: []
20
19
  }
21
20
  };
22
- const editor = (0, _platecore.createPlateEditor)({
21
+ const editor = (0, _internal.createPlateEditor)({
23
22
  id: (0, _randomId.randomId)('editor'),
24
23
  editor: options.input,
25
- plugins: options.plugins || (0, _plugins.getPlugins)(sdk, trackingHandler)
24
+ plugins: options.plugins || (0, _plugins.getPlugins)(sdk, trackingHandler),
25
+ normalizeInitialValue: false
26
26
  });
27
27
  return {
28
28
  editor,
@@ -9,6 +9,7 @@ import noop from 'lodash/noop';
9
9
  import { ContentfulEditorIdProvider, getContentfulEditorId } from './ContentfulEditorProvider';
10
10
  import { createOnChangeCallback } from './helpers/callbacks';
11
11
  import { toSlateValue } from './helpers/toSlateValue';
12
+ import { normalizeInitialValue } from './internal/misc';
12
13
  import { getPlugins, disableCorePlugins } from './plugins';
13
14
  import { styles } from './RichTextEditor.styles';
14
15
  import { SdkProvider } from './SdkProvider';
@@ -16,32 +17,33 @@ import { SyncEditorValue } from './SyncEditorValue';
16
17
  import Toolbar from './Toolbar';
17
18
  import StickyToolbarWrapper from './Toolbar/components/StickyToolbarWrapper';
18
19
  export const ConnectedRichTextEditor = (props)=>{
19
- const id = getContentfulEditorId(props.sdk);
20
- const plugins = React.useMemo(()=>getPlugins(props.sdk, props.onAction ?? noop, props.restrictedMarks), [
21
- props.sdk,
22
- props.onAction,
23
- props.restrictedMarks
20
+ const { sdk , onAction , restrictedMarks } = props;
21
+ const id = getContentfulEditorId(sdk);
22
+ const plugins = React.useMemo(()=>getPlugins(sdk, onAction ?? noop, restrictedMarks), [
23
+ sdk,
24
+ onAction,
25
+ restrictedMarks
24
26
  ]);
25
27
  const handleChange = props.onChange;
26
- const isFirstRender = React.useRef(true);
27
- const value = toSlateValue(props.value);
28
+ const initialValue = React.useMemo(()=>{
29
+ return normalizeInitialValue({
30
+ plugins,
31
+ disableCorePlugins
32
+ }, toSlateValue(props.value));
33
+ }, [
34
+ props.value,
35
+ plugins
36
+ ]);
28
37
  const onChange = React.useMemo(()=>createOnChangeCallback((document)=>{
29
- if (!isFirstRender.current && handleChange) {
30
- handleChange(document);
31
- }
38
+ handleChange?.(document);
32
39
  }), [
33
40
  handleChange
34
41
  ]);
35
- const firstInteractionHandler = React.useCallback(()=>{
36
- isFirstRender.current = false;
37
- }, [
38
- isFirstRender
39
- ]);
40
42
  const classNames = cx(styles.editor, props.minHeight !== undefined ? css({
41
43
  minHeight: props.minHeight
42
44
  }) : undefined, props.isDisabled ? styles.disabled : styles.enabled, props.isToolbarHidden && styles.hiddenToolbar);
43
45
  return React.createElement(SdkProvider, {
44
- sdk: props.sdk
46
+ sdk: sdk
45
47
  }, React.createElement(ContentfulEditorIdProvider, {
46
48
  value: id
47
49
  }, React.createElement("div", {
@@ -49,8 +51,7 @@ export const ConnectedRichTextEditor = (props)=>{
49
51
  "data-test-id": "rich-text-editor"
50
52
  }, React.createElement(PlateProvider, {
51
53
  id: id,
52
- initialValue: value,
53
- normalizeInitialValue: true,
54
+ initialValue: initialValue,
54
55
  plugins: plugins,
55
56
  disableCorePlugins: disableCorePlugins,
56
57
  onChange: onChange
@@ -59,15 +60,12 @@ export const ConnectedRichTextEditor = (props)=>{
59
60
  }, React.createElement(Toolbar, {
60
61
  isDisabled: props.isDisabled
61
62
  })), React.createElement(SyncEditorValue, {
62
- incomingValue: value
63
+ incomingValue: initialValue
63
64
  }), React.createElement(Plate, {
64
65
  id: id,
65
66
  editableProps: {
66
67
  className: classNames,
67
- readOnly: props.isDisabled,
68
- onKeyDown: firstInteractionHandler,
69
- onChange: firstInteractionHandler,
70
- onClick: firstInteractionHandler
68
+ readOnly: props.isDisabled
71
69
  }
72
70
  })))));
73
71
  };
@@ -1,7 +1,18 @@
1
1
  import * as p from '@udecode/plate-core';
2
+ import { normalize } from './transforms';
2
3
  export const createPlateEditor = (options = {})=>{
3
4
  return p.createPlateEditor(options);
4
5
  };
6
+ export const normalizeInitialValue = (options, initialValue)=>{
7
+ const editor = createPlateEditor(options);
8
+ if (initialValue) {
9
+ editor.children = initialValue;
10
+ }
11
+ normalize(editor, {
12
+ force: true
13
+ });
14
+ return editor.children;
15
+ };
5
16
  export const withoutNormalizing = (editor, fn)=>{
6
17
  return p.withoutNormalizing(editor, fn);
7
18
  };
@@ -1,5 +1,4 @@
1
- import { createPlateEditor } from '@udecode/plate-core';
2
- import { normalize } from '../internal';
1
+ import { normalize, createPlateEditor } from '../internal';
3
2
  import { getPlugins } from '../plugins';
4
3
  import { randomId } from './randomId';
5
4
  export const createTestEditor = (options)=>{
@@ -12,7 +11,8 @@ export const createTestEditor = (options)=>{
12
11
  const editor = createPlateEditor({
13
12
  id: randomId('editor'),
14
13
  editor: options.input,
15
- plugins: options.plugins || getPlugins(sdk, trackingHandler)
14
+ plugins: options.plugins || getPlugins(sdk, trackingHandler),
15
+ normalizeInitialValue: false
16
16
  });
17
17
  return {
18
18
  editor,
@@ -26,6 +26,28 @@ export declare const createPlateEditor: (options?: CreatePlateEditorOptions) =>
26
26
  childrenFactory: () => Value;
27
27
  currentKeyboardEvent: import("react").KeyboardEvent<Element> | null;
28
28
  };
29
+ /**
30
+ * The only reason for this helper to exist is to run the initial normalization
31
+ * before mounting the Plate editor component which in turn avoids the false
32
+ * trigger of `onChange`.
33
+ *
34
+ * Background:
35
+ *
36
+ * Due to legacy behavior, it's possible to have "valid" RT document (based on
37
+ * the schema from rich-text-types) that doesn't make sense. For example, links
38
+ * with no text nodes?[1]. Solving that requires an initial normalization pass
39
+ * which modifies the slate tree by definition -> triggering onChange.
40
+ *
41
+ * The initial onChange trigger is undesirable as the user may not have touched
42
+ * the RT content yet or the editor is rendered as readonly.
43
+ *
44
+ * Ideally, we should not initialize the editor twice but that's the only
45
+ * way that I could get this to work. Improvements are welcome.
46
+ *
47
+ * [1]: See cypress/e2e/rich-text/.../invalidDocumentNormalizable.js for more
48
+ * examples.
49
+ */
50
+ export declare const normalizeInitialValue: (options: CreatePlateEditorOptions, initialValue?: Value) => Value;
29
51
  export declare const withoutNormalizing: (editor: PlateEditor, fn: () => boolean | void) => boolean;
30
52
  export declare const focusEditor: (editor: PlateEditor, target?: Location) => void;
31
53
  export declare const blurEditor: (editor: PlateEditor) => void;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  import { FieldExtensionSDK } from '@contentful/app-sdk';
3
- import { PlateEditor, PlatePlugin, Value } from '../internal/types';
3
+ import { PlatePlugin } from '../internal/types';
4
4
  import { RichTextTrackingActionHandler } from '../plugins/Tracking';
5
5
  export declare const createTestEditor: (options: {
6
6
  input?: any;
@@ -8,8 +8,8 @@ export declare const createTestEditor: (options: {
8
8
  trackingHandler?: RichTextTrackingActionHandler;
9
9
  plugins?: PlatePlugin[];
10
10
  }) => {
11
- editor: PlateEditor & Omit<import("slate").BaseEditor, "children" | "operations" | "marks" | "isInline" | "isVoid" | "normalizeNode" | "apply" | "getFragment" | "insertFragment" | "insertNode"> & {
12
- children: Value;
11
+ editor: import("../internal").PlateEditor & Omit<import("slate").BaseEditor, "children" | "operations" | "marks" | "isInline" | "isVoid" | "normalizeNode" | "apply" | "getFragment" | "insertFragment" | "insertNode"> & {
12
+ children: import("../internal").Value;
13
13
  operations: import("@udecode/plate-core").TOperation<import("@udecode/plate-core").TDescendant>[];
14
14
  marks: Record<string, any> | null;
15
15
  isInline: <N extends import("@udecode/plate-core").TElement>(element: N) => boolean;
@@ -22,11 +22,11 @@ export declare const createTestEditor: (options: {
22
22
  } & import("@udecode/plate-core").UnknownObject & Pick<import("slate-history").HistoryEditor, "history" | "undo" | "redo"> & Pick<import("slate-react").ReactEditor, "insertData" | "insertFragmentData" | "setFragmentData" | "insertTextData" | "hasRange"> & {
23
23
  key: any;
24
24
  id: string;
25
- plugins: import("@udecode/plate-core").WithPlatePlugin<{}, Value, import("@udecode/plate-core").PlateEditor<Value>>[];
26
- pluginsByKey: Record<string, import("@udecode/plate-core").WithPlatePlugin<{}, Value, import("@udecode/plate-core").PlateEditor<Value>>>;
25
+ plugins: import("@udecode/plate-core").WithPlatePlugin<{}, import("../internal").Value, import("@udecode/plate-core").PlateEditor<import("../internal").Value>>[];
26
+ pluginsByKey: Record<string, import("@udecode/plate-core").WithPlatePlugin<{}, import("../internal").Value, import("@udecode/plate-core").PlateEditor<import("../internal").Value>>>;
27
27
  prevSelection: import("slate").BaseRange | null;
28
28
  blockFactory: (node?: Partial<import("@udecode/plate-core").TElement> | undefined, path?: import("slate").Path | undefined) => import("@udecode/plate-core").TElement;
29
- childrenFactory: () => Value;
29
+ childrenFactory: () => import("../internal").Value;
30
30
  currentKeyboardEvent: import("react").KeyboardEvent<Element> | null;
31
31
  };
32
32
  normalize: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/field-editor-rich-text",
3
- "version": "3.8.1",
3
+ "version": "3.8.3",
4
4
  "source": "./src/index.tsx",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -44,7 +44,7 @@
44
44
  "@contentful/f36-icons": "^4.1.1",
45
45
  "@contentful/f36-tokens": "^4.0.0",
46
46
  "@contentful/f36-utils": "^4.19.0",
47
- "@contentful/field-editor-reference": "^5.12.0",
47
+ "@contentful/field-editor-reference": "^5.13.1",
48
48
  "@contentful/field-editor-shared": "^1.3.0",
49
49
  "@contentful/rich-text-plain-text-renderer": "^16.0.4",
50
50
  "@contentful/rich-text-types": "16.1.0",
@@ -81,5 +81,5 @@
81
81
  "prism-react-renderer": "2.0.5",
82
82
  "react": ">=16.14.0"
83
83
  },
84
- "gitHead": "f05cad6474475af4d6f4bfd8ad261432878a7f57"
84
+ "gitHead": "b7e8ebd1b812512d967805061f812350b4cb08bf"
85
85
  }