Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added
- Added 'readonly' property. #TINY-11907
Comment thread
tiny-ben-tran marked this conversation as resolved.

### Fixed
- Updated dependencies. #INT-3324

### Changed
- Moved tinymce dependency to be a optional peer dependency. #INT-3324
- Updated tinymce dev dependency to version ^7 from 5.10.7 so now all internal tinymce types point to version 7. #INT-3324
- 'disabled' property is now mapped to editor's 'disabled' option if Tiny >= 7.6.0 is used. #TINY-11907
Comment thread
michalnieruchalski-tiugo marked this conversation as resolved.
Outdated

## 8.0.1 - 2024-07-12

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"tinymce-5": "npm:tinymce@^5",
"tinymce-6": "npm:tinymce@^6",
"tinymce-7": "npm:tinymce@^7",
"tinymce-7.5.0": "npm:tinymce@7.5.0",
Comment thread
tiny-ben-tran marked this conversation as resolved.
"to-string-loader": "^1.1.5",
"tslib": "^2.6.2",
"typescript": "~5.5.4",
Expand Down
15 changes: 15 additions & 0 deletions stories/Editor.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { MatTabsModule } from '@angular/material/tabs';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ContainerComponent, ContentProjectionComponent } from './contentprojection/ContentProjection.component';
import { BindingComponent } from './data-binding/DataBinding.component';
import { ReadonlyComponent } from './readonly/Readonly.component';

const meta: Meta = {
component: EditorComponent,
Expand Down Expand Up @@ -137,6 +138,20 @@ export const DisablingStory: StoryObj<EditorComponent> = {
}
};

export const ReadonlyStory: StoryObj<EditorComponent> = {
name: 'Readonly',
render: () => ({
moduleMetadata: {
imports: [ ReactiveFormsModule, FormsModule ],
declarations: [ ReadonlyComponent ],
},
template: `<readonly/>`
}),
parameters: {
notes: 'Example of toggling readonly state in the editor component'
}
};

