From 31e7619495abc465438daf343eddc2ee76f9e11a Mon Sep 17 00:00:00 2001 From: Arkadiusz Filipczak Date: Tue, 24 Oct 2023 10:40:33 +0200 Subject: [PATCH 1/8] Inserted image should be block by default. --- .../ckeditor5-ckbox/tests/ckboxcommand.js | 1 + packages/ckeditor5-image/src/imageconfig.ts | 13 +- packages/ckeditor5-image/src/imageutils.ts | 10 +- packages/ckeditor5-image/tests/autoimage.js | 3 +- .../tests/image/insertimagecommand.js | 3 +- .../tests/imageupload/imageuploadediting.js | 3 +- .../tests/imageupload/imageuploadprogress.js | 3 +- .../tests/imageupload/imageuploadui.js | 12 +- .../tests/imageupload/uploadimagecommand.js | 3 +- packages/ckeditor5-image/tests/imageutils.js | 383 ++++++++++-------- .../ckeditor5-image/tests/pictureediting.js | 3 +- .../integrations/insertcontent.js | 3 +- 12 files changed, 247 insertions(+), 193 deletions(-) diff --git a/packages/ckeditor5-ckbox/tests/ckboxcommand.js b/packages/ckeditor5-ckbox/tests/ckboxcommand.js index aef524390f0..f894f8cf920 100644 --- a/packages/ckeditor5-ckbox/tests/ckboxcommand.js +++ b/packages/ckeditor5-ckbox/tests/ckboxcommand.js @@ -1201,6 +1201,7 @@ function createTestEditor( config = {} ) { substitutePlugins: [ CloudServicesCoreMock ], + image: { insert: { type: 'auto' } }, ...config } ); } diff --git a/packages/ckeditor5-image/src/imageconfig.ts b/packages/ckeditor5-image/src/imageconfig.ts index 891befeec63..88e027d234b 100644 --- a/packages/ckeditor5-image/src/imageconfig.ts +++ b/packages/ckeditor5-image/src/imageconfig.ts @@ -484,16 +484,19 @@ export interface ImageInsertConfig { integrations: Array; /** - * This options allows to override the image type used by the {@link module:image/image/insertimagecommand~InsertImageCommand} - * when the user inserts new images into the editor content. By default, this option is unset which means the editor will choose - * the optimal image type based on the context of the insertion (e.g. the current selection and availability of plugins) + * This option allows to override the image type used by the {@link module:image/image/insertimagecommand~InsertImageCommand} + * when the user inserts new images into the editor content. By default, all images inserted into the editor will be block + * if {@link module:image/imageblock~ImageBlock} is loaded. To let the editor decide the image type, choose `'auto'`. * * Available options are: * * * `'block'` – all images inserted into the editor will be block (requires the {@link module:image/imageblock~ImageBlock} plugin), - * * `'inline'` – all images inserted into the editor will be inline (requires the {@link module:image/imageinline~ImageInline} plugin). + * * `'inline'` – all images inserted into the editor will be inline (requires the {@link module:image/imageinline~ImageInline} plugin), + * * `'auto'` – the editor will choose the optimal image type based on the context of the insertion and availability of plugins. + * + * @default 'block' */ - type?: 'inline' | 'block'; + type?: 'inline' | 'block' | 'auto'; } /** diff --git a/packages/ckeditor5-image/src/imageutils.ts b/packages/ckeditor5-image/src/imageutils.ts index 94249bcdcc3..74ee9deeba1 100644 --- a/packages/ckeditor5-image/src/imageutils.ts +++ b/packages/ckeditor5-image/src/imageutils.ts @@ -105,7 +105,7 @@ export default class ImageUtils extends Plugin { const model = editor.model; const selection = model.document.selection; - imageType = determineImageTypeForInsertion( editor, selectable || selection, imageType ); + const determinedImageType = determineImageTypeForInsertion( editor, selectable || selection, imageType ); // Mix declarative attributes with selection attributes because the new image should "inherit" // the latter for best UX. For instance, inline images inserted into existing links @@ -116,20 +116,20 @@ export default class ImageUtils extends Plugin { }; for ( const attributeName in attributes ) { - if ( !model.schema.checkAttribute( imageType, attributeName ) ) { + if ( !model.schema.checkAttribute( determinedImageType, attributeName ) ) { delete attributes[ attributeName ]; } } return model.change( writer => { const { setImageSizes = true } = options; - const imageElement = writer.createElement( imageType!, attributes ); + const imageElement = writer.createElement( determinedImageType, attributes ); model.insertObject( imageElement, selectable, null, { setSelection: 'on', // If we want to insert a block image (for whatever reason) then we don't want to split text blocks. // This applies only when we don't have the selectable specified (i.e., we insert multiple block images at once). - findOptimalPosition: !selectable && imageType != 'imageInline' ? 'auto' : undefined + findOptimalPosition: !selectable && determinedImageType != 'imageInline' ? 'auto' : undefined } ); // Inserting an image might've failed due to schema regulations. @@ -381,7 +381,7 @@ function determineImageTypeForInsertion( return 'imageInline'; } - if ( configImageInsertType === 'block' ) { + if ( configImageInsertType !== 'auto' ) { return 'imageBlock'; } diff --git a/packages/ckeditor5-image/tests/autoimage.js b/packages/ckeditor5-image/tests/autoimage.js index 3279cc9c5ba..178e80ebcc4 100644 --- a/packages/ckeditor5-image/tests/autoimage.js +++ b/packages/ckeditor5-image/tests/autoimage.js @@ -31,7 +31,8 @@ describe( 'AutoImage - integration', () => { return ClassicTestEditor .create( editorElement, { - plugins: [ Typing, Paragraph, Link, Image, LinkImage, ImageCaption, AutoImage ] + plugins: [ Typing, Paragraph, Link, Image, LinkImage, ImageCaption, AutoImage ], + image: { insert: { type: 'auto' } } } ) .then( newEditor => { editor = newEditor; diff --git a/packages/ckeditor5-image/tests/image/insertimagecommand.js b/packages/ckeditor5-image/tests/image/insertimagecommand.js index a8420316178..6a67f6bf3b3 100644 --- a/packages/ckeditor5-image/tests/image/insertimagecommand.js +++ b/packages/ckeditor5-image/tests/image/insertimagecommand.js @@ -17,7 +17,8 @@ describe( 'InsertImageCommand', () => { beforeEach( () => { return VirtualTestEditor .create( { - plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ] + plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ], + image: { insert: { type: 'auto' } } } ) .then( newEditor => { editor = newEditor; diff --git a/packages/ckeditor5-image/tests/imageupload/imageuploadediting.js b/packages/ckeditor5-image/tests/imageupload/imageuploadediting.js index 1d2081582e4..2e910e6b616 100644 --- a/packages/ckeditor5-image/tests/imageupload/imageuploadediting.js +++ b/packages/ckeditor5-image/tests/imageupload/imageuploadediting.js @@ -64,7 +64,8 @@ describe( 'ImageUploadEditing', () => { plugins: [ ImageBlockEditing, ImageInlineEditing, ImageUploadEditing, Paragraph, UndoEditing, UploadAdapterPluginMock, ClipboardPipeline - ] + ], + image: { insert: { type: 'auto' } } } ) .then( newEditor => { editor = newEditor; diff --git a/packages/ckeditor5-image/tests/imageupload/imageuploadprogress.js b/packages/ckeditor5-image/tests/imageupload/imageuploadprogress.js index 2a74f83589d..f633a402f18 100644 --- a/packages/ckeditor5-image/tests/imageupload/imageuploadprogress.js +++ b/packages/ckeditor5-image/tests/imageupload/imageuploadprogress.js @@ -52,7 +52,8 @@ describe( 'ImageUploadProgress', () => { plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph, ImageUploadEditing, ImageUploadProgress, UploadAdapterPluginMock, ClipboardPipeline - ] + ], + image: { insert: { type: 'auto' } } } ) .then( newEditor => { editor = newEditor; diff --git a/packages/ckeditor5-image/tests/imageupload/imageuploadui.js b/packages/ckeditor5-image/tests/imageupload/imageuploadui.js index 13d2df5258b..ea08d8f86ef 100644 --- a/packages/ckeditor5-image/tests/imageupload/imageuploadui.js +++ b/packages/ckeditor5-image/tests/imageupload/imageuploadui.js @@ -140,9 +140,8 @@ describe( 'ImageUploadUI', () => { const id = fileRepository.getLoader( files[ 0 ] ).id; expect( getModelData( model ) ).to.equal( - '' + - `f[]oo` + - '' + `[]` + + 'foo' ); } ); @@ -158,10 +157,9 @@ describe( 'ImageUploadUI', () => { const id2 = fileRepository.getLoader( files[ 1 ] ).id; expect( getModelData( model ) ).to.equal( - '' + - `foo` + - `[]` + - '' + + 'foo' + + `` + + `[]` + 'bar' ); } ); diff --git a/packages/ckeditor5-image/tests/imageupload/uploadimagecommand.js b/packages/ckeditor5-image/tests/imageupload/uploadimagecommand.js index 6397e5b75a9..a66a30d614f 100644 --- a/packages/ckeditor5-image/tests/imageupload/uploadimagecommand.js +++ b/packages/ckeditor5-image/tests/imageupload/uploadimagecommand.js @@ -33,7 +33,8 @@ describe( 'UploadImageCommand', () => { beforeEach( () => { return VirtualTestEditor .create( { - plugins: [ FileRepository, ImageBlockEditing, ImageInlineEditing, Paragraph, UploadAdapterPluginMock ] + plugins: [ FileRepository, ImageBlockEditing, ImageInlineEditing, Paragraph, UploadAdapterPluginMock ], + image: { insert: { type: 'auto' } } } ) .then( newEditor => { editor = newEditor; diff --git a/packages/ckeditor5-image/tests/imageutils.js b/packages/ckeditor5-image/tests/imageutils.js index f570c86a262..57e38216a1a 100644 --- a/packages/ckeditor5-image/tests/imageutils.js +++ b/packages/ckeditor5-image/tests/imageutils.js @@ -476,264 +476,309 @@ describe( 'ImageUtils plugin', () => { describe( 'insertImage()', () => { let editor, model; - beforeEach( () => { - return VirtualTestEditor - .create( { - plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ] - } ) - .then( newEditor => { - editor = newEditor; - model = editor.model; - - imageUtils = editor.plugins.get( 'ImageUtils' ); - - const schema = model.schema; - schema.extend( 'imageBlock', { allowAttributes: 'uploadId' } ); - } ); - } ); - - afterEach( async () => { + afterEach( () => { return editor.destroy(); } ); - it( 'should insert inline image in a paragraph with text', () => { - setModelData( model, 'f[o]o' ); + describe( 'config.image.insert.type set to "auto"', () => { + beforeEach( () => createEditor( { + plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ], + image: { insert: { type: 'auto' } } + } ) ); - imageUtils.insertImage( editor ); + it( 'should insert inline image in a paragraph with text', () => { + setModelData( model, 'f[o]o' ); - expect( getModelData( model ) ).to.equal( 'f[]o' ); - } ); + imageUtils.insertImage( editor ); - it( 'should insert a block image when the selection is inside an empty paragraph', () => { - setModelData( model, '[]' ); + expect( getModelData( model ) ).to.equal( 'f[]o' ); + } ); - imageUtils.insertImage( editor ); + it( 'should insert a block image when the selection is inside an empty paragraph', () => { + setModelData( model, '[]' ); - expect( getModelData( model ) ).to.equal( '[]' ); - } ); + imageUtils.insertImage( editor ); - it( 'should insert a block image in the document root', () => { - setModelData( model, '[]' ); + expect( getModelData( model ) ).to.equal( '[]' ); + } ); - imageUtils.insertImage( editor ); + it( 'should insert a block image in the document root', () => { + setModelData( model, '[]' ); - expect( getModelData( model ) ).to.equal( '[]' ); - } ); + imageUtils.insertImage( editor ); - it( 'should insert image with given attributes', () => { - setModelData( model, 'f[o]o' ); + expect( getModelData( model ) ).to.equal( '[]' ); + } ); - imageUtils.insertImage( { src: 'bar' } ); + it( 'should insert image with given attributes', () => { + setModelData( model, 'f[o]o' ); - expect( getModelData( model ) ).to.equal( 'f[]o' ); - } ); + imageUtils.insertImage( { src: 'bar' } ); - it( 'should not insert image nor crash when image could not be inserted', () => { - model.schema.register( 'other', { - allowIn: '$root', - allowChildren: '$text', - isLimit: true + expect( getModelData( model ) ).to.equal( 'f[]o' ); } ); - editor.conversion.for( 'downcast' ).elementToElement( { model: 'other', view: 'p' } ); + it( 'should use the inline image type when there is only ImageInlineEditing plugin enabled', async () => { + const consoleWarnStub = sinon.stub( console, 'warn' ); - setModelData( model, '[]' ); + await editor.destroy(); + await createEditor( { + plugins: [ ImageUtils, ImageInlineEditing, Paragraph ], + image: { insert: { type: 'auto' } } + } ); - imageUtils.insertImage(); + setModelData( model, 'f[o]o' ); - expect( getModelData( model ) ).to.equal( '[]' ); - } ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - it( 'should use the block image type when the config.image.insert.type="block" option is set', async () => { - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageBlockEditing, ImageInlineEditing, Paragraph ], - image: { insert: { type: 'block' } } + expect( consoleWarnStub.called ).to.be.false; + expect( getModelData( model ) ).to.equal( 'f[]o' ); + + console.warn.restore(); } ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should use block the image type when there is only ImageBlockEditing plugin enabled', async () => { + const consoleWarnStub = sinon.stub( console, 'warn' ); - newEditor.plugins.get( 'ImageUtils' ).insertImage( newEditor ); + await editor.destroy(); + await createEditor( { + plugins: [ ImageUtils, ImageBlockEditing, Paragraph ], + image: { insert: { type: 'auto' } } + } ); - expect( getModelData( newEditor.model ) ).to.equal( '[]foo' ); + setModelData( model, 'f[o]o' ); - await newEditor.destroy(); - } ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - it( 'should use the inline image type if the config.image.insert.type="inline" option is set', async () => { - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageBlockEditing, ImageInlineEditing, Paragraph ], - image: { insert: { type: 'inline' } } + expect( consoleWarnStub.called ).to.be.false; + expect( getModelData( model ) ).to.equal( '[]foo' ); + + console.warn.restore(); } ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should pass the allowed custom attributes to the inserted block image', () => { + setModelData( model, '[]' ); + model.schema.extend( 'imageBlock', { allowAttributes: 'customAttribute' } ); - newEditor.plugins.get( 'ImageUtils' ).insertImage(); + imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); - expect( getModelData( newEditor.model ) ).to.equal( 'f[]o' ); + expect( getModelData( model ) ) + .to.equal( '[]' ); + } ); - await newEditor.destroy(); - } ); + it( 'should omit the disallowed attributes while inserting a block image', () => { + setModelData( model, '[]' ); + + imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); - it( 'should use the inline image type when there is only ImageInlineEditing plugin enabled', async () => { - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageInlineEditing, Paragraph ] + expect( getModelData( model ) ) + .to.equal( '[]' ); } ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should pass the allowed custom attributes to the inserted inline image', () => { + setModelData( model, 'f[o]o' ); + model.schema.extend( 'imageInline', { allowAttributes: 'customAttribute' } ); - newEditor.plugins.get( 'ImageUtils' ).insertImage(); + imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); - expect( getModelData( newEditor.model ) ).to.equal( 'f[]o' ); + expect( getModelData( model ) ) + .to.equal( 'f[]o' ); + } ); - await newEditor.destroy(); - } ); + it( 'should omit the disallowed attributes while inserting an inline image', () => { + setModelData( model, 'f[o]o' ); + + imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); - it( 'should use block the image type when there is only ImageBlockEditing plugin enabled', async () => { - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageBlockEditing, Paragraph ] + expect( getModelData( model ) ).to.equal( 'f[]o' ); } ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should return the inserted image element', () => { + setModelData( model, '[]' ); - newEditor.plugins.get( 'ImageUtils' ).insertImage(); + const imageElement = imageUtils.insertImage( editor ); - expect( getModelData( newEditor.model ) ).to.equal( '[]foo' ); + expect( getModelData( model ) ).to.equal( '[]' ); + expect( imageElement.is( 'element', 'imageBlock' ) ).to.be.true; + expect( imageElement ).to.equal( model.document.getRoot().getChild( 0 ) ); + } ); - await newEditor.destroy(); - } ); + it( 'should return null when the image could not be inserted', () => { + model.schema.register( 'other', { + allowIn: '$root', + allowChildren: '$text', + isLimit: true + } ); - it( 'should use the block image type when the config.image.insert.type="inline" option is set ' + - 'but ImageInlineEditing plugin is not enabled', async () => { - const consoleWarnStub = sinon.stub( console, 'warn' ); - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageBlockEditing, Paragraph ], - image: { insert: { type: 'inline' } } + editor.conversion.for( 'downcast' ).elementToElement( { model: 'other', view: 'p' } ); + + setModelData( model, '[]' ); + + const imageElement = imageUtils.insertImage(); + + expect( getModelData( model ) ).to.equal( '[]' ); + + expect( imageElement ).to.be.null; } ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should set image width and height', done => { + setModelData( model, 'f[o]o' ); - newEditor.plugins.get( 'ImageUtils' ).insertImage(); + imageUtils.insertImage( { src: '/assets/sample.png' } ); - expect( consoleWarnStub.calledOnce ).to.equal( true ); - expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal( 'image-inline-plugin-required' ); - expect( getModelData( newEditor.model ) ).to.equal( '[]foo' ); + setTimeout( () => { + expect( getModelData( model ) ).to.equal( + 'f[]o' + ); + + done(); + }, 100 ); + } ); - await newEditor.destroy(); - console.warn.restore(); + it( 'should not set image width and height if `setImageSizes` parameter is false', done => { + setModelData( model, 'f[o]o' ); + + imageUtils.insertImage( { src: '/assets/sample.png' }, null, null, { setImageSizes: false } ); + + setTimeout( () => { + expect( getModelData( model ) ).to.equal( + 'f[]o' + ); + + done(); + }, 100 ); + } ); } ); - it( 'should use the inline image type when the image.insert.type="block" option is set ' + - 'but ImageBlockEditing plugin is not enabled', async () => { - const consoleWarnStub = sinon.stub( console, 'warn' ); - const newEditor = await VirtualTestEditor.create( { - plugins: [ ImageUtils, ImageInlineEditing, Paragraph ], + describe( 'config.image.insert.type set to "block"', () => { + beforeEach( () => createEditor( { + plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ], image: { insert: { type: 'block' } } - } ); + } ) ); - setModelData( newEditor.model, 'f[o]o' ); + it( 'should use the block image type', () => { + setModelData( model, 'f[o]o' ); - newEditor.plugins.get( 'ImageUtils' ).insertImage(); + editor.plugins.get( 'ImageUtils' ).insertImage( editor ); - expect( consoleWarnStub.calledOnce ).to.equal( true ); - expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal( 'image-block-plugin-required' ); - expect( getModelData( newEditor.model ) ).to.equal( 'f[]o' ); + expect( getModelData( model ) ).to.equal( '[]foo' ); + } ); - await newEditor.destroy(); - console.warn.restore(); - } ); + it( 'should use the inline image type when ImageBlockEditing plugin is not enabled', async () => { + const consoleWarnStub = sinon.stub( console, 'warn' ); + + await editor.destroy(); + await createEditor( { + plugins: [ ImageInlineEditing, Paragraph ], + image: { insert: { type: 'block' } } + } ); + + setModelData( model, 'f[o]o' ); - it( 'should pass the allowed custom attributes to the inserted block image', () => { - setModelData( model, '[]' ); - model.schema.extend( 'imageBlock', { allowAttributes: 'customAttribute' } ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); + expect( consoleWarnStub.calledOnce ).to.be.true; + expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal( 'image-block-plugin-required' ); + expect( getModelData( model ) ).to.equal( 'f[]o' ); - expect( getModelData( model ) ) - .to.equal( '[]' ); + console.warn.restore(); + } ); } ); - it( 'should omit the disallowed attributes while inserting a block image', () => { - setModelData( model, '[]' ); + describe( 'config.image.insert.type set to "inline"', () => { + beforeEach( () => createEditor( { + plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ], + image: { insert: { type: 'inline' } } + } ) ); - imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); + it( 'should use the inline image type', () => { + setModelData( model, 'f[o]o' ); - expect( getModelData( model ) ) - .to.equal( '[]' ); - } ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - it( 'should pass the allowed custom attributes to the inserted inline image', () => { - setModelData( model, 'f[o]o' ); - model.schema.extend( 'imageInline', { allowAttributes: 'customAttribute' } ); + expect( getModelData( model ) ).to.equal( 'f[]o' ); + } ); - imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); + it( 'should use the block image type when ImageInlineEditing plugin is not enabled', async () => { + const consoleWarnStub = sinon.stub( console, 'warn' ); - expect( getModelData( model ) ) - .to.equal( 'f[]o' ); - } ); + await editor.destroy(); + await createEditor( { + plugins: [ ImageBlockEditing, Paragraph ], + image: { insert: { type: 'inline' } } + } ); + + setModelData( model, 'f[o]o' ); - it( 'should omit the disallowed attributes while inserting an inline image', () => { - setModelData( model, 'f[o]o' ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - imageUtils.insertImage( { src: 'foo', customAttribute: 'value' } ); + expect( consoleWarnStub.calledOnce ).to.be.true; + expect( consoleWarnStub.firstCall.args[ 0 ] ).to.equal( 'image-inline-plugin-required' ); + expect( getModelData( model ) ).to.equal( '[]foo' ); - expect( getModelData( model ) ).to.equal( 'f[]o' ); + console.warn.restore(); + } ); } ); - it( 'should return the inserted image element', () => { - setModelData( model, '[]' ); + describe( 'config.image.insert.type not provided', () => { + beforeEach( () => createEditor( { + plugins: [ ImageBlockEditing, ImageInlineEditing, Paragraph ] + } ) ); - const imageElement = imageUtils.insertImage( editor ); + it( 'should not insert image nor crash when image could not be inserted', () => { + model.schema.register( 'other', { + allowIn: '$root', + allowChildren: '$text', + isLimit: true + } ); - expect( getModelData( model ) ).to.equal( '[]' ); - expect( imageElement.is( 'element', 'imageBlock' ) ).to.be.true; - expect( imageElement ).to.equal( model.document.getRoot().getChild( 0 ) ); - } ); + editor.conversion.for( 'downcast' ).elementToElement( { model: 'other', view: 'p' } ); - it( 'should return null when the image could not be inserted', () => { - model.schema.register( 'other', { - allowIn: '$root', - allowChildren: '$text', - isLimit: true + setModelData( model, '[]' ); + + imageUtils.insertImage(); + + expect( getModelData( model ) ).to.equal( '[]' ); } ); - editor.conversion.for( 'downcast' ).elementToElement( { model: 'other', view: 'p' } ); + it( 'should use the inline image type when there is only ImageInlineEditing plugin enabled', async () => { + const consoleWarnStub = sinon.stub( console, 'warn' ); - setModelData( model, '[]' ); + await editor.destroy(); + await createEditor( { + plugins: [ ImageInlineEditing, Paragraph ] + } ); - const imageElement = imageUtils.insertImage(); + setModelData( model, 'f[o]o' ); - expect( getModelData( model ) ).to.equal( '[]' ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - expect( imageElement ).to.be.null; - } ); + expect( consoleWarnStub.called ).to.be.false; + expect( getModelData( model ) ).to.equal( 'f[]o' ); - it( 'should set image width and height', done => { - setModelData( model, 'f[o]o' ); + console.warn.restore(); + } ); - imageUtils.insertImage( { src: '/assets/sample.png' } ); + it( 'should use block the image type by default', () => { + setModelData( model, 'f[o]o' ); - setTimeout( () => { - expect( getModelData( model ) ).to.equal( - 'f[]o' - ); + editor.plugins.get( 'ImageUtils' ).insertImage(); - done(); - }, 100 ); + expect( getModelData( model ) ).to.equal( '[]foo' ); + } ); } ); - it( 'should not set image width and height if `setImageSizes` parameter is false', done => { - setModelData( model, 'f[o]o' ); - - imageUtils.insertImage( { src: '/assets/sample.png' }, null, null, { setImageSizes: false } ); + async function createEditor( config ) { + editor = await VirtualTestEditor.create( config ); + model = editor.model; + imageUtils = editor.plugins.get( 'ImageUtils' ); - setTimeout( () => { - expect( getModelData( model ) ).to.equal( - 'f[]o' - ); + const schema = model.schema; - done(); - }, 100 ); - } ); + if ( schema.isRegistered( 'imageBlock' ) ) { + schema.extend( 'imageBlock', { allowAttributes: 'uploadId' } ); + } + } } ); describe( 'findViewImgElement()', () => { diff --git a/packages/ckeditor5-image/tests/pictureediting.js b/packages/ckeditor5-image/tests/pictureediting.js index 8f07e3a460b..92273bf44f9 100644 --- a/packages/ckeditor5-image/tests/pictureediting.js +++ b/packages/ckeditor5-image/tests/pictureediting.js @@ -1873,7 +1873,8 @@ describe( 'PictureEditing', () => { ImageBlockEditing, ImageInlineEditing, LinkImageEditing, ImageResizeEditing, ImageCaptionEditing, ImageUploadEditing, UploadAdapterPluginMock - ] + ], + image: { insert: { type: 'auto' } } } ); model = editor.model; diff --git a/packages/ckeditor5-list/tests/documentlist/integrations/insertcontent.js b/packages/ckeditor5-list/tests/documentlist/integrations/insertcontent.js index f7b54d5ea1f..3481c87b4e2 100644 --- a/packages/ckeditor5-list/tests/documentlist/integrations/insertcontent.js +++ b/packages/ckeditor5-list/tests/documentlist/integrations/insertcontent.js @@ -44,7 +44,8 @@ describe( 'Inserting widgets in document lists', () => { plugins: [ Paragraph, CodeBlockEditing, DocumentListEditing, IndentEditing, BlockQuoteEditing, MediaEmbedEditing, Table, Image, HtmlEmbed, PageBreak, HorizontalLine, Widget - ] + ], + image: { insert: { type: 'auto' } } } ); model = editor.model; From b267b864637a79ad081d72664b189fe1a56ec80d Mon Sep 17 00:00:00 2001 From: Arkadiusz Filipczak Date: Tue, 24 Oct 2023 15:23:50 +0200 Subject: [PATCH 2/8] Inserting image: manual test for all image types. --- .../tests/manual/imageinsert.html | 23 ++--- .../tests/manual/imageinsert.js | 85 ++++++++++--------- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/packages/ckeditor5-image/tests/manual/imageinsert.html b/packages/ckeditor5-image/tests/manual/imageinsert.html index e76e370667e..de7cc867e63 100644 --- a/packages/ckeditor5-image/tests/manual/imageinsert.html +++ b/packages/ckeditor5-image/tests/manual/imageinsert.html @@ -10,19 +10,20 @@ } -
+

Insert configured as "auto"

+

Image upload via URL with CKFinder integration

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

-
- - +

Insert configured as "inline"

+
+

Image upload via URL with CKFinder integration

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum.

+
diff --git a/packages/ckeditor5-image/tests/manual/imageinsert.js b/packages/ckeditor5-image/tests/manual/imageinsert.js index fff9865912d..a7ded8edada 100644 --- a/packages/ckeditor5-image/tests/manual/imageinsert.js +++ b/packages/ckeditor5-image/tests/manual/imageinsert.js @@ -3,7 +3,7 @@ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ -/* globals window, document, console */ +/* globals window, document, console, CKEditorInspector */ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; @@ -13,41 +13,50 @@ import CKFinder from '@ckeditor/ckeditor5-ckfinder/src/ckfinder'; import ImageInsert from '../../src/imageinsert'; import AutoImage from '../../src/autoimage'; -ClassicEditor - .create( document.querySelector( '#editor' ), { - plugins: [ ArticlePluginSet, ImageInsert, AutoImage, LinkImage, CKFinderUploadAdapter, CKFinder ], - toolbar: [ - 'heading', - '|', - 'bold', - 'italic', - 'link', - 'bulletedList', - 'numberedList', - 'blockQuote', - 'insertImage', - 'insertTable', - 'mediaEmbed', - 'undo', - 'redo' - ], - image: { - toolbar: [ 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ], - insert: { - integrations: [ - 'insertImageViaUrl', - 'openCKFinder' - ] +createEditor( 'editor1', 'auto' ); +createEditor( 'editor2', 'block' ); +createEditor( 'editor3', 'inline' ); + +function createEditor( elementId, imageType ) { + ClassicEditor + .create( document.querySelector( '#' + elementId ), { + plugins: [ ArticlePluginSet, ImageInsert, AutoImage, LinkImage, CKFinderUploadAdapter, CKFinder ], + toolbar: [ + 'heading', + '|', + 'bold', + 'italic', + 'link', + 'bulletedList', + 'numberedList', + 'blockQuote', + 'insertImage', + 'insertTable', + 'mediaEmbed', + 'undo', + 'redo' + ], + image: { + toolbar: [ 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ], + insert: { + integrations: [ + 'insertImageViaUrl', + 'openCKFinder' + ], + type: imageType + } + }, + ckfinder: { + // eslint-disable-next-line max-len + uploadUrl: 'https://ckeditor.com/apps/ckfinder/3.5.0/core/connector/php/connector.php?command=QuickUpload&type=Files&responseType=json' } - }, - ckfinder: { - // eslint-disable-next-line max-len - uploadUrl: 'https://ckeditor.com/apps/ckfinder/3.5.0/core/connector/php/connector.php?command=QuickUpload&type=Files&responseType=json' - } - } ) - .then( editor => { - window.editor = editor; - } ) - .catch( err => { - console.error( err.stack ); - } ); + } ) + .then( editor => { + window[ elementId ] = editor; + + CKEditorInspector.attach( imageType, editor ); + } ) + .catch( err => { + console.error( err ); + } ); +} From 3bf4697d290e5022aaa0c31bbcdd1ce65e603ada Mon Sep 17 00:00:00 2001 From: godai78 Date: Wed, 25 Oct 2023 12:42:56 +0200 Subject: [PATCH 3/8] Docs: update image install docs. [short flow] --- .../docs/features/images-installation.md | 24 +++++++++++++------ .../docs/features/images-overview.md | 4 ++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/ckeditor5-image/docs/features/images-installation.md b/packages/ckeditor5-image/docs/features/images-installation.md index a9962c24914..794e12ce3eb 100644 --- a/packages/ckeditor5-image/docs/features/images-installation.md +++ b/packages/ckeditor5-image/docs/features/images-installation.md @@ -1,7 +1,7 @@ --- category: features-images menu-title: Installation -meta-title: Installation and configuration of image features | CKEditor 5 Documentation +meta-title: Installation and configuration of the image features | CKEditor 5 Documentation meta-description: Learn how to install and configure various image-related CKEdiotr 5 plugins. order: 15 modified_at: 2021-06-17 @@ -17,7 +17,7 @@ npm install --save @ckeditor/ckeditor5-image You may want to install the [`@ckeditor/ckeditor5-link`](https://www.npmjs.com/package/@ckeditor/ckeditor5-link) package if you want to use the {@link features/images-linking `LinkImage`} plugin in your editor. -Next add the {@link features/images-overview#image-features plugins that you need} to your plugin list. You also need to set the desired image toolbar items. Notice the {@link features/toolbar#separating-toolbar-items separators} used to organize the toolbar. +Next, add the {@link features/images-overview#image-features plugins that you need} to your plugin list. You also need to set the desired image toolbar items. Notice the {@link features/toolbar#separating-toolbar-items separators} used to organize the toolbar. ```js import { Image, ImageCaption, ImageResize, ImageStyle, ImageToolbar } from '@ckeditor/ckeditor5-image'; @@ -36,7 +36,7 @@ ClassicEditor '|', 'linkImage' ] - } + }, } ) .then( /* ... */ ) .catch( /* ... */ ); @@ -84,11 +84,21 @@ By default, the {@link module:image/image~Image} plugin available in all {@link - - Up to CKEditor 5 v[27.1.0], only **block** images were supported. The support for **inline** images started in v[28.0.0] in all editor builds loading the `Image` plugin. +By default, all images inserted into the content are treated as block images. It means, that inserting an image inside a paragraph (or inside other content blocks) will split the paragraph and add a new block for the image. Once inserted, the image can be turned into an inline image with the use of the {@link features/images-overview#image-contextual-toolbar contextual toolbar}. - If your integration depends on a ready–to–use editor build and you want to take advantage of updated CKEditor 5 but **without the support for inline images** (e.g. to maintain content compatibility), check out the {@link updating/update-to-29 official migration guide} that will help you configure the editor. - +To change this default behavior, you can use the `type` setting in the editor configuration: + +```js +ClassicEditor.create( element, { + image: { + insert: { + type: 'auto' + } + } +} ); +``` + +There are three possible options: `auto`, `block`, and `inline`. The `auto` option uses the default setting, while the other two force the use of the selected image type at insert. ## Contribute diff --git a/packages/ckeditor5-image/docs/features/images-overview.md b/packages/ckeditor5-image/docs/features/images-overview.md index 8476af8d179..7b6a7af64da 100644 --- a/packages/ckeditor5-image/docs/features/images-overview.md +++ b/packages/ckeditor5-image/docs/features/images-overview.md @@ -2,7 +2,7 @@ category: features-images menu-title: Basics meta-title: Image features overview | CKEditor 5 Documentation -meta-description: Find out all about images in CKEditor 5. +meta-description: Find out all about images in CKEditor 5 - available image features, attributes, or the image toolbar. order: 10 modified_at: 2021-06-17 --- @@ -37,7 +37,7 @@ The [`@ckeditor/ckeditor5-image`](https://www.npmjs.com/package/@ckeditor/ckedit * {@link features/images-resizing Image resizing} lets the user control the dimensions of images in the content. * {@link features/images-linking Linking images} makes it possible to use them as URL anchors. * A selection of {@link features/image-upload image upload methods} allows for the most convenient way of adding images. These include support for {@link features/images-inserting#inserting-images-via-pasting-a-url-into-the-editor inserting an image via a URL} and even {@link features/images-inserting#inserting-images-via-a-source-url via pasting a URL into the editor} along with custom integrations. -* Support for {@link features/images-responsive responsive images} in CKEditor 5 is brought by the {@link features/ckbox CKBox} management platform. Responsive images will display properly on any viewport, enhancing the accessibility, reach and user experience. +* Support for {@link features/images-responsive responsive images} in CKEditor 5 is brought by the {@link features/ckbox CKBox} management platform. Responsive images will display properly on any viewport, enhancing the accessibility, reach, and user experience. The availability of these plugins varies in different {@link installation/getting-started/predefined-builds predefined editor builds} but the most important ones are present in all builds as presented in the table below: From 24848e0e7390d44e90bf51e90f2c915c3d0ff3da Mon Sep 17 00:00:00 2001 From: godai78 Date: Wed, 25 Oct 2023 12:52:53 +0200 Subject: [PATCH 4/8] Docs: listing update. [short flow] --- .../ckeditor5-image/docs/features/images-installation.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-image/docs/features/images-installation.md b/packages/ckeditor5-image/docs/features/images-installation.md index 794e12ce3eb..5ff3490a436 100644 --- a/packages/ckeditor5-image/docs/features/images-installation.md +++ b/packages/ckeditor5-image/docs/features/images-installation.md @@ -35,7 +35,11 @@ ClassicEditor 'imageTextAlternative', '|', 'linkImage' - ] + ], + insert: { + type: 'auto' + } + // If this setting is omitted, the editor defaults to `block`. See explanation below. }, } ) .then( /* ... */ ) @@ -98,7 +102,7 @@ ClassicEditor.create( element, { } ); ``` -There are three possible options: `auto`, `block`, and `inline`. The `auto` option uses the default setting, while the other two force the use of the selected image type at insert. +There are three possible options: `auto`, `block`, and `inline`. The `auto` option uses the default setting, while the other two force the use of the selected image type at insert. If this setting is omitted in the configuration, the editor defaults to `block`. ## Contribute From 7c3c9f270b584899e039813275d06e645589cbc1 Mon Sep 17 00:00:00 2001 From: godai78 Date: Wed, 25 Oct 2023 13:00:37 +0200 Subject: [PATCH 5/8] Docs: listing formatting for better readibility. [short flow] --- packages/ckeditor5-image/docs/features/images-installation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/ckeditor5-image/docs/features/images-installation.md b/packages/ckeditor5-image/docs/features/images-installation.md index 5ff3490a436..17bf51fe012 100644 --- a/packages/ckeditor5-image/docs/features/images-installation.md +++ b/packages/ckeditor5-image/docs/features/images-installation.md @@ -39,7 +39,8 @@ ClassicEditor insert: { type: 'auto' } - // If this setting is omitted, the editor defaults to `block`. See explanation below. + // If this setting is omitted, the editor defaults to `block`. + // See explanation below. }, } ) .then( /* ... */ ) From 6452b111e2493cd71aa714dd922be17736cf884e Mon Sep 17 00:00:00 2001 From: Arkadiusz Filipczak Date: Thu, 26 Oct 2023 15:19:08 +0200 Subject: [PATCH 6/8] Fixes in docs for inserting images as block by default. --- .../docs/features/images-installation.md | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/ckeditor5-image/docs/features/images-installation.md b/packages/ckeditor5-image/docs/features/images-installation.md index 17bf51fe012..1117a42c42b 100644 --- a/packages/ckeditor5-image/docs/features/images-installation.md +++ b/packages/ckeditor5-image/docs/features/images-installation.md @@ -37,11 +37,11 @@ ClassicEditor 'linkImage' ], insert: { + // If this setting is omitted, the editor defaults to `'block'`. + // See explanation below. type: 'auto' } - // If this setting is omitted, the editor defaults to `block`. - // See explanation below. - }, + } } ) .then( /* ... */ ) .catch( /* ... */ ); @@ -89,9 +89,9 @@ By default, the {@link module:image/image~Image} plugin available in all {@link -By default, all images inserted into the content are treated as block images. It means, that inserting an image inside a paragraph (or inside other content blocks) will split the paragraph and add a new block for the image. Once inserted, the image can be turned into an inline image with the use of the {@link features/images-overview#image-contextual-toolbar contextual toolbar}. +By default, if the `image.insert.type` configuration is not specified, all images inserted into the content will be treated as block images. This means that inserting an image inside a paragraph (or other content blocks) will create a new block for the image immediately below or above the current paragraph or block. After insertion, you can transform the block image into an inline image using the {@link features/images-overview#image-contextual-toolbar contextual toolbar}. -To change this default behavior, you can use the `type` setting in the editor configuration: +If you wish to modify this behavior, the type setting in the editor configuration can be used: ```js ClassicEditor.create( element, { @@ -103,7 +103,15 @@ ClassicEditor.create( element, { } ); ``` -There are three possible options: `auto`, `block`, and `inline`. The `auto` option uses the default setting, while the other two force the use of the selected image type at insert. If this setting is omitted in the configuration, the editor defaults to `block`. +The type setting accepts three values: + +- `'auto'`: The editor determines the image type based on the cursor's position. For example, if you insert an image in the middle of a paragraph, it will be inserted as inline. If you insert it at the end or beginning of a paragraph, it becomes a block image. +- `'block'`: Always insert images as block elements, placing them below or above the current paragraph or block. +- `'inline'`: Always insert images as inline elements within the current paragraph or block. + +If the type setting is omitted from the configuration, the behavior defaults to inserting images as block. + +**Important**: If only one type of image plugin is enabled (e.g., `ImageInline` is enabled but `ImageBlock` is not), the `image.insert.type` configuration will be effectively ignored and the supported image type will be used. ## Contribute From 34b61da765c687c4f7db399e3875de9c4cc2af33 Mon Sep 17 00:00:00 2001 From: godai78 Date: Fri, 27 Oct 2023 07:25:07 +0200 Subject: [PATCH 7/8] Docs: minor fixes. [short flow] --- .../docs/features/images-installation.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/ckeditor5-image/docs/features/images-installation.md b/packages/ckeditor5-image/docs/features/images-installation.md index 1117a42c42b..c0bd081565f 100644 --- a/packages/ckeditor5-image/docs/features/images-installation.md +++ b/packages/ckeditor5-image/docs/features/images-installation.md @@ -37,7 +37,7 @@ ClassicEditor 'linkImage' ], insert: { - // If this setting is omitted, the editor defaults to `'block'`. + // If this setting is omitted, the editor defaults to 'block'. // See explanation below. type: 'auto' } @@ -91,7 +91,7 @@ By default, the {@link module:image/image~Image} plugin available in all {@link By default, if the `image.insert.type` configuration is not specified, all images inserted into the content will be treated as block images. This means that inserting an image inside a paragraph (or other content blocks) will create a new block for the image immediately below or above the current paragraph or block. After insertion, you can transform the block image into an inline image using the {@link features/images-overview#image-contextual-toolbar contextual toolbar}. -If you wish to modify this behavior, the type setting in the editor configuration can be used: +If you wish to modify this behavior, the `type` setting in the editor configuration can be used: ```js ClassicEditor.create( element, { @@ -103,13 +103,13 @@ ClassicEditor.create( element, { } ); ``` -The type setting accepts three values: +The `type` setting accepts the following three values: -- `'auto'`: The editor determines the image type based on the cursor's position. For example, if you insert an image in the middle of a paragraph, it will be inserted as inline. If you insert it at the end or beginning of a paragraph, it becomes a block image. -- `'block'`: Always insert images as block elements, placing them below or above the current paragraph or block. -- `'inline'`: Always insert images as inline elements within the current paragraph or block. +* `'auto'`: The editor determines the image type based on the cursor's position. For example, if you insert an image in the middle of a paragraph, it will be inserted as inline. If you insert it at the end or beginning of a paragraph, it becomes a block image. +* `'block'`: Always insert images as block elements, placing them below or above the current paragraph or block. +* `'inline'`: Always insert images as inline elements within the current paragraph or block. -If the type setting is omitted from the configuration, the behavior defaults to inserting images as block. +If the `type` setting is omitted from the configuration, the behavior defaults to inserting images as a block. **Important**: If only one type of image plugin is enabled (e.g., `ImageInline` is enabled but `ImageBlock` is not), the `image.insert.type` configuration will be effectively ignored and the supported image type will be used. From b48e588fc0e498377fd2d49d279cf581f11b5776 Mon Sep 17 00:00:00 2001 From: Arkadiusz Filipczak Date: Fri, 27 Oct 2023 08:29:50 +0200 Subject: [PATCH 8/8] Inserting image: fixes after code review. --- packages/ckeditor5-image/tests/imageutils.js | 12 ++++++++++-- packages/ckeditor5-image/tests/manual/imageinsert.js | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/ckeditor5-image/tests/imageutils.js b/packages/ckeditor5-image/tests/imageutils.js index 57e38216a1a..f98250fccbf 100644 --- a/packages/ckeditor5-image/tests/imageutils.js +++ b/packages/ckeditor5-image/tests/imageutils.js @@ -537,7 +537,7 @@ describe( 'ImageUtils plugin', () => { console.warn.restore(); } ); - it( 'should use block the image type when there is only ImageBlockEditing plugin enabled', async () => { + it( 'should use the block image type when there is only ImageBlockEditing plugin enabled', async () => { const consoleWarnStub = sinon.stub( console, 'warn' ); await editor.destroy(); @@ -699,6 +699,14 @@ describe( 'ImageUtils plugin', () => { expect( getModelData( model ) ).to.equal( 'f[]o' ); } ); + it( 'should use the inline image type in an empty paragraph', () => { + setModelData( model, '[]' ); + + editor.plugins.get( 'ImageUtils' ).insertImage(); + + expect( getModelData( model ) ).to.equal( '[]' ); + } ); + it( 'should use the block image type when ImageInlineEditing plugin is not enabled', async () => { const consoleWarnStub = sinon.stub( console, 'warn' ); @@ -759,7 +767,7 @@ describe( 'ImageUtils plugin', () => { console.warn.restore(); } ); - it( 'should use block the image type by default', () => { + it( 'should use the block image type by default', () => { setModelData( model, 'f[o]o' ); editor.plugins.get( 'ImageUtils' ).insertImage(); diff --git a/packages/ckeditor5-image/tests/manual/imageinsert.js b/packages/ckeditor5-image/tests/manual/imageinsert.js index a7ded8edada..948d39ae120 100644 --- a/packages/ckeditor5-image/tests/manual/imageinsert.js +++ b/packages/ckeditor5-image/tests/manual/imageinsert.js @@ -54,7 +54,7 @@ function createEditor( elementId, imageType ) { .then( editor => { window[ elementId ] = editor; - CKEditorInspector.attach( imageType, editor ); + CKEditorInspector.attach( { [ imageType ]: editor } ); } ) .catch( err => { console.error( err );