Skip to content

Commit

Permalink
Merge pull request #9418 from tony/tn-mediaembed-element-name-alt
Browse files Browse the repository at this point in the history
Feature (media-embed): Introduced the `config.mediaEmbed.elementName` to allow setting semantic element name. Closes #9373.
  • Loading branch information
psmyrek committed Apr 12, 2021
1 parent e142d6d commit aefc6a2
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 7 deletions.
10 changes: 10 additions & 0 deletions packages/ckeditor5-media-embed/docs/features/media-embed.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ By default, the media embed feature outputs semantic `<oembed url="...">` tags f
</figure>
```

Further customization of semantic data output can be done through the {@link module:media-embed/mediaembed~MediaEmbedConfig#elementName `config.mediaEmbed.elementName`}. As an example, if `elementName` is set to `o-embed`:

```html
<figure class="media">
<o-embed url="https://media-url"></o-embed>
</figure>
```

If `elementName` is overridden to something beside the default value, existing `<oembed>` elements will still be shown when for backward compatibility purposes.

#### Including previews in data

Optionally, by setting `mediaEmbed.previewsInData` to `true` you can configure the media embed feature to output media in the same way they look in the editor. So if the media element is "previewable", the media preview (HTML) is saved to the database:
Expand Down
1 change: 1 addition & 0 deletions packages/ckeditor5-media-embed/src/converters.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* @param {module:media-embed/mediaregistry~MediaRegistry} registry The registry providing
* the media and their content.
* @param {Object} options
* @param {String} [options.elementName] When set, overrides the default element name for semantic media embeds.
* @param {String} [options.renderMediaPreview] When `true`, the converter will create the view in the non-semantic form.
* @param {String} [options.renderForEditingView] When `true`, the converter will create a view specific for the
* editing pipeline (e.g. including CSS classes, content placeholders).
Expand Down
28 changes: 28 additions & 0 deletions packages/ckeditor5-media-embed/src/mediaembed.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,34 @@ export default class MediaEmbed extends Plugin {
* @member {Array.<String>} module:media-embed/mediaembed~MediaEmbedConfig#removeProviders
*/

/**
* Overrides the element name used for "semantic" data.
*
* This is not relevant if {@link module:media-embed/mediaembed~MediaEmbedConfig#previewsInData `config.mediaEmbed.previewsInData`}
* is set to `true`.
*
* When unset, the feature produces tag `<oembed>`:
*
* <figure class="media">
* <oembed url="https://url"></oembed>
* </figure>
*
* To override the element name with, for instance, the `o-embed` name:
*
* mediaEmbed: {
* elementName: 'o-embed'
* }
*
* This will produce semantic data with `<o-embed>` tag:
*
* <figure class="media">
* <o-embed url="https://url"></o-embed>
* </figure>
*
* @default 'oembed'
* @member {String} [module:media-embed/mediaembed~MediaEmbedConfig#elementName]
*/

/**
* Controls the data format produced by the feature.
*
Expand Down
16 changes: 10 additions & 6 deletions packages/ckeditor5-media-embed/src/mediaembedediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default class MediaEmbedEditing extends Plugin {
super( editor );

editor.config.define( 'mediaEmbed', {
elementName: 'oembed',
providers: [
{
name: 'dailymotion',
Expand Down Expand Up @@ -162,6 +163,8 @@ export default class MediaEmbedEditing extends Plugin {
const t = editor.t;
const conversion = editor.conversion;
const renderMediaPreview = editor.config.get( 'mediaEmbed.previewsInData' );
const elementName = editor.config.get( 'mediaEmbed.elementName' );

const registry = this.registry;

editor.commands.add( 'mediaEmbed', new MediaEmbedCommand( editor ) );
Expand All @@ -181,6 +184,7 @@ export default class MediaEmbedEditing extends Plugin {
const url = modelElement.getAttribute( 'url' );

return createMediaFigureElement( writer, registry, url, {
elementName,
renderMediaPreview: url && renderMediaPreview
} );
}
Expand All @@ -189,6 +193,7 @@ export default class MediaEmbedEditing extends Plugin {
// Model -> Data (url -> data-oembed-url)
conversion.for( 'dataDowncast' ).add(
modelToViewUrlAttributeConverter( registry, {
elementName,
renderMediaPreview
} ) );

Expand All @@ -198,6 +203,7 @@ export default class MediaEmbedEditing extends Plugin {
view: ( modelElement, { writer } ) => {
const url = modelElement.getAttribute( 'url' );
const figure = createMediaFigureElement( writer, registry, url, {
elementName,
renderForEditingView: true
} );

Expand All @@ -208,19 +214,17 @@ export default class MediaEmbedEditing extends Plugin {
// Model -> View (url -> data-oembed-url)
conversion.for( 'editingDowncast' ).add(
modelToViewUrlAttributeConverter( registry, {
elementName,
renderForEditingView: true
} ) );

// View -> Model (data-oembed-url -> url)
conversion.for( 'upcast' )
// Upcast semantic media.
.elementToElement( {
view: {
name: 'oembed',
attributes: {
url: true
}
},
view: element => [ 'oembed', elementName ].includes( element.name ) && element.getAttribute( 'url' ) ?
{ name: true } :
null,
model: ( viewMedia, { writer } ) => {
const url = viewMedia.getAttribute( 'url' );

Expand Down
4 changes: 3 additions & 1 deletion packages/ckeditor5-media-embed/src/mediaregistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default class MediaRegistry {
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer used to produce a view element.
* @param {String} url The URL to be translated into a view element.
* @param {Object} options
* @param {String} [options.elementName]
* @param {String} [options.renderMediaPreview]
* @param {String} [options.renderForEditingView]
* @returns {module:engine/view/element~Element}
Expand Down Expand Up @@ -206,6 +207,7 @@ class Media {
*
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer used to produce a view element.
* @param {Object} options
* @param {String} [options.elementName]
* @param {String} [options.renderMediaPreview]
* @param {String} [options.renderForEditingView]
* @returns {module:engine/view/element~Element}
Expand Down Expand Up @@ -233,7 +235,7 @@ class Media {
attributes.url = this.url;
}

viewElement = writer.createEmptyElement( 'oembed', attributes );
viewElement = writer.createEmptyElement( options.elementName, attributes );
}

writer.setCustomProperty( 'media-content', true, viewElement );
Expand Down
1 change: 1 addition & 0 deletions packages/ckeditor5-media-embed/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export function isMediaWidget( viewElement ) {
* @param {module:media-embed/mediaregistry~MediaRegistry} registry
* @param {String} url
* @param {Object} options
* @param {String} [options.elementName]
* @param {String} [options.useSemanticWrapper]
* @param {String} [options.renderForEditingView]
* @returns {module:engine/view/containerelement~ContainerElement}
Expand Down
150 changes: 150 additions & 0 deletions packages/ckeditor5-media-embed/tests/mediaembedediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,156 @@ describe( 'MediaEmbedEditing', () => {
} );

describe( 'conversion in the data pipeline', () => {
describe( 'elementName#o-embed', () => {
beforeEach( () => {
return createTestEditor( {
elementName: 'o-embed',
providers: providerDefinitions
} )
.then( newEditor => {
editor = newEditor;
model = editor.model;
doc = model.document;
view = editor.editing.view;
} );
} );

describe( 'model to view', () => {
it( 'should convert', () => {
setModelData( model, '<media url="https://ckeditor.com"></media>' );

expect( editor.getData() ).to.equal(
'<figure class="media">' +
'<o-embed url="https://ckeditor.com"></o-embed>' +
'</figure>' );
} );

it( 'should convert (no url)', () => {
setModelData( model, '<media></media>' );

expect( editor.getData() ).to.equal(
'<figure class="media">' +
'<o-embed></o-embed>' +
'</figure>' );
} );

it( 'should convert (preview-less media)', () => {
setModelData( model, '<media url="https://preview-less"></media>' );

expect( editor.getData() ).to.equal(
'<figure class="media">' +
'<o-embed url="https://preview-less"></o-embed>' +
'</figure>' );
} );
} );

describe( 'view to model', () => {
it( 'should convert media figure', () => {
editor.setData( '<figure class="media"><o-embed url="https://ckeditor.com"></o-embed></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '<media url="https://ckeditor.com"></media>' );
} );

it( 'should not convert if there is no media class', () => {
editor.setData( '<figure class="quote">My quote</figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should not convert if there is no o-embed wrapper inside #1', () => {
editor.setData( '<figure class="media"></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should not convert if there is no o-embed wrapper inside #2', () => {
editor.setData( '<figure class="media">test</figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should not convert when the wrapper has no data-o-embed-url attribute', () => {
editor.setData( '<figure class="media"><div></div></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should not convert in the wrong context', () => {
model.schema.register( 'blockquote', { inheritAllFrom: '$block' } );
model.schema.addChildCheck( ( ctx, childDef ) => {
if ( ctx.endsWith( '$root' ) && childDef.name == 'media' ) {
return false;
}
} );

editor.conversion.elementToElement( { model: 'blockquote', view: 'blockquote' } );

editor.setData(
'<blockquote><figure class="media"><o-embed url="https://ckeditor.com"></o-embed></figure></blockquote>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '<blockquote></blockquote>' );
} );

it( 'should not convert if the o-embed wrapper is already consumed', () => {
editor.data.upcastDispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
const img = data.viewItem.getChild( 0 );
conversionApi.consumable.consume( img, { name: true } );
}, { priority: 'high' } );

editor.setData( '<figure class="media"><o-embed url="https://ckeditor.com"></o-embed></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should not convert if the figure is already consumed', () => {
editor.data.upcastDispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
conversionApi.consumable.consume( data.viewItem, { name: true, class: 'image' } );
}, { priority: 'high' } );

editor.setData( '<figure class="media"><o-embed url="https://ckeditor.com"></o-embed></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '' );
} );

it( 'should discard the contents of the media', () => {
editor.setData( '<figure class="media"><o-embed url="https://ckeditor.com">foo bar</o-embed></figure>' );

expect( getModelData( model, { withoutSelection: true } ) )
.to.equal( '<media url="https://ckeditor.com"></media>' );
} );

it( 'should not convert unknown media', () => {
return createTestEditor( {
providers: [
testProviders.A
]
} )
.then( newEditor => {
newEditor.setData(
'<figure class="media">' +
'<div data-oembed-url="foo.com/123"></div>' +
'</figure>' +
'<figure class="media">' +
'<div data-oembed-url="unknown.media/123"></div>' +
'</figure>' );

expect( getModelData( newEditor.model, { withoutSelection: true } ) )
.to.equal( '<media url="foo.com/123"></media>' );

return newEditor.destroy();
} );
} );
} );
} );

describe( 'previewsInData=false', () => {
beforeEach( () => {
return createTestEditor( {
Expand Down

0 comments on commit aefc6a2

Please sign in to comment.