diff --git a/packages/devextreme/js/__internal/ui/html_editor/modules/m_toolbar.ts b/packages/devextreme/js/__internal/ui/html_editor/modules/m_toolbar.ts index 5db29446d347..16502069da47 100644 --- a/packages/devextreme/js/__internal/ui/html_editor/modules/m_toolbar.ts +++ b/packages/devextreme/js/__internal/ui/html_editor/modules/m_toolbar.ts @@ -120,13 +120,16 @@ if (Quill) { } this.quill.on('editor-change', (eventName, newValue, oldValue, eventSource) => { - const isSilentMode = eventSource === SILENT_ACTION && isEmptyObject(this.quill.getFormat()); + const isSilentMode = eventSource === SILENT_ACTION + && isEmptyObject(this.quill.getFormat()); if (!isSilentMode) { const isSelectionChanged = eventName === SELECTION_CHANGE_EVENT; this._updateToolbar(isSelectionChanged); } + + this._updateHeaderFormatWidget(); }); } } @@ -535,6 +538,18 @@ if (Quill) { this._toggleClearFormatting(hasFormats || selection.length > 1); } + _updateHeaderFormatWidget() { + const selection = this.quill.getSelection(); + const formatName = 'header'; + const formatWidget = this._toolbarWidgets.getByName(formatName); + const formats = this.quill.getFormat(selection); + if (!selection || !formatWidget) { + return; + } + + this._markActiveFormatWidget(formatName, formatWidget, formats); + } + _markActiveFormatWidget(name, widget, formats) { if (this._isColorFormat(name)) { this._updateColorWidget(name, formats[name]); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/toolbarIntegration.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/toolbarIntegration.tests.js index 3ae5a169884e..6d0e1fb72c61 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/toolbarIntegration.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/toolbarIntegration.tests.js @@ -3,6 +3,7 @@ import $ from 'jquery'; import 'ui/html_editor'; import fx from 'common/core/animation/fx'; +import nativePointerMock from '../../../helpers/nativePointerMock.js'; import { checkLink, prepareEmbedValue, prepareTableValue } from './utils.js'; const TOOLBAR_CLASS = 'dx-htmleditor-toolbar'; @@ -10,11 +11,13 @@ const TOOLBAR_WRAPPER_CLASS = 'dx-htmleditor-toolbar-wrapper'; const TOOLBAR_FORMAT_WIDGET_CLASS = 'dx-htmleditor-toolbar-format'; const TOOLBAR_MULTILINE_CLASS = 'dx-toolbar-multiline'; const TOOLBAR_FORMAT_BUTTON_ACTIVE_CLASS = 'dx-format-active'; +const HTML_EDITOR_CONTENT_CLASS = 'dx-htmleditor-content'; const DROPDOWNMENU_CLASS = 'dx-dropdownmenu-button'; const DROPDOWNEDITOR_ICON_CLASS = 'dx-dropdowneditor-icon'; const BUTTON_CONTENT_CLASS = 'dx-button-content'; const QUILL_CONTAINER_CLASS = 'dx-quill-container'; const STATE_DISABLED_CLASS = 'dx-state-disabled'; +const FORMAT_ACTIVE_CLASS = 'dx-format-active'; const HEX_FIELD_CLASS = 'dx-colorview-label-hex'; const INPUT_CLASS = 'dx-texteditor-input'; const DIALOG_CLASS = 'dx-formdialog'; @@ -25,6 +28,8 @@ const LIST_ITEM_CLASS = 'dx-list-item'; const BLACK_PIXEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYGWNgYmL6DwABFgEGpP/tHAAAAABJRU5ErkJggg=='; +const ENTER_KEY_CODE = 13; + const { test, module: testModule } = QUnit; function getToolbar($container) { @@ -146,6 +151,96 @@ export default function() { .trigger('dxclick'); }); + test('header format should be reset after moving to next line (T1315504)', function(assert) { + const $htmlEditor = $('#htmlEditor').dxHtmlEditor({ + value: '
test
', + toolbar: { + items: [{ + name: 'header', + acceptedValues: [false, 1, 2, 3, 4, 5], + options: { + opened: true + }, + }], + multiline: false + }, + }); + const htmlEditor = $htmlEditor.dxHtmlEditor('instance'); + const $formatWidget = $htmlEditor.find(`.${TOOLBAR_FORMAT_WIDGET_CLASS}`); + const $content = $htmlEditor.find(`.${HTML_EDITOR_CONTENT_CLASS}`); + htmlEditor.setSelection(4, 0); + + $(`.${LIST_ITEM_CLASS}`) + .last() + .trigger('dxclick'); + + + const value = $formatWidget.find(`.${INPUT_CLASS}`).val(); + assert.strictEqual(value, 'Heading 5', 'Header format is applied'); + + nativePointerMock().simulateEvent($content.get(0), 'keydown', { keyCode: ENTER_KEY_CODE }); + + const newValue = $formatWidget.find(`.${INPUT_CLASS}`).val(); + assert.strictEqual(newValue, 'Normal text', 'Header format is reset'); + }); + + test('font format should not be reset after moving to next line', function(assert) { + const $htmlEditor = $('#htmlEditor').dxHtmlEditor({ + value: 'test
', + toolbar: { + items: [{ + name: 'font', + acceptedValues: ['Arial', 'Courier New', 'Georgia'], + options: { + opened: true + }, + }], + multiline: false + }, + }); + const htmlEditor = $htmlEditor.dxHtmlEditor('instance'); + const $formatWidget = $htmlEditor.find(`.${TOOLBAR_FORMAT_WIDGET_CLASS}`); + const $content = $htmlEditor.find(`.${HTML_EDITOR_CONTENT_CLASS}`); + htmlEditor.setSelection(4, 0); + + $(`.${LIST_ITEM_CLASS}`) + .last() + .trigger('dxclick'); + + + const value = $formatWidget.find(`.${INPUT_CLASS}`).val(); + assert.strictEqual(value, 'Georgia', 'Font format is applied'); + + nativePointerMock().simulateEvent($content.get(0), 'keydown', { keyCode: ENTER_KEY_CODE }); + + const newValue = $formatWidget.find(`.${INPUT_CLASS}`).val(); + assert.strictEqual(newValue, 'Georgia', 'Font format is not reset'); + }); + + ['bold', 'italic', 'strike', 'underline'].forEach((formatName) => { + test(`${formatName} format should not be reset after moving to next line`, function(assert) { + const $htmlEditor = $('#htmlEditor').dxHtmlEditor({ + value: 'test
', + toolbar: { + items: [formatName], + multiline: false + }, + }); + const htmlEditor = $htmlEditor.dxHtmlEditor('instance'); + const $formatWidget = $htmlEditor.find(`.${TOOLBAR_FORMAT_WIDGET_CLASS}`); + const $content = $htmlEditor.find(`.${HTML_EDITOR_CONTENT_CLASS}`); + htmlEditor.setSelection(4, 0); + + $formatWidget.trigger('dxclick'); + + assert.strictEqual($formatWidget.hasClass(FORMAT_ACTIVE_CLASS), true, `${formatName} format is applied`); + + nativePointerMock().simulateEvent($content.get(0), 'keydown', { keyCode: ENTER_KEY_CODE }); + + assert.strictEqual($formatWidget.hasClass(FORMAT_ACTIVE_CLASS), true, `${formatName} format is not reset`); + }); + }); + test('adaptive menu should be hidden after selecting formatting', function(assert) { const done = assert.async(); const instance = $('#htmlEditor').dxHtmlEditor({