Skip to content

Commit 82b17af

Browse files
committed
TINY-11906: Change to support previous versions of TinyMCE
1 parent f72c164 commit 82b17af

8 files changed

Lines changed: 129 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## Unreleased
88

99
### Changed
10-
- `disabled` property is now mapped to the TinyMCE `disabled` option.
10+
- The `disabled` property now toggles the `disabled` option. #TINY-11906
1111

1212
### Added
13-
- Added `readonly` property that maps to the TinyMCE `readonly` option.
13+
- Added `readonly` property that can be used to toggle the `readonly` mode. #TINY-11906
1414

1515
## 6.1.0 - 2025-03-31
1616

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ This package is a thin wrapper around [TinyMCE](https://github.com/tinymce/tinym
1010
* For our quick demos, check out the TinyMCE React [Storybook](https://tinymce.github.io/tinymce-react/).
1111

1212

13-
### Support
14-
15-
Version 7.0 is intended to support TinyMCE version 7.6 and above.
16-
1713
### Issues
1814

1915
Have you found an issue with tinymce-react or do you have a feature request? Open up an [issue](https://github.com/tinymce/tinymce-react/issues) and let us know or submit a [pull request](https://github.com/tinymce/tinymce-react/pulls). *Note: For issues concerning TinyMCE please visit the [TinyMCE repository](https://github.com/tinymce/tinymce).*

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,15 @@
6868
"react-dom": "^19.0.0",
6969
"rimraf": "^6.0.1",
7070
"storybook": "^8.6.4",
71-
"tinymce": "^7.2.1",
71+
"tinymce": "^7",
7272
"tinymce-4": "npm:tinymce@^4",
7373
"tinymce-5": "npm:tinymce@^5",
7474
"tinymce-6": "npm:tinymce@^6",
7575
"tinymce-7": "npm:tinymce@^7",
76+
"tinymce-7.5": "npm:tinymce@7.5",
7677
"typescript": "~5.8.2",
7778
"vite": "^6.2.1"
7879
},
79-
"version": "7.0.0-rc",
80+
"version": "6.2.0-rc",
8081
"name": "@tinymce/tinymce-react"
8182
}

src/main/ts/Utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { eventPropTypes, IEventPropTypes } from './components/EditorPropTypes';
22
import { IAllProps } from './components/Editor';
33
import type { Editor as TinyMCEEditor, EditorEvent } from 'tinymce';
4+
import { getTinymce } from './TinyMCE';
5+
import { TinyVer } from '@tinymce/miniature';
46

57
export const isFunction = (x: unknown): x is Function => typeof x === 'function';
68

@@ -109,4 +111,19 @@ export const setMode = (editor: TinyMCEEditor | undefined, mode: 'readonly' | 'd
109111
(editor as any).setMode(mode);
110112
}
111113
}
114+
};
115+
116+
export const getTinymceOrError = (view: Window) => {
117+
const tinymce = getTinymce(view);
118+
if (!tinymce) {
119+
throw new Error('tinymce should have been loaded into global scope');
120+
}
121+
122+
return tinymce;
123+
};
124+
125+
export const isDisabledOptionSupported = (view: Window) => {
126+
const tinymce = getTinymceOrError(view);
127+
128+
return !TinyVer.isLessThan(tinymce, '7.6.0');
112129
};

src/main/ts/components/Editor.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ import * as React from 'react';
22
import type { Bookmark, EditorEvent, TinyMCE, Editor as TinyMCEEditor } from 'tinymce';
33
import { IEvents } from '../Events';
44
import { ScriptItem, ScriptLoader } from '../ScriptLoader2';
5-
import { getTinymce } from '../TinyMCE';
6-
import { configHandlers, isBeforeInputEventAvailable, isFunction, isInDoc, isTextareaOrInput, mergePlugins, setMode, uuid } from '../Utils';
5+
import { configHandlers, isBeforeInputEventAvailable,
6+
isFunction, isInDoc, isTextareaOrInput, mergePlugins,
7+
setMode, uuid, isDisabledOptionSupported,
8+
getTinymceOrError } from '../Utils';
79
import { EditorPropTypes, IEditorPropTypes } from './EditorPropTypes';
10+
import { getTinymce } from '../TinyMCE';
811

912
const changeEvents = 'change keyup compositionend setcontent CommentChange';
1013

@@ -14,7 +17,7 @@ interface DoNotUse<T extends string> {
1417
__brand: T;
1518
}
1619

17-
type OmittedInitProps = 'selector' | 'target' | 'readonly' | 'license_key';
20+
type OmittedInitProps = 'selector' | 'target' | 'readonly' | 'disabled' | 'license_key';
1821

1922
type EditorOptions = Parameters<TinyMCE['init']>[0];
2023

@@ -222,7 +225,11 @@ export class Editor extends React.Component<IAllProps> {
222225
}
223226

224227
if (this.props.disabled !== prevProps.disabled) {
225-
this.editor.options.set('disabled', this.props.disabled);
228+
if (isDisabledOptionSupported(this.view)) {
229+
this.editor.options.set('disabled', this.props.disabled);
230+
} else {
231+
setMode(this.editor, this.props.disabled ? 'readonly' : 'design');
232+
}
226233
}
227234
}
228235
}
@@ -442,16 +449,16 @@ export class Editor extends React.Component<IAllProps> {
442449
return;
443450
}
444451

445-
const tinymce = getTinymce(this.view);
446-
if (!tinymce) {
447-
throw new Error('tinymce should have been loaded into global scope');
448-
}
452+
const tinymce = getTinymceOrError(this.view);
449453

450454
const finalInit: EditorOptions = {
451455
...this.props.init as Omit<InitOptions, OmittedInitProps>,
452456
selector: undefined,
453457
target,
454-
disabled: this.props.disabled,
458+
...isDisabledOptionSupported(this.view)
459+
? { disabled: this.props.disabled, readonly: this.props.readonly }
460+
: { readonly: this.props.disabled || this.props.readonly }
461+
,
455462
inline: this.inline,
456463
plugins: mergePlugins(this.props.init?.plugins, this.props.plugins),
457464
toolbar: this.props.toolbar ?? this.props.init?.toolbar,
@@ -488,8 +495,7 @@ export class Editor extends React.Component<IAllProps> {
488495
editor.undoManager.add();
489496
editor.setDirty(false);
490497
}
491-
const readonly = this.props.readonly ?? false;
492-
setMode(this.editor, readonly ? 'readonly' : 'design');
498+
493499
// ensure existing init_instance_callback is called
494500
if (this.props.init && isFunction(this.props.init.init_instance_callback)) {
495501
this.props.init.init_instance_callback(editor);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { context, describe, it } from '@ephox/bedrock-client';
2+
import * as Loader from '../alien/Loader';
3+
import { Assertions, Waiter } from '@ephox/agar';
4+
5+
describe('EditorDisabledTest', () => {
6+
7+
context('with TinyMCE before 7.6', () => {
8+
([ '7.5' ] as Array<any>).forEach((version) =>
9+
Loader.withVersion(version, (render) => {
10+
it('updating disabled prop should toggle the editor\'s mode', async () => {
11+
using ctx = await render({
12+
disabled: true
13+
});
14+
15+
Assertions.assertEq('mode is readonly', 'readonly', ctx.editor.mode.get());
16+
17+
await ctx.reRender({
18+
disabled: false
19+
});
20+
await Waiter.pTryUntil('mode is changed to design', () => {
21+
Assertions.assertEq('mode is design', 'design', ctx.editor.mode.get());
22+
});
23+
});
24+
25+
it('updating readonly prop should toggle the editor\'s mode', async () => {
26+
using ctx = await render({
27+
readonly: true
28+
});
29+
Assertions.assertEq('mode is readonly', 'readonly', ctx.editor.mode.get());
30+
31+
await ctx.reRender({
32+
readonly: false
33+
});
34+
await Waiter.pTryUntil('mode is changed to design', () => {
35+
Assertions.assertEq('mode is design', 'design', ctx.editor.mode.get());
36+
});
37+
});
38+
})
39+
);
40+
});
41+
42+
context('with TinyMCE 7.6 and above', () => {
43+
([ '7' ] as Array<any>).forEach((version) =>
44+
Loader.withVersion(version, (render) => {
45+
it('updating disabled prop should only change the editor\'s state', async () => {
46+
using ctx = await render({
47+
disabled: true
48+
});
49+
50+
Assertions.assertEq('mode is design', 'design', ctx.editor.mode.get());
51+
Assertions.assertEq('editor is disabled', true, ctx.editor.options.get('disabled'));
52+
53+
await ctx.reRender({
54+
disabled: false
55+
});
56+
57+
await Waiter.pTryUntil('editor\'s state should be updated', () => {
58+
Assertions.assertEq('mode is design', 'design', ctx.editor.mode.get());
59+
Assertions.assertEq('editor is not disabled', false, ctx.editor.options.get('disabled'));
60+
});
61+
});
62+
63+
it('updating readonly prop should only change the editor\'s mode', async () => {
64+
using ctx = await render({
65+
readonly: true
66+
});
67+
Assertions.assertEq('mode is readonly', 'readonly', ctx.editor.mode.get());
68+
Assertions.assertEq('editor is not disabled', false, ctx.editor.options.get('disabled'));
69+
70+
await ctx.reRender({
71+
readonly: false
72+
});
73+
await Waiter.pTryUntil('editor\'s mode should be updated', () => {
74+
Assertions.assertEq('mode is design', 'design', ctx.editor.mode.get());
75+
Assertions.assertEq('editor is not disabled', false, ctx.editor.options.get('disabled'));
76+
});
77+
});
78+
})
79+
);
80+
});
81+
});

src/test/ts/browser/EditorInitTest.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,29 +69,6 @@ describe('EditorInitTest', () => {
6969
TinyAssertions.assertContent(ctx.editor, '<p>New Value</p>');
7070
});
7171

72-
it('Disabled prop should disable editor', async function () {
73-
using ctx = await render();
74-
const editor = ctx.editor;
75-
const minorVersion = parseInt(editor.editorManager.minorVersion, 10);
76-
// disabled option was re-introduced in tinymce 7.6
77-
if (version !== '7' || minorVersion < 6) {
78-
this.skip();
79-
}
80-
81-
Assertions.assertEq('Should be enabled', false, editor.options.get('disabled'));
82-
await ctx.reRender({ ...defaultProps, disabled: true });
83-
Assertions.assertEq('Should be disabled', true,
84-
editor.options.get('disabled'));
85-
});
86-
87-
it('Readonly prop should set editor to readonly mode', async () => {
88-
using ctx = await render();
89-
Assertions.assertEq('Should be design mode', true, '4' === version ? !ctx.editor.readonly : ctx.editor.mode.get() === 'design');
90-
await ctx.reRender({ ...defaultProps, disabled: true });
91-
Assertions.assertEq('Should be readonly mode', true, '4' === version ? ctx.editor.readonly : ctx.editor.mode.get() === 'readonly');
92-
93-
});
94-
9572
it('Using an overriden props will cause a TS error', async () => {
9673
// eslint-disable-next-line @typescript-eslint/no-unused-vars
9774
using _ = await render({

yarn.lock

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8705,12 +8705,17 @@ tiny-invariant@^1.3.1, tiny-invariant@^1.3.3:
87058705
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-6.8.4.tgz#53e1313ebfe5524b24c0fa45937d51fda058632d"
87068706
integrity sha512-okoJyxuPv1gzASxQDNgQbnUXOdAIyoOSXcXcZZu7tiW0PSKEdf3SdASxPBupRj+64/E3elHwVRnzSdo82Emqbg==
87078707

8708+
"tinymce-7.5@npm:tinymce@7.5":
8709+
version "7.5.1"
8710+
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.5.1.tgz#4c207ab930d3a073bf851ddd3a8aa44d8d94d7bd"
8711+
integrity sha512-GRXJUB0BEIOUHUEC+q9IjsgWGIAQ4Tn5t5hfpB/YR7No3oPgKHG03v1d3nbov9aqdyVW7Be+UD4I3ZerQG30VQ==
8712+
87088713
"tinymce-7@npm:tinymce@^7":
8709-
version "7.2.1"
8710-
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.2.1.tgz#9b4f6b5a0fa647e2953c174ac69aa47483683332"
8711-
integrity sha512-ADd1cvdIuq6NWyii0ZOZRuu+9sHIdQfcRNWBcBps2K8vy7OjlRkX6iw7zz1WlL9kY4z4L1DvIP+xOrVX/46aHA==
8714+
version "7.8.0"
8715+
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.8.0.tgz#d57a597aecdc2108f2dd68fe74c6099c0a0ef66f"
8716+
integrity sha512-MUER5MWV9mkOB4expgbWknh/C5ZJvOXQlMVSx4tJxTuYtcUCDB6bMZ34fWNOIc8LvrnXmGHGj0eGQuxjQyRgrA==
87128717

8713-
tinymce@^7.2.1:
8718+
tinymce@^7:
87148719
version "7.8.0"
87158720
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.8.0.tgz#d57a597aecdc2108f2dd68fe74c6099c0a0ef66f"
87168721
integrity sha512-MUER5MWV9mkOB4expgbWknh/C5ZJvOXQlMVSx4tJxTuYtcUCDB6bMZ34fWNOIc8LvrnXmGHGj0eGQuxjQyRgrA==

0 commit comments

Comments
 (0)