export const ViewQueryStory: StoryObj<EditorComponent> = {
name: 'View Query',
render: () => ({
Expand Down
7 changes: 7 additions & 0 deletions stories/readonly/Readonly.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<button (click)="toggleReadonly()">{{ isReadonly ? 'Escape readonly' : 'Enter readonly' }}</button>
<editor
[apiKey]="apiKey"
[readonly]="isReadonly"
[initialValue]="initialValue"
[init]="{ height: 300 }"
/>
13 changes: 13 additions & 0 deletions stories/readonly/Readonly.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { apiKey, sampleContent } from '../Settings';

@Component({
selector: 'readonly',
templateUrl: './Readonly.component.html',
})
export class ReadonlyComponent {
public isReadonly = false;
public apiKey = apiKey;
public initialValue = sampleContent;
public toggleReadonly = () => (this.isReadonly = !this.isReadonly);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
import { FormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject, takeUntil } from 'rxjs';
import { getTinymce } from '../TinyMCE';
import { listenTinyMCEEvent, bindHandlers, isTextarea, mergePlugins, uuid, noop, isNullOrUndefined } from '../utils/Utils';
import { listenTinyMCEEvent, bindHandlers, isTextarea, mergePlugins, uuid, noop, isNullOrUndefined, setMode } from '../utils/Utils';
import * as DisabledUtils from '../utils/DisabledUtils';
import { EventObj, Events } from './Events';
import { ScriptLoader } from '../utils/ScriptLoader';
import type { Editor as TinyMCEEditor, TinyMCE } from 'tinymce';
Expand Down Expand Up @@ -64,14 +65,26 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal
@Input() public modelEvents = 'change input undo redo';
@Input() public allowedEvents?: string | string[];
@Input() public ignoreEvents?: string | string[];
@Input()
public set readonly(val) {
this._readonly = val;
if (this._editor && this._editor.initialized) {
setMode(this._editor, val ? 'readonly' : 'design');
}
}

public get readonly() {
return this._readonly;
}

@Input()
public set disabled(val) {
this._disabled = val;
if (this._editor && this._editor.initialized) {
if (typeof this._editor.mode?.set === 'function') {
this._editor.mode.set(val ? 'readonly' : 'design');
} else if ('setMode' in this._editor && typeof this._editor.setMode === 'function') {
this._editor.setMode(val ? 'readonly' : 'design');
if (DisabledUtils.isDisabledOptionSupported()) {
this._editor.options.set('disabled', val ?? false);
} else {
setMode(this._editor, val ? 'readonly' : 'design');
}
}
}
Expand All @@ -89,6 +102,7 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal
private _elementRef: ElementRef;
private _element?: HTMLElement;
private _disabled?: boolean;
private _readonly?: boolean;
private _editor?: TinyMCEEditor;

private onTouchedCallback = noop;
Expand Down Expand Up @@ -176,7 +190,10 @@ export class EditorComponent extends Events implements AfterViewInit, ControlVal
selector: undefined,
target: this._element,
inline: this.inline,
readonly: this.disabled,
...( DisabledUtils.isDisabledOptionSupported()
? { disabled: this.disabled, readonly: this.readonly }
: { readonly: this.disabled || this.readonly }
),
license_key: this.licenseKey,
plugins: mergePlugins((this.init && this.init.plugins) as string, this.plugins),
toolbar: this.toolbar || (this.init && this.init.toolbar),
Expand Down
12 changes: 12 additions & 0 deletions tinymce-angular-component/src/main/ts/utils/DisabledUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { getTinymce } from '../TinyMCE';
import { TinyMCE } from 'tinymce';

const isDisabledOptionSupported = () => {
const tiny: TinyMCE = getTinymce();
// Disabled option is supported since Tiny 7.6.0
return Number(tiny.majorVersion) >= 7 && Number(tiny.minorVersion) >= 6;
Comment thread
michalnieruchalski-tiugo marked this conversation as resolved.
Outdated
};

export {
isDisabledOptionSupported
};
16 changes: 13 additions & 3 deletions tinymce-angular-component/src/main/ts/utils/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { HasEventTargetAddRemove } from 'rxjs/internal/observable/fromEvent';

import { EditorComponent } from '../editor/editor.component';
import { validEvents, Events } from '../editor/Events';
import { Editor } from 'tinymce';

// Caretaker note: `fromEvent` supports passing JQuery-style event targets, the editor has `on` and `off` methods which
// will be invoked upon subscription and teardown.
Expand Down Expand Up @@ -47,10 +48,10 @@ const getValidEvents = (ctx: EditorComponent): (keyof Events)[] => {
};

const parseStringProperty = (property: string | string[] | undefined, defaultValue: (keyof Events)[]): string[] => {
if ( typeof property === 'string') {
if (typeof property === 'string') {
return property.split(',').map((value) => value.trim());
}
if ( Array.isArray(property)) {
if (Array.isArray(property)) {
return property;
}
return defaultValue;
Expand Down Expand Up @@ -91,6 +92,14 @@ const isObserved = (o: Subject<unknown>): boolean =>
// checking if a subject has observers.
o.observed || o.observers?.length > 0;

const setMode = (editor: Editor, mode: 'readonly' | 'design') => {
if (typeof editor.mode?.set === 'function') {
editor.mode.set(mode);
} else if ('setMode' in editor && typeof editor.setMode === 'function') {
editor.setMode(mode);
}
Comment thread
tiny-ben-tran marked this conversation as resolved.
};

export {
listenTinyMCEEvent,
bindHandlers,
Expand All @@ -99,5 +108,6 @@ export {
normalizePluginArray,
mergePlugins,
noop,
isNullOrUndefined
isNullOrUndefined,
setMode
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Assertions } from '@ephox/agar';
import '../alien/InitTestEnvironment';

import { EditorComponent } from '../../../main/ts/public_api';
import { describe, it } from '@ephox/bedrock-client';
import { eachVersionContext, editorHook } from '../alien/TestHooks';
import { Editor } from 'tinymce';

describe('DisabledPropertyTest', () => {
const getMode = (editor: Editor) => {
if (typeof editor.mode?.get === 'function') {
return editor.mode.get();
}
return editor.readonly ? 'readonly' : 'design';
};
const assertDesignMode = (editor: Editor) => Assertions.assertEq('TinyMCE should be in design mode', 'design', getMode(editor));
const assertReadonlyMode = (editor: Editor) => Assertions.assertEq('TinyMCE should be in readonly mode', 'readonly', getMode(editor));
const assertDisabledOption = (editor: Editor, expected: boolean) =>
Assertions.assertEq(`TinyMCE should have disabled option set to ${expected}`, expected, editor.options.get('disabled'));

eachVersionContext([ '7.5.0' ], () => {
const createFixture = editorHook(EditorComponent);

it(`Component 'disabled' property is mapped to editor 'readonly' property`, async () => {
const { editor } = await createFixture({ disabled: true });
assertReadonlyMode(editor);
});

it(`Toggling component's 'disabled' property is mapped to editor 'readonly' property`, async () => {
const fixture = await createFixture();
const { editor } = fixture;

assertDesignMode(editor);

fixture.componentRef.setInput('disabled', true);
fixture.detectChanges();
assertReadonlyMode(editor);

fixture.componentRef.setInput('disabled', false);
fixture.detectChanges();
assertDesignMode(editor);
});

it(`[disabled]=true [readonly]=false triggers readonly mode`, async () => {
const { editor } = await createFixture({ disabled: true, readonly: false });
assertReadonlyMode(editor);
});

it(`[disabled]=false [readonly]=true triggers readonly mode`, async () => {
const { editor } = await createFixture({ disabled: false, readonly: true });
assertReadonlyMode(editor);
});
});

eachVersionContext([ '7' ], () => {
const createFixture = editorHook(EditorComponent);

it(`Component 'disabled' property is mapped to editor 'disabled' property`, async () => {
const { editor } = await createFixture({ disabled: true });

Assertions.assertEq('TinyMCE should have disabled option set to true', true, editor.options.get('disabled'));
assertDesignMode(editor);
});

it(`Toggling component's 'disabled' property is mapped to editor 'disabled' property`, async () => {
const fixture = await createFixture();
const { editor } = fixture;

assertDesignMode(editor);
assertDisabledOption(editor, false);

fixture.componentRef.setInput('disabled', true);
fixture.detectChanges();
assertDesignMode(editor);
assertDisabledOption(editor, true);

fixture.componentRef.setInput('disabled', false);
fixture.detectChanges();
assertDesignMode(editor);
assertDisabledOption(editor, false);
});
});

eachVersionContext([ '4', '5', '6', '7' ], () => {
const createFixture = editorHook(EditorComponent);

it(`Setting the 'readonly' property causing readonly mode`, async () => {
const { editor } = await createFixture({ readonly: true });
assertReadonlyMode(editor);
});

it(`Toggling component's 'readonly' property is mapped to editor 'readonly' mode`, async () => {
const fixture = await createFixture();
const { editor } = fixture;

assertDesignMode(editor);

fixture.componentRef.setInput('readonly', true);
fixture.detectChanges();
assertReadonlyMode(editor);

fixture.componentRef.setInput('readonly', false);
fixture.detectChanges();
assertDesignMode(editor);
});
});
});
11 changes: 8 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13082,10 +13082,15 @@ tiny-invariant@^1.3.1, tiny-invariant@^1.3.3:
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-6.8.3.tgz#0025a4aaa4c24dc2a3e32e83dfda705d196fd802"
integrity sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==

"tinymce-7.5.0@npm:tinymce@7.5.0":
version "7.5.0"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.5.0.tgz#56388d314399c288a100df4aaf468153f29477f1"
integrity sha512-A7iuQPIfeze5rO6bvnnPwP7TiWnPA9AGr8U/9ssLwrEG+FMYPzvLPt3RT8ktVn/wPSJkVBBSLCAZX2dAHb8YEA==

"tinymce-7@npm:tinymce@^7":
version "7.1.2"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.1.2.tgz#cb40e527dc03d6a0547a23c91231a946e50dae03"
integrity sha512-I/M5WRyEJjwIhyIv6FhkvZS1mWNbb0sIEvDkP8akBnuV1X78mkNhi6Kz9FBBbHzy61U3pmXgzyCSaDZfdQbCSg==
version "7.8.0"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-7.8.0.tgz#d57a597aecdc2108f2dd68fe74c6099c0a0ef66f"
integrity sha512-MUER5MWV9mkOB4expgbWknh/C5ZJvOXQlMVSx4tJxTuYtcUCDB6bMZ34fWNOIc8LvrnXmGHGj0eGQuxjQyRgrA==

tinymce@^7:
version "7.2.1"
Expand Down