Skip to content

Commit

Permalink
Merge pull request #392 from ckeditor/i/373
Browse files Browse the repository at this point in the history
Feature: Added `disableWatchdog` property to the React component. Closes #373.
  • Loading branch information
pomek authored Jun 26, 2023
2 parents 76a7a08 + feff964 commit 3fa02f4
Show file tree
Hide file tree
Showing 6 changed files with 460 additions and 352 deletions.
12 changes: 12 additions & 0 deletions demo-react-16/src/EditorDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ type EditorDemoProps = {
type EditorDemoState = {
documents: Array<string>;
documentID: number;
isWatchdogDisabled: boolean;
editor: ClassicEditor | null;
};

export default class EditorDemo extends React.Component<EditorDemoProps, EditorDemoState> {
public state: EditorDemoState = {
documents: [ this.props.content ],
documentID: 0,
isWatchdogDisabled: false,
editor: null
};

Expand Down Expand Up @@ -54,11 +56,17 @@ export default class EditorDemo extends React.Component<EditorDemoProps, EditorD
>
Next document ID
</button>
<button
onClick={ () => this.setIsWatchdogDisabled() }
>
{ this.state.isWatchdogDisabled ? 'Enable' : 'Disable' } watchdog
</button>
</div>

<CKEditor
editor={ ClassicEditor }
id={ this.state.documentID }
disableWatchdog={ this.state.isWatchdogDisabled }
data={ this.state.documents[ this.state.documentID ] }
watchdogConfig={ { crashNumberLimit: 10 } }

Expand Down Expand Up @@ -131,6 +139,10 @@ export default class EditorDemo extends React.Component<EditorDemoProps, EditorD
} );
}

private setIsWatchdogDisabled() {
this.setState( { isWatchdogDisabled: !this.state.isWatchdogDisabled } );
}

private previousDocumentID(): void {
this.setState( { documentID: Math.max( this.state.documentID - 1, 0 ) } );
}
Expand Down
7 changes: 7 additions & 0 deletions demo-react-18/src/EditorDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type EditorDemoState = {
};

export default function EditorDemo( props: EditorDemoProps ): JSX.Element {
const [ isWatchdogDisabled, setIsWatchdogDisabled ] = useState( false );
const [ state, setState ] = useState<EditorDemoState>( {
documents: [ props.content ],
documentID: 0,
Expand Down Expand Up @@ -101,12 +102,18 @@ export default function EditorDemo( props: EditorDemoProps ): JSX.Element {
>
Next document ID
</button>
<button
onClick={ () => setIsWatchdogDisabled( !isWatchdogDisabled ) }
>
{ isWatchdogDisabled ? 'Enable' : 'Disable' } watchdog
</button>
</div>

{ /* @ts-expect-error: Caused by linking to parent project and conflicting react types */ }
<CKEditor
editor={ ClassicEditor }
id={ state.documentID }
disableWatchdog={ isWatchdogDisabled }
data={ state.documents[ state.documentID ] }
watchdogConfig={ { crashNumberLimit: 10 } }

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"webpack-cli": "^4.9.2"
},
"resolutions": {
"strip-ansi-cjs": "1.0.0"
"strip-ansi-cjs": "1.0.0",
"string-width": "^4.0.0"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
Expand Down
39 changes: 32 additions & 7 deletions src/ckeditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,15 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr

/**
* An instance of EditorWatchdog or an instance of EditorWatchdog-like adapter for ContextWatchdog.
* It holds the instance of the editor under `this.watchdog.editor` if `props.disableWatchdog` is set to false.
*/
private watchdog: EditorWatchdog<TEditor> | EditorWatchdogAdapter<TEditor> | null = null;

/**
* Holds the instance of the editor if `props.disableWatchdog` is set to true.
*/
private instance: Editor | undefined | null;

constructor( props: Props<TEditor> ) {
super( props );

Expand All @@ -60,6 +66,10 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr
* An editor instance.
*/
public get editor(): Editor | null {
if ( this.props.disableWatchdog ) {
return this.instance!;
}

if ( !this.watchdog ) {
return null;
}
Expand All @@ -81,6 +91,10 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr
return true;
}

if ( nextProps.disableWatchdog !== this.props.disableWatchdog ) {
return true;
}

if ( this._shouldUpdateEditor( nextProps ) ) {
this.editor.data.set( nextProps.data! );
}
Expand Down Expand Up @@ -133,6 +147,11 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr
private async _initializeEditor(): Promise<unknown> {
await this.editorDestructionInProgress;

if ( this.props.disableWatchdog ) {
this.instance = await this._createEditor( this.domContainer.current!, this._getConfig() );
return;
}

/* istanbul ignore next */
if ( this.watchdog ) {
return;
Expand Down Expand Up @@ -225,16 +244,20 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr
// that <CKEditorContext /> component will be destroyed first, so during the code execution
// the `ContextWatchdog#state` would have a correct value. See `EditorWatchdogAdapter#destroy()` for more information.
/* istanbul ignore next */
setTimeout( () => {
setTimeout( async () => {
if ( this.watchdog ) {
this.watchdog.destroy().then( () => {
this.watchdog = null;
await this.watchdog.destroy();
this.watchdog = null;
return resolve();
}

resolve();
} );
} else {
resolve();
if ( this.instance ) {
await this.instance.destroy();
this.instance = null;
return resolve();
}

resolve();
} );
} );
}
Expand Down Expand Up @@ -288,6 +311,7 @@ export default class CKEditor<TEditor extends Editor> extends React.Component<Pr
editor: PropTypes.func.isRequired as unknown as Validator<{ create( ...args: any ): Promise<any> }>,
data: PropTypes.string,
config: PropTypes.object,
disableWatchdog: PropTypes.bool,
watchdogConfig: PropTypes.object,
onChange: PropTypes.func,
onReady: PropTypes.func,
Expand All @@ -310,6 +334,7 @@ interface Props<TEditor extends Editor> extends InferProps<typeof CKEditor.propT
editor: { create( ...args: any ): Promise<TEditor> };
config?: EditorConfig;
watchdogConfig?: WatchdogConfig;
disableWatchdog?: boolean;
onReady?: ( editor: TEditor ) => void;
onError?: ( error: Error, details: ErrorDetails ) => void;
onChange?: ( event: EventInfo, editor: TEditor ) => void;
Expand Down
81 changes: 81 additions & 0 deletions tests/ckeditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,87 @@ describe( '<CKEditor> Component', () => {
expect( secondWatchdog.state ).to.equal( 'ready' );
} );
} );

describe( '#disableWatchdog', () => {
it( 'should not initialize watchdog if disableWatchdog is set to true', async () => {
await new Promise( ( res, rej ) => {
wrapper = mount( <CKEditor
editor={ Editor }
onReady={ res }
onError={ rej }
config={ { initialData: '<p>foo</p>' } }
disableWatchdog={ true }
id="1"
/> );
} );

const { watchdog } = wrapper.instance();

expect( watchdog ).to.equal( null );
} );

it( 'should initialize watchdog if disableWatchdog is set to false', async () => {
await new Promise( ( res, rej ) => {
wrapper = mount( <CKEditor
editor={ Editor }
onReady={ res }
onError={ rej }
config={ { initialData: '<p>foo</p>' } }
disableWatchdog={ false }
id="1"
/> );
} );

const { watchdog } = wrapper.instance();

expect( watchdog ).not.to.equal( null );
} );

it( 'should initialize watchdog if disableWatchdog is not set', async () => {
await new Promise( ( res, rej ) => {
wrapper = mount( <CKEditor
editor={ Editor }
onReady={ res }
onError={ rej }
config={ { initialData: '<p>foo</p>' } }
id="1"
/> );
} );

const { watchdog } = wrapper.instance();

expect( watchdog ).not.to.equal( null );
} );

it( 'should re-render when disableWatchdog has changed', async () => {
await new Promise( ( res, rej ) => {
wrapper = mount( <CKEditor
editor={ Editor }
onReady={ res }
onError={ rej }
config={ { initialData: '<p>foo</p>' } }
id="1"
/> );
} );

const { watchdog: watchdog1 } = wrapper.instance();
expect( watchdog1 ).not.to.equal( null );

await new Promise( res => {
wrapper.setProps( { onReady: res, disableWatchdog: true } );
} );

const { watchdog: watchdog2 } = wrapper.instance();
expect( watchdog2 ).to.equal( null );

await new Promise( res => {
wrapper.setProps( { onReady: res, disableWatchdog: false } );
} );

const { watchdog: watchdog3 } = wrapper.instance();
expect( watchdog3 ).not.to.equal( null );
} );
} );
} );

describe( 'destroy', () => {
Expand Down
Loading

0 comments on commit 3fa02f4

Please sign in to comment.