diff --git a/packages/ckeditor5-html-embed/lang/contexts.json b/packages/ckeditor5-html-embed/lang/contexts.json index 329e539b754..3227cf17be9 100644 --- a/packages/ckeditor5-html-embed/lang/contexts.json +++ b/packages/ckeditor5-html-embed/lang/contexts.json @@ -3,5 +3,7 @@ "HTML snippet": "The HTML snippet.", "Paste raw HTML here...": "A placeholder that will be displayed in the raw HTML textarea field.", "Edit source": "A label of a button that switches the HTML embed to the source editing mode.", - "Save changes": "A label of a button that saves the HTML embed content and navigates back to the preview." + "Save changes": "A label of a button that saves the HTML embed content and navigates back to the preview.", + "No preview available": "An information displayed in the HTML embed preview if the content is not previewable.", + "Empty snippet content": "An information displayed in the HTML embed preview if the HTML snippet has no content." } diff --git a/packages/ckeditor5-html-embed/src/htmlembedediting.js b/packages/ckeditor5-html-embed/src/htmlembedediting.js index 117c973952d..83151e4d25e 100644 --- a/packages/ckeditor5-html-embed/src/htmlembedediting.js +++ b/packages/ckeditor5-html-embed/src/htmlembedediting.js @@ -327,13 +327,27 @@ export default class HtmlEmbedEditing extends Plugin { } function createPreviewContainer( { domDocument, state, props, editor } ) { - const domPreviewContainer = createElement( domDocument, 'div', { - class: 'raw-html-embed__preview', + const sanitizedOutput = props.sanitizeHtml( state.getRawHtmlValue() ); + const placeholderText = state.getRawHtmlValue().length > 0 ? + t( 'No preview available' ) : + t( 'Empty snippet content' ); + + const domPreviewPlaceholder = createElement( domDocument, 'div', { + class: 'ck ck-reset_all raw-html-embed__preview-placeholder' + }, placeholderText ); + + const domPreviewContent = createElement( domDocument, 'div', { + class: 'raw-html-embed__preview-content', dir: editor.locale.contentLanguageDirection } ); - const sanitizeOutput = props.sanitizeHtml( state.getRawHtmlValue() ); - domPreviewContainer.innerHTML = sanitizeOutput.html; + domPreviewContent.innerHTML = sanitizedOutput.html; + + const domPreviewContainer = createElement( domDocument, 'div', { + class: 'raw-html-embed__preview' + }, [ + domPreviewPlaceholder, domPreviewContent + ] ); return domPreviewContainer; } diff --git a/packages/ckeditor5-html-embed/tests/htmlembedediting.js b/packages/ckeditor5-html-embed/tests/htmlembedediting.js index 54cd05c5aae..fa7f4328910 100644 --- a/packages/ckeditor5-html-embed/tests/htmlembedediting.js +++ b/packages/ckeditor5-html-embed/tests/htmlembedediting.js @@ -695,16 +695,20 @@ describe( 'HtmlEmbedEditing', () => { } ); } ); - it( 'renders a div with a preview', () => { + it( 'should render a div with a preview and placeholder', () => { setModelData( model, '' ); const widget = viewDocument.getRoot().getChild( 0 ); const contentWrapper = widget.getChild( 1 ); const domContentWrapper = editor.editing.view.domConverter.mapViewToDom( contentWrapper ); - expect( domContentWrapper.querySelector( 'div.raw-html-embed__preview' ).innerHTML ).to.equal( 'foo' ); + expect( domContentWrapper.querySelector( 'div.raw-html-embed__preview-content' ).innerHTML ) + .to.equal( 'foo' ); + + expect( domContentWrapper.querySelector( 'div.raw-html-embed__preview-placeholder' ) ) + .to.not.equal( null ); } ); - it( 'updates the preview once the model changes', () => { + it( 'should update the preview once the model changes', () => { setModelData( model, '' ); editor.model.change( writer => writer.setAttribute( 'value', 'bar', editor.model.document.getRoot().getChild( 0 ) ) ); @@ -713,7 +717,38 @@ describe( 'HtmlEmbedEditing', () => { const contentWrapper = widget.getChild( 1 ); const domContentWrapper = editor.editing.view.domConverter.mapViewToDom( contentWrapper ); - expect( domContentWrapper.querySelector( 'div.raw-html-embed__preview' ).innerHTML ).to.equal( 'bar' ); + expect( domContentWrapper.querySelector( 'div.raw-html-embed__preview-content' ).innerHTML ).to.equal( 'bar' ); + } ); + + describe( 'placeholder', () => { + function getPlaceholder() { + const widget = viewDocument.getRoot().getChild( 0 ); + const contentWrapper = widget.getChild( 1 ); + const domContentWrapper = editor.editing.view.domConverter.mapViewToDom( contentWrapper ); + + return domContentWrapper.querySelector( 'div.raw-html-embed__preview-placeholder' ); + } + + it( 'should inherit the styles from the editor', () => { + setModelData( model, '' ); + const placeholder = getPlaceholder(); + + expect( placeholder.classList.value ).to.contain( 'ck ck-reset_all' ); + } ); + + it( 'should display the proper information if the snippet is empty', () => { + setModelData( model, '' ); + const placeholder = getPlaceholder(); + + expect( placeholder.innerHTML ).to.equal( 'Empty snippet content' ); + } ); + + it( 'should display the proper information if the snippet is not empty', () => { + setModelData( model, '' ); + const placeholder = getPlaceholder(); + + expect( placeholder.innerHTML ).to.equal( 'No preview available' ); + } ); } ); describe( 'different setting of ui and content language', () => { @@ -769,7 +804,7 @@ describe( 'HtmlEmbedEditing', () => { const contentWrapper = widget.getChild( 1 ); const domContentWrapper = editor.editing.view.domConverter.mapViewToDom( contentWrapper ); - return domContentWrapper.querySelector( 'div.raw-html-embed__preview' ); + return domContentWrapper.querySelector( 'div.raw-html-embed__preview-content' ); } } ); } ); diff --git a/packages/ckeditor5-html-embed/tests/manual/htmlembed.html b/packages/ckeditor5-html-embed/tests/manual/htmlembed.html index 4380ce4bd1b..800bb5c3f46 100644 --- a/packages/ckeditor5-html-embed/tests/manual/htmlembed.html +++ b/packages/ckeditor5-html-embed/tests/manual/htmlembed.html @@ -169,4 +169,12 @@

HTML comments in HTML snippet

fooo
+ +

Not previewable content in HTML snippet

+
+ +
+ +

Empty HTML snippet

+
diff --git a/packages/ckeditor5-html-embed/tests/manual/htmlembed.md b/packages/ckeditor5-html-embed/tests/manual/htmlembed.md index 8425a29bc16..58ceabe1d6b 100644 --- a/packages/ckeditor5-html-embed/tests/manual/htmlembed.md +++ b/packages/ckeditor5-html-embed/tests/manual/htmlembed.md @@ -18,17 +18,22 @@ --- -After the editor initialization, it should contain 5 widgets with embedded HTML: +After the editor initialization, it should contain 8 widgets with embedded HTML: - `video` - `audio` - `picture` (resize the window to see other images, limits: 1200px, 650px) - `iframe` - `table` +- `text with comment` +- `comment` +- `empty` -All resources are provided by the ["Sample Files for Development"](http://techslides.com/sample-files-for-development) article. Thanks! +Resources are provided by the ["Sample Files for Development"](http://techslides.com/sample-files-for-development) article. Thanks! -By default, the "previews in view" mode is enabled. It means that previews should be visible. +By default, the "previews in view" mode is enabled. It means that previews should be visible. +* If no preview is available (e.g. the only content of HTML snippet is a comment or script), the text "No preview available" should be displayed. +* If the HTML snippet is empty, the text "Empty content" should be displayed. We use the [`sanitize-html`](https://www.npmjs.com/package/sanitize-html) package to clean up the input HTML. It means that some of the elements or attributes may be not rendered in the editing view. However, they still will be returned in the editor's data. diff --git a/packages/ckeditor5-html-embed/theme/htmlembed.css b/packages/ckeditor5-html-embed/theme/htmlembed.css index 84bdaa96f25..3cc26905490 100644 --- a/packages/ckeditor5-html-embed/theme/htmlembed.css +++ b/packages/ckeditor5-html-embed/theme/htmlembed.css @@ -27,6 +27,35 @@ display: flex; flex-direction: column; } + + & .raw-html-embed__preview { + position: relative; + overflow: hidden; + display: flex; + } + + & .raw-html-embed__preview-content { + width: 100%; + position: relative; + margin: auto; + + /* Gives spacing to the small renderable elements, so they always cover the placeholder. */ + display: table; + border-collapse: separate; + border-spacing: 7px; + } + + & .raw-html-embed__preview-placeholder { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + + display: flex; + align-items: center; + justify-content: center; + } } .ck-content .raw-html-embed { diff --git a/packages/ckeditor5-theme-lark/theme/ckeditor5-html-embed/htmlembed.css b/packages/ckeditor5-theme-lark/theme/ckeditor5-html-embed/htmlembed.css index bec464292d8..b89d2273ead 100644 --- a/packages/ckeditor5-theme-lark/theme/ckeditor5-html-embed/htmlembed.css +++ b/packages/ckeditor5-theme-lark/theme/ckeditor5-html-embed/htmlembed.css @@ -131,20 +131,27 @@ /* The preview data container. */ & .raw-html-embed__preview { - overflow: hidden; - box-sizing: border-box; - width: var(--ck-html-embed-content-width); min-height: var(--ck-html-embed-content-min-height); + width: var(--ck-html-embed-content-width); + + /* Disable all mouse interaction as long as the editor is not read–only. */ + @nest .ck-editor__editable:not(.ck-read-only) & { + pointer-events: none; + } + } + + & .raw-html-embed__preview-content { + box-sizing: border-box; text-align: center; + background-color: var(--ck-color-base-foreground); - & > table { + & > * { margin-left: auto; margin-right: auto; } + } - /* Disable all mouse interaction as long as the editor is not read–only. */ - @nest .ck-editor__editable:not(.ck-read-only) & { - pointer-events: none; - } + & .raw-html-embed__preview-placeholder { + color: var(--ck-html-embed-source-disabled-color) } }