Skip to content

Commit f4fb967

Browse files
authored
HtmlEditor: Move the execution abort to Popup.onHiding && Skip execution in onValueChanged
1 parent 8af0af8 commit f4fb967

File tree

6 files changed

+83
-12
lines changed

6 files changed

+83
-12
lines changed

packages/devextreme/js/__internal/ui/html_editor/ui/aiDialog.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
103103

104104
private _commandChangeSuppressed = false;
105105

106+
private _commandOptionSuppressed = false;
107+
106108
private _commandOptionsList?: string[];
107109

108110
private _commandSelectBox!: dxSelectBox;
@@ -163,7 +165,10 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
163165
at: 'center',
164166
of: this._$container,
165167
},
166-
...this._popupUserConfig,
168+
onHiding: () => {
169+
this._processCommandCompletion();
170+
},
171+
...this._popupConfig,
167172
}) as PopupProperties;
168173
}
169174

@@ -199,12 +204,17 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
199204

200205
protected _renderOptionSelectBox($container: dxElementWrapper): void {
201206
const $optionSelectBox = $('<div>').appendTo($container);
207+
202208
this._optionSelectBox = new SelectBox($optionSelectBox.get(0), {
203209
items: this._commandOptionsList,
204210
value: this._currentOption ?? this._commandOptionsList?.[0],
205211
visible: this._isCommandWithOptionsSelected(),
206212
onInitialized: this._addEscapeHandler.bind(this),
207213
onValueChanged: ({ value }): void => {
214+
if (this._commandOptionSuppressed) {
215+
return;
216+
}
217+
208218
this._currentOption = value;
209219

210220
if (this._isOpen() && value) {
@@ -524,6 +534,7 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
524534
}
525535

526536
private _processCommandCompletion(dialogState?: DialogState): void {
537+
this._abort?.();
527538
this._abort = undefined;
528539
this._isAICommandExecuting = false;
529540

@@ -568,7 +579,6 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
568579
}
569580

570581
private _stopAICommandExecution(): void {
571-
this._abort?.();
572582
this._processCommandCompletion(this._getInitialDialogState());
573583
}
574584

@@ -599,12 +609,14 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
599609
private _refreshOptionSelectBox(): void {
600610
const hasOptions = this._isCommandWithOptionsSelected();
601611

612+
this._commandOptionSuppressed = true;
602613
this._optionSelectBox.option({
603614
disabled: this._isAICommandExecuting,
604615
visible: hasOptions,
605616
items: this._commandOptionsList ?? [],
606617
value: this._currentOption ?? this._commandOptionsList?.[0],
607618
});
619+
this._commandOptionSuppressed = false;
608620
}
609621

610622
private _setTextAreasInitialState(): void {
@@ -734,7 +746,6 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
734746
}
735747

736748
updateAIIntegration(aiIntegration: AIIntegration): void {
737-
this._abort?.();
738749
this._processCommandCompletion(this._getInitialDialogState());
739750
this._aiIntegration = aiIntegration;
740751
this._executeAICommand();
@@ -765,10 +776,6 @@ export default class AIDialog extends BaseDialog<AIDialogResult> {
765776

766777
hide(resultText: AIDialogResult['resultText'], event: AIDialogResult['event']): void {
767778
this.deferred?.resolve({ resultText, event });
768-
769-
this._abort?.();
770-
this._processCommandCompletion();
771-
772779
super.hide();
773780
}
774781
}

packages/devextreme/js/__internal/ui/html_editor/ui/formDialog.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export default class FormDialog extends BaseDialog {
107107
},
108108
},
109109
],
110-
...this._popupUserConfig,
110+
...this._popupConfig,
111111
}) as PopupProperties;
112112
}
113113

packages/devextreme/js/__internal/ui/html_editor/ui/m_baseDialog.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ const DROPDOWN_EDITOR_OVERLAY_CLASS = 'dx-dropdowneditor-overlay';
1111
abstract class BaseDialog<T = unknown> {
1212
_$container: dxElementWrapper;
1313

14-
_popupUserConfig?: PopupProperties;
14+
_popupConfig?: PopupProperties;
1515

1616
_popup!: Popup;
1717

1818
deferred?: DeferredObj<T>;
1919

2020
constructor($container: dxElementWrapper, popupConfig?: PopupProperties) {
2121
this._$container = $container;
22-
this._popupUserConfig = popupConfig;
22+
this._popupConfig = popupConfig;
2323

2424
this._renderPopup();
2525
}

packages/devextreme/js/__internal/ui/popup/m_popup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const DISABLED_STATE_CLASS = 'dx-state-disabled';
5858
const POPUP_DRAGGABLE_CLASS = 'dx-popup-draggable';
5959

6060
const POPUP_TITLE_CLASS = 'dx-popup-title';
61-
const POPUP_TITLE_CLOSEBUTTON_CLASS = 'dx-closebutton';
61+
export const POPUP_TITLE_CLOSEBUTTON_CLASS = 'dx-closebutton';
6262

6363
const POPUP_BOTTOM_CLASS = 'dx-popup-bottom';
6464

packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/aiDialog.tests.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,19 @@ QUnit.module('AIDialog', () => {
868868

869869
this.resolve('');
870870
});
871+
872+
QUnit.test('changing the dialog state does not trigger additional command execution if the command has options', function(assert) {
873+
this.showDialog({ currentCommand: 'changeStyle', currentCommandOption: 'business' });
874+
const commandSelectBox = getCommandSelectBoxInstance(this.$element);
875+
876+
assert.ok(this.sendRequestStub.calledOnce, 'command execution is called once');
877+
878+
commandSelectBox.option('value', 'askAI');
879+
assert.ok(this.sendRequestStub.calledOnce, 'command execution is called once');
880+
881+
commandSelectBox.option('value', 'changeStyle');
882+
assert.strictEqual(this.sendRequestStub.callCount, 2, 'command execution is called twice');
883+
});
871884
});
872885

873886
QUnit.module('UI interactivity during generation', integrationModuleConfig, () => {

packages/devextreme/testing/tests/DevExpress.ui.widgets.htmlEditor/htmlEditorParts/aiDialogIntegration.tests.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import {
1414
getResultTextAreaValue,
1515
} from '../../../helpers/aiDialog.js';
1616
import { AI_DIALOG_CLASS } from '__internal/ui/html_editor/ui/aiDialog';
17+
import { POPUP_TITLE_CLOSEBUTTON_CLASS } from '__internal/ui/popup/m_popup';
1718
import { DX_MENU_ITEM_CLASS } from '__internal/ui/menu/m_menu';
1819
import uiErrors from 'ui/widget/ui.errors';
20+
import keyboardMock from '../../../helpers/keyboardMock.js';
1921

2022
const MENU_ITEM_CLASS = 'dx-menu-item';
2123
const MENU_CLASS = 'dx-menu';
@@ -28,7 +30,7 @@ const setupHtmlEditorWithAi = (config) => {
2830
toolbar: {
2931
items: [{
3032
name: 'ai',
31-
commands: ['summarize']
33+
commands: ['summarize'],
3234
}],
3335
},
3436
...config
@@ -43,6 +45,18 @@ const getAIDialogElement = ($htmlEditor) => {
4345
return $htmlEditor.find(`.${AI_DIALOG_CLASS}`);
4446
};
4547

48+
const getPopup = (htmlEditor) => {
49+
return getAIDialog(htmlEditor)._popup;
50+
};
51+
52+
const getOverlayContentElement = (htmlEditor) => {
53+
return $(getPopup(htmlEditor).content()).parent();
54+
};
55+
56+
const getCloseButtonElement = (htmlEditor) => {
57+
return getOverlayContentElement(htmlEditor).find(`.${POPUP_TITLE_CLOSEBUTTON_CLASS}`);
58+
};
59+
4660
QUnit.module('AI dialog integration', () => {
4761
QUnit.module('toolbar', () => {
4862
QUnit.test('Should pass correct payload to dialog on item click', function(assert) {
@@ -507,4 +521,41 @@ QUnit.module('AI dialog integration', () => {
507521
assert.strictEqual($menuItem.attr('aria-label'), 'AI Assistant toolbar item', 'menu item has correct aria-label');
508522
});
509523
});
524+
525+
QUnit.module('command execution abort on hiding', {
526+
beforeEach() {
527+
this.abortSpy = sinon.spy();
528+
this.commandSpy = sinon.stub().returns(this.abortSpy);
529+
const aiIntegration = { summarize: this.commandSpy };
530+
this.htmlEditor = setupHtmlEditorWithAi({ aiIntegration });
531+
},
532+
afterEach() {
533+
sinon.restore();
534+
},
535+
}, () => {
536+
[
537+
{
538+
name: 'click outside',
539+
action() { $(document).trigger('dxpointerdown'); },
540+
},
541+
{
542+
name: 'click on the close button',
543+
action() { getCloseButtonElement(this.htmlEditor).trigger('dxclick'); },
544+
},
545+
{
546+
name: 'esc keydown',
547+
action() { keyboardMock(getOverlayContentElement(this.htmlEditor)).press('escape'); },
548+
},
549+
].forEach(({ name, action }) => {
550+
QUnit.test(`should abort on ${name}`, function(assert) {
551+
openAIDialog($(this.htmlEditor.$element()));
552+
553+
assert.ok(this.commandSpy.calledOnce, 'command execution is started');
554+
555+
action.call(this);
556+
557+
assert.ok(this.abortSpy.calledOnce, 'abort function is called once');
558+
});
559+
});
560+
});
510561
});

0 commit comments

Comments
 (0)