Skip to content

Commit

Permalink
INT-3292: Support for TinyMCE v7 (#378)
Browse files Browse the repository at this point in the history
* INT-3292: Added tinymce v7 to possible deps

* INT-3292: Add input and composition related events

* INT-3292: Remove unnecessary ng-template

* INT-3292: Added Version type and default `cloudChannel` is now set to 7

* INT-3292: Updated README

* INT-3292: Use `?` instead of `| undefined`

* INT-3292: Added missing apiKey props to stories

* INT-3292: Added the `licenseKey` prop

* INT-3292: Added a JSDoc link to the TinyMCE Angular technical reference doc page

* INT-3292: Test against TinyMCE v7 as well

* INT-3292: Updated package version

* INT-3292: Updated changelog

* INT-3292: Add missing `validEvents`

* INT-3292: Edited readme, changelog and package peer deps to state the support for Angular 16+ only
  • Loading branch information
danoaky-tiny authored Apr 24, 2024
1 parent b5cda49 commit 8dca388
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 44 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## 7.1.1 - 2024-04-04
## 8.0.0 - 2024-04-24
### Fixed
- Updated CI library to latest
- Updated dependencies. #INT-3274
- Usage of RxJS deprecated operators. #INT-3274
### Added
- Support for Angular 15, 16 & 17. #INT-3274
- Support for Angular 16 & 17. Angular 15 is still supported via ^7.0.0 #INT-3274
- Support for the OnPush change detection strategy. #INT-2974
- Support for TinyMCE version 7. #INT-3292
- Added `licenseKey` prop. #INT-3292
- Added events `onInput`, `onCompositionEnd`, `onCompositionStart` & `onCompositionUpdate`. #INT-3292
- Added a JSDoc link to the TinyMCE 7 Angular Technical Reference docs page. #INT-3292
### Improved
- Updated Storybook to v8, as well as now using CSFv3 components. #INT-3274
- Improved `cloudChannel` type. #INT-3292

## 7.0.0 - 2022-06-27
### Added
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,32 @@

This package is a thin wrapper around [TinyMCE](https://github.com/tinymce/tinymce) to make it easier to use in an Angular application.

* If you need detailed documentation on TinyMCE, see: [TinyMCE Documentation](https://www.tiny.cloud/docs/tinymce/6/).
* For the TinyMCE Angular Quick Start, see: [TinyMCE Documentation - Angular Integration](https://www.tiny.cloud/docs/tinymce/6/angular-cloud/).
* For the TinyMCE Angular Technical Reference, see: [TinyMCE Documentation - TinyMCE Angular Technical Reference](https://www.tiny.cloud/docs/tinymce/6/angular-ref/).
* If you need detailed documentation on TinyMCE, see: [TinyMCE Documentation](https://www.tiny.cloud/docs/tinymce/7/).
* For the TinyMCE Angular Quick Start, see: [TinyMCE Documentation - Angular Integration](https://www.tiny.cloud/docs/tinymce/7/angular-cloud/).
* For the TinyMCE Angular Technical Reference, see: [TinyMCE Documentation - TinyMCE Angular Technical Reference](https://www.tiny.cloud/docs/tinymce/7/angular-ref/).
* For our quick demos, check out the TinyMCE Angular [Storybook](https://tinymce.github.io/tinymce-angular/).

### Support

For Angular 16+, use integration version 8.x:

`npm install @tinymce/tinymce-angular@^8`

For Angular 14+, use integration version 7.x:

`npm install @tinymce/tinymce-angular@^7.0.0`
`npm install @tinymce/tinymce-angular@^7`

For Angular 13+, use integration version 6.x:

`npm install @tinymce/tinymce-angular@^6.0.0`
`npm install @tinymce/tinymce-angular@^6`

For Angular 9+, use integration version 4.x:

`npm install @tinymce/tinymce-angular@^4.0.0`
`npm install @tinymce/tinymce-angular@^4`

For Angular 8 and below use integration version 3.x:

`npm install @tinymce/tinymce-angular@^3.0.0`
`npm install @tinymce/tinymce-angular@^3`

Versions below Angular 5 are not supported.

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"tinymce-4": "npm:tinymce@^4",
"tinymce-5": "npm:tinymce@^5",
"tinymce-6": "npm:tinymce@^6",
"tinymce-7": "npm:tinymce@^7",
"to-string-loader": "^1.1.5",
"tslib": "^2.6.2",
"typescript": "~5.4.3",
Expand All @@ -71,6 +72,6 @@
"dependencies": {
"tinymce": "^5.10.7"
},
"version": "7.1.1-rc.2",
"version": "8.0.0-rc",
"name": "@tinymce/tinymce-angular"
}
2 changes: 2 additions & 0 deletions stories/form-control/FormControl.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/* eslint-disable no-console */
import { Component } from '@angular/core';
import { FormBuilder, FormControl } from '@angular/forms';
import { apiKey } from 'stories/Settings';

@Component({
selector: 'form-control',
templateUrl: './FormControl.component.html',
})
export class FormControlComponent {
public apiKey = apiKey;
public formControl: FormControl<string | null>;

// eslint-disable-next-line @typescript-eslint/no-parameter-properties
Expand Down
3 changes: 2 additions & 1 deletion stories/form-with-on-push/form-with-on-push.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import {
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import type { EditorComponent } from '../../tinymce-angular-component/src/main/ts/public_api';
import { apiKey } from 'stories/Settings';

@Component({
selector: 'form-with-on-push',
templateUrl: './form-with-on-push.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FormWithOnPushComponent {
@Input() public apiKey = '';
@Input() public apiKey = apiKey;
public readonly initialValue = '';
public readonly init: EditorComponent['init'] = {
plugins: [ 'help' ],
Expand Down
1 change: 1 addition & 0 deletions stories/form-with-on-push/form-with-on-push.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<form [formGroup]="form" style="display: flex; gap: 0.2em; flex-direction: column;">
<editor
[apiKey]="apiKey"
formControlName="tiny"
plugins="table"
init="{ width: '200px', }"
Expand Down
8 changes: 4 additions & 4 deletions tinymce-angular-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"license": "MIT",
"private": false,
"peerDependencies": {
"@angular/core": ">=14.0.0",
"@angular/common": ">=14.0.0",
"@angular/forms": ">=14.0.0"
"@angular/core": ">=16.0.0",
"@angular/common": ">=16.0.0",
"@angular/forms": ">=16.0.0"
},
"dependencies": {
"tinymce": "^6.0.0 || ^5.5.0"
"tinymce": "^7.0.0 || ^6.0.0 || ^5.5.0"
}
}
8 changes: 8 additions & 0 deletions tinymce-angular-component/src/main/ts/editor/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export class Events {
@Output() public onBeforePaste: EventEmitter<EventObj<ClipboardEvent>> = new EventEmitter();
@Output() public onBlur: EventEmitter<EventObj<FocusEvent>> = new EventEmitter();
@Output() public onClick: EventEmitter<EventObj<MouseEvent>> = new EventEmitter();
@Output() public onCompositionEnd: EventEmitter<EventObj<CompositionEvent>> = new EventEmitter();
@Output() public onCompositionStart: EventEmitter<EventObj<CompositionEvent>> = new EventEmitter();
@Output() public onCompositionUpdate: EventEmitter<EventObj<CompositionEvent>> = new EventEmitter();
@Output() public onContextMenu: EventEmitter<EventObj<MouseEvent>> = new EventEmitter();
@Output() public onCopy: EventEmitter<EventObj<ClipboardEvent>> = new EventEmitter();
@Output() public onCut: EventEmitter<EventObj<ClipboardEvent>> = new EventEmitter();
Expand Down Expand Up @@ -51,6 +54,7 @@ export class Events {
@Output() public onGetContent: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onHide: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onInit: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onInput: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onInitNgModel: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onLoadContent: EventEmitter<EventObj<any>> = new EventEmitter();
@Output() public onNodeChange: EventEmitter<EventObj<any>> = new EventEmitter();
Expand Down Expand Up @@ -88,6 +92,9 @@ export const validEvents: (keyof Events)[] = [
'onChange',
'onClearUndos',
'onClick',
'onCompositionEnd',
'onCompositionStart',
'onCompositionUpdate',
'onContextMenu',
'onCopy',
'onCut',
Expand All @@ -107,6 +114,7 @@ export const validEvents: (keyof Events)[] = [
'onGetContent',
'onHide',
'onInit',
'onInput',
'onKeyDown',
'onKeyPress',
'onKeyUp',
Expand Down
36 changes: 22 additions & 14 deletions tinymce-angular-component/src/main/ts/editor/editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,37 @@ const EDITOR_COMPONENT_VALUE_ACCESSOR = {
multi: true
};

export type Version = `${'4' | '5' | '6' | '7'}${'' | '-dev' | '-testing' | `.${number}` | `.${number}.${number}`}`;

@Component({
selector: 'editor',
template: '<ng-template></ng-template>',
template: '',
styles: [ ':host { display: block; }' ],
providers: [ EDITOR_COMPONENT_VALUE_ACCESSOR ],
standalone: true,
imports: [ CommonModule, FormsModule ],
changeDetection: ChangeDetectionStrategy.OnPush
})

/**
* @see {@link https://www.tiny.cloud/docs/tinymce/7/angular-ref/} for the TinyMCE Angular Technical Reference
*/
export class EditorComponent extends Events implements AfterViewInit, ControlValueAccessor, OnDestroy {

@Input() public cloudChannel = '6';
@Input() public cloudChannel: Version = '7';
@Input() public apiKey = 'no-api-key';
@Input() public init: EditorOptions | undefined;
@Input() public licenseKey?: string;
@Input() public init?: EditorOptions;
@Input() public id = '';
@Input() public initialValue: string | undefined;
@Input() public outputFormat: 'html' | 'text' | undefined;
@Input() public inline: boolean | undefined;
@Input() public tagName: string | undefined;
@Input() public plugins: string | undefined;
@Input() public toolbar: string | string[] | undefined;
@Input() public initialValue?: string;
@Input() public outputFormat?: 'html' | 'text';
@Input() public inline?: boolean;
@Input() public tagName?: string;
@Input() public plugins?: string;
@Input() public toolbar?: string | string[];
@Input() public modelEvents = 'change input undo redo';
@Input() public allowedEvents: string | string[] | undefined;
@Input() public ignoreEvents: string | string[] | undefined;
@Input() public allowedEvents?: string | string[];
@Input() public ignoreEvents?: string | string[];
@Input()
public set disabled(val) {
this._disabled = val;
Expand All @@ -80,9 +87,9 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal
public ngZone: NgZone;

private _elementRef: ElementRef;
private _element: HTMLElement | undefined;
private _disabled: boolean | undefined;
private _editor: TinyMCEEditor | undefined;
private _element?: HTMLElement;
private _disabled?: boolean;
private _editor?: TinyMCEEditor;

private onTouchedCallback = noop;
private onChangeCallback: any;
Expand Down Expand Up @@ -169,6 +176,7 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal
target: this._element,
inline: this.inline,
readonly: this.disabled,
license_key: this.licenseKey,
plugins: mergePlugins((this.init && this.init.plugins) as string, this.plugins),
toolbar: this.toolbar || (this.init && this.init.toolbar),
setup: (editor: TinyMCEEditor) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { VersionLoader } from '@tinymce/miniature';

import { EditorComponent } from '../../../main/ts/public_api';
import { TestStore } from '../alien/TestStore';
import { Version } from '../../../main/ts/editor/editor.component';

UnitTest.asynctest('EventBlacklistingTest', (success, failure) => {

Expand Down Expand Up @@ -37,7 +38,7 @@ UnitTest.asynctest('EventBlacklistingTest', (success, failure) => {
});
});

const sTestVersion = (version: '4' | '5' | '6') => VersionLoader.sWithVersion(
const sTestVersion = (version: Version) => VersionLoader.sWithVersion(
version,
Log.chainsAsStep('', 'Events should be bound when allowed',
[
Expand Down Expand Up @@ -70,6 +71,7 @@ UnitTest.asynctest('EventBlacklistingTest', (success, failure) => {
Pipeline.async({}, [
sTestVersion('4'),
sTestVersion('5'),
sTestVersion('6')
sTestVersion('6'),
sTestVersion('7')
], success, failure);
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { UnitTest } from '@ephox/bedrock-client';
import { VersionLoader } from '@tinymce/miniature';

import { EditorComponent, EditorModule } from '../../../main/ts/public_api';
import { Version } from '../../../main/ts/editor/editor.component';

UnitTest.asynctest('FormControlTest', (success, failure) => {
@Component({
Expand Down Expand Up @@ -53,7 +54,7 @@ UnitTest.asynctest('FormControlTest', (success, failure) => {
TestBed.resetTestingModule();
});

const sTestVersion = (version: '4' | '5' | '6') => VersionLoader.sWithVersion(
const sTestVersion = (version: Version) => VersionLoader.sWithVersion(
version,
Log.chainsAsStep('', 'FormControl interaction ', [
cSetupEditorWithFormControl,
Expand Down Expand Up @@ -89,6 +90,7 @@ UnitTest.asynctest('FormControlTest', (success, failure) => {
Pipeline.async({}, [
sTestVersion('4'),
sTestVersion('5'),
sTestVersion('6')
sTestVersion('6'),
sTestVersion('7')
], success, failure);
});
23 changes: 21 additions & 2 deletions tinymce-angular-component/src/test/ts/browser/LoadTinyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SelectorFilter, Attribute, SugarElement, Remove } from '@ephox/sugar';

import { EditorModule, EditorComponent, TINYMCE_SCRIPT_SRC } from '../../../main/ts/public_api';
import { ScriptLoader } from '../../../main/ts/utils/ScriptLoader';
import { Version } from '../../../main/ts/editor/editor.component';

UnitTest.asynctest('LoadTinyTest', (success, failure) => {
const cSetupEditor = (attributes: string[], providers: any[]) => Chain.async<void, void>((_, next) => {
Expand Down Expand Up @@ -60,14 +61,19 @@ UnitTest.asynctest('LoadTinyTest', (success, failure) => {
TestBed.resetTestingModule();
});

const cAssertTinymceVersion = (version: '4' | '5' | '6' | '7') => Chain.op(() => {
const cAssertTinymceVersion = (version: Version) => Chain.op(() => {
Assertions.assertEq(`Loaded version of TinyMCE should be ${version}`, version, Global.tinymce.majorVersion);
});

Pipeline.async({}, [
Log.chainsAsStep('Should be able to load local version of TinyMCE specified via depdendency injection', '', [
cDeleteTinymce,

cSetupEditor([], [{ provide: TINYMCE_SCRIPT_SRC, useValue: '/project/node_modules/tinymce-7/tinymce.min.js' }]),
cAssertTinymceVersion('7'),
cTeardown,
cDeleteTinymce,

cSetupEditor([], [{ provide: TINYMCE_SCRIPT_SRC, useValue: '/project/node_modules/tinymce-6/tinymce.min.js' }]),
cAssertTinymceVersion('6'),
cTeardown,
Expand All @@ -83,7 +89,20 @@ UnitTest.asynctest('LoadTinyTest', (success, failure) => {
cTeardown,
cDeleteTinymce,
]),
Log.chainsAsStep('Should be able to load TinyMCE from Cloud', '', [
Log.chainsAsStep('Should be able to load TinyMCE 7 from Cloud', '', [
cSetupEditor([ 'apiKey="a-fake-api-key"', 'cloudChannel="7"' ], []),
cAssertTinymceVersion('7'),
Chain.op(() => {
Assertions.assertEq(
'TinyMCE should have been loaded from Cloud',
'https://cdn.tiny.cloud/1/a-fake-api-key/tinymce/7',
Global.tinymce.baseURI.source
);
}),
cTeardown,
cDeleteTinymce
]),
Log.chainsAsStep('Should be able to load TinyMCE 6 from Cloud', '', [
cSetupEditor([ 'apiKey="a-fake-api-key"', 'cloudChannel="6"' ], []),
cAssertTinymceVersion('6'),
Chain.op(() => {
Expand Down
6 changes: 4 additions & 2 deletions tinymce-angular-component/src/test/ts/browser/NgModelTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { VersionLoader } from '@tinymce/miniature';
import { SugarElement } from '@ephox/sugar';

import { EditorModule, EditorComponent } from '../../../main/ts/public_api';
import { Version } from '../../../main/ts/editor/editor.component';

UnitTest.asynctest('NgModelTest', (success, failure) => {
class Base {
Expand Down Expand Up @@ -70,7 +71,7 @@ UnitTest.asynctest('NgModelTest', (success, failure) => {
context.fixture.detectChanges();
});

const sTestVersion = (version: '4' | '5' | '6') => VersionLoader.sWithVersion(version, GeneralSteps.sequence([
const sTestVersion = (version: Version) => VersionLoader.sWithVersion(version, GeneralSteps.sequence([
Log.chainsAsStep('', 'should be pristine, untouched, and valid initially', [
cSetupEditorWithNgModel(),
cAssertNgModelState('valid', true),
Expand Down Expand Up @@ -144,6 +145,7 @@ UnitTest.asynctest('NgModelTest', (success, failure) => {
Pipeline.async({}, [
sTestVersion('4'),
sTestVersion('5'),
sTestVersion('6')
sTestVersion('6'),
sTestVersion('7')
], success, failure);
});
6 changes: 4 additions & 2 deletions tinymce-angular-component/src/test/ts/browser/NgZoneTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UnitTest } from '@ephox/bedrock-client';
import { VersionLoader } from '@tinymce/miniature';

import { EditorComponent } from '../../../main/ts/public_api';
import { Version } from '../../../main/ts/editor/editor.component';

UnitTest.asynctest('NgZoneTest', (success, failure) => {
const createComponent = <T>(componentType: Type<T>) => {
Expand All @@ -20,7 +21,7 @@ UnitTest.asynctest('NgZoneTest', (success, failure) => {
TestBed.resetTestingModule();
});

const sTestVersion = (version: '4' | '5' | '6') => VersionLoader.sWithVersion(
const sTestVersion = (version: Version) => VersionLoader.sWithVersion(
version,
Log.chainsAsStep('', 'Subscribers to events should rune within NgZone', [
Chain.async<void, ComponentFixture<EditorComponent>>((_, next) => {
Expand Down Expand Up @@ -56,6 +57,7 @@ UnitTest.asynctest('NgZoneTest', (success, failure) => {
Pipeline.async({}, [
sTestVersion('4'),
sTestVersion('5'),
sTestVersion('6')
sTestVersion('6'),
sTestVersion('7')
], success, failure);
});
Loading

0 comments on commit 8dca388

Please sign in to comment.