Skip to content

Commit

Permalink
Merge pull request #3558 from neos/bugfix/3557-placeholder-plugin-aut…
Browse files Browse the repository at this point in the history
…oparagraph-false

BUGFIX: 3557 placeholder plugin with autoparagraph false
  • Loading branch information
mhsdesign authored Jul 3, 2023
2 parents 5ec6536 + 19402f2 commit bb7dd82
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 136 deletions.
1 change: 1 addition & 0 deletions packages/neos-ui-ckeditor5-bindings/src/ckEditorApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import debounce from 'lodash.debounce';
import DecoupledEditor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor';
import {actions} from '@neos-project/neos-ui-redux-store';
import {cleanupContentBeforeCommit} from './cleanupContentBeforeCommit'
import './placeholder.vanilla-css'; // eslint-disable-line no-unused-vars

let currentEditor = null;
let editorConfig = {};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,9 @@
// We remove opening and closing span tags that are produced by the inlineMode plugin

// TODO: remove when this is fixed: https://github.com/ckeditor/ckeditor5/issues/401
/** @param {String} content */
export const cleanupContentBeforeCommit = content => {
// TODO: remove when this is fixed: https://github.com/ckeditor/ckeditor5/issues/401
if (content.match(/^<([a-z][a-z0-9]*)\b[^>]*>&nbsp;<\/\1>$/)) {
return '';
}

if (content.includes('<neos-inline-wrapper>')) {
let contentWithoutOuterInlineWrapper = content;

if (content.startsWith('<neos-inline-wrapper>') && content.endsWith('</neos-inline-wrapper>')) {
contentWithoutOuterInlineWrapper = content
.replace(/^<neos-inline-wrapper>/, '')
.replace(/<\/neos-inline-wrapper>$/, '');
}

if (contentWithoutOuterInlineWrapper.includes('<neos-inline-wrapper>')) {
// in the case, multiple root paragraph elements were inserted into the ckeditor (wich is currently not prevented if the html is modified from outside)
// we have multiple root elements of type <neos-inline-wrapper>. We will convert all of them into spans.
return content
.replace(/<neos-inline-wrapper>/g, '<span>')
.replace(/<\/neos-inline-wrapper>/g, '</span>');
}
return contentWithoutOuterInlineWrapper;
}
return content;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {cleanupContentBeforeCommit} from './cleanupContentBeforeCommit'
import {cleanupContentBeforeCommit} from './cleanupContentBeforeCommit';

const assertCleanedUpContent = (input, expected) => {
expect(cleanupContentBeforeCommit(input)).toBe(expected);
Expand All @@ -8,29 +8,3 @@ test('remove empty nbsp', () => {
assertCleanedUpContent('<p>&nbsp;</p>', '');
assertCleanedUpContent('<span>&nbsp;</span>', '');
})

describe('ckeditor inline mode hack, cleanup <neos-inline-wrapper>', () => {
test('noop', () => {
assertCleanedUpContent('<p></p>', '<p></p>');

assertCleanedUpContent('', '');
})

test('cleanup single <neos-inline-wrapper>', () => {
assertCleanedUpContent('<neos-inline-wrapper></neos-inline-wrapper>', '');
assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper>', 'foo');

assertCleanedUpContent('<neos-inline-wrapper><span>foo</span></neos-inline-wrapper>', '<span>foo</span>');
})

test('cleanup multiple <neos-inline-wrapper>', () => {
assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper><neos-inline-wrapper>bar</neos-inline-wrapper>', '<span>foo</span><span>bar</span>');

assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper><neos-inline-wrapper>bar</neos-inline-wrapper>', '<span>foo</span><span>bar</span>');
})

test('cleanup <neos-inline-wrapper> after other root', () => {
// in the case you had multiple paragraphs and a headline and switched to autoparagrahp: false
assertCleanedUpContent('<h1>foo</h1><neos-inline-wrapper>bar</neos-inline-wrapper>', '<h1>foo</h1><span>bar</span>');
})
})
16 changes: 10 additions & 6 deletions packages/neos-ui-ckeditor5-bindings/src/manifest.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import CkEditorConfigRegistry from './registry/CkEditorConfigRegistry';
import {$add, $get, $or} from 'plow-js';
import {stripTags} from '@neos-project/utils-helpers';

import NeosPlaceholder from './plugins/neosPlaceholder';
import InlineMode from './plugins/inlineMode';
import Sub from './plugins/sub';
import Sup from './plugins/sup';
Expand Down Expand Up @@ -81,13 +81,18 @@ export default ckEditorRegistry => {

//
// Base CKE configuration
// - configuration of language
// - and placeholder feature see https://ckeditor.com/docs/ckeditor5/16.0.0/api/module_core_editor_editorconfig-EditorConfig.html#member-placeholder
//
config.set('baseConfiguration', (ckEditorConfiguration, {globalRegistry, editorOptions, userPreferences}) => {
const i18nRegistry = globalRegistry.get('i18n');
return Object.assign(ckEditorConfiguration, {
language: String($get('interfaceLanguage', userPreferences)),
neosPlaceholder: unescape(i18nRegistry.translate($get('placeholder', editorOptions) || ''))
});
const placeholder = $get('placeholder', editorOptions);
return {
...ckEditorConfiguration,
// stripTags, because we allow `<p>Edit text here</p>` as placeholder for legacy
placeholder: placeholder ? stripTags(i18nRegistry.translate(placeholder)) : undefined,
language: String($get('interfaceLanguage', userPreferences))
};
});

//
Expand All @@ -96,7 +101,6 @@ export default ckEditorRegistry => {
config.set('essentials', addPlugin(Essentials));
config.set('paragraph', addPlugin(Paragraph));
config.set('inlineMode', addPlugin(InlineMode, disableParagraph));
config.set('neosPlaceholder', addPlugin(NeosPlaceholder));
config.set('sub', addPlugin(Sub, $get('formatting.sub')));
config.set('sup', addPlugin(Sup, $get('formatting.sup')));
config.set('bold', addPlugin(Bold, $get('formatting.strong')));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.ck.ck-placeholder:before, .ck .ck-placeholder:before {
content: attr(data-placeholder);

/* See ckeditor/ckeditor5#469. */
pointer-events: none;

color: #999;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* We remove opening and closing span tags that are produced by the inlineMode plugin
*
* @private only exported for testing
* @param {String} content
*/
export const cleanupNeosInlineWrapper = content => {
if (content.includes('<neos-inline-wrapper>')) {
let contentWithoutOuterInlineWrapper = content;

if (content.startsWith('<neos-inline-wrapper>') && content.endsWith('</neos-inline-wrapper>')) {
contentWithoutOuterInlineWrapper = content
.replace(/^<neos-inline-wrapper>/, '')
.replace(/<\/neos-inline-wrapper>$/, '');
}

if (contentWithoutOuterInlineWrapper.includes('<neos-inline-wrapper>')) {
// in the case, multiple root paragraph elements were inserted into the ckeditor (wich is currently not prevented if the html is modified from outside)
// we have multiple root elements of type <neos-inline-wrapper>. We will convert all of them into spans.
return content
.replace(/<neos-inline-wrapper>/g, '<span>')
.replace(/<\/neos-inline-wrapper>/g, '</span>');
}
return contentWithoutOuterInlineWrapper;
}
return content;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {cleanupNeosInlineWrapper} from './cleanupNeosInlineWrapper';

const assertCleanedUpContent = (input, expected) => {
expect(cleanupNeosInlineWrapper(input)).toBe(expected);
}

describe('ckeditor inline mode hack, cleanup <neos-inline-wrapper>', () => {
test('noop', () => {
assertCleanedUpContent('<p></p>', '<p></p>');

assertCleanedUpContent('', '');
})

test('cleanup single <neos-inline-wrapper>', () => {
assertCleanedUpContent('<neos-inline-wrapper></neos-inline-wrapper>', '');
assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper>', 'foo');

assertCleanedUpContent('<neos-inline-wrapper><span>foo</span></neos-inline-wrapper>', '<span>foo</span>');
})

test('cleanup multiple <neos-inline-wrapper>', () => {
assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper><neos-inline-wrapper>bar</neos-inline-wrapper>', '<span>foo</span><span>bar</span>');

assertCleanedUpContent('<neos-inline-wrapper>foo</neos-inline-wrapper><neos-inline-wrapper>bar</neos-inline-wrapper>', '<span>foo</span><span>bar</span>');
})

test('cleanup <neos-inline-wrapper> after other root', () => {
// in the case you had multiple paragraphs and a headline and switched to autoparagrahp: false
assertCleanedUpContent('<h1>foo</h1><neos-inline-wrapper>bar</neos-inline-wrapper>', '<h1>foo</h1><span>bar</span>');
})
})
10 changes: 8 additions & 2 deletions packages/neos-ui-ckeditor5-bindings/src/plugins/inlineMode.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import {cleanupNeosInlineWrapper} from './cleanupNeosInlineWrapper';

/**
* HACK, since there is yet no native support
Expand All @@ -14,8 +15,7 @@ export default class InlineMode extends Plugin {
// we map paragraph model into plain <span> element in edit mode
editor.conversion.for('editingDowncast').elementToElement({model: 'paragraph', view: 'span', converterPriority: 'high'});

// to avoid having a wrapping "span" tag, we will convert the outmost 'paragraph' and strip the custom tag 'neos-inline-wrapper'
// in a hacky cleanup in cleanupContentBeforeCommit
// to avoid having a wrapping "span" tag, we will convert the outmost 'paragraph' ...
// see https://neos-project.slack.com/archives/C07QEQ1U2/p1687952441254759 - i could find a better solution
editor.conversion.for('dataDowncast').elementToElement({model: 'paragraph', view: ( modelElement, viewWriter ) => {
const parentIsRoot = modelElement.parent.is('$root');
Expand All @@ -26,6 +26,12 @@ export default class InlineMode extends Plugin {
return viewWriter.createContainerElement('neos-inline-wrapper');
}, converterPriority: 'high'});

// ... and strip the custom tag 'neos-inline-wrapper' in a hacky cleanup in cleanupData
editor.data.decorate('get');
editor.data.on('get', (event) => {
event.return = cleanupNeosInlineWrapper(event.return)
});

// we redefine enter key to create soft breaks (<br>) instead of new paragraphs
editor.editing.view.document.on('enter', (evt, data) => {
editor.execute('shiftEnter');
Expand Down
75 changes: 0 additions & 75 deletions packages/neos-ui-ckeditor5-bindings/src/plugins/neosPlaceholder.js

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./dist/index.js" defer></script>
<link rel="stylesheet" href="./dist/index.css">
<title>CKEditor Manual Test</title>
</head>
<body>
Expand Down

0 comments on commit bb7dd82

Please sign in to comment.