Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chat: should send message on enter key if some text is entered #28003

Merged
merged 12 commits into from
Sep 9, 2024
23 changes: 21 additions & 2 deletions packages/devextreme/js/__internal/ui/chat/chat_message_box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { Properties as DOMComponentProperties } from '@ts/core/widget/dom_c
import DOMComponent from '@ts/core/widget/dom_component';
import type { OptionChanged } from '@ts/core/widget/types';

import type { EnterKeyEvent } from '../../../ui/text_area';
import type dxTextArea from '../../../ui/text_area';
import TextArea from '../m_text_area';

Expand Down Expand Up @@ -59,6 +60,12 @@ class MessageBox extends DOMComponent<MessageBox, Properties> {
this._renderButton();
}

_isValuableTextEntered(): boolean {
marker-dao marked this conversation as resolved.
Show resolved Hide resolved
const { text } = this._textArea.option();

return !!text?.trim();
}

_renderTextArea(): void {
const {
activeStateEnabled,
Expand All @@ -77,7 +84,19 @@ class MessageBox extends DOMComponent<MessageBox, Properties> {
stylingMode: 'outlined',
placeholder: 'Type a message',
autoResizeEnabled: true,
valueChangeEvent: 'input',
maxHeight: '20em',
onEnterKey: (e: EnterKeyEvent): void => {
if (!e.event?.shiftKey) {
this._sendHandler(e);
}
},
});

this._textArea.registerKeyHandler('enter', (event: KeyboardEvent) => {
if (!event.shiftKey && this._isValuableTextEntered()) {
event.preventDefault();
ksercs marked this conversation as resolved.
Show resolved Hide resolved
}
});
}

Expand Down Expand Up @@ -112,10 +131,10 @@ class MessageBox extends DOMComponent<MessageBox, Properties> {
);
}

_sendHandler(e: ClickEvent): void {
_sendHandler(e: ClickEvent | EnterKeyEvent): void {
const { text } = this._textArea.option();

if (!text?.trim()) {
if (!this._isValuableTextEntered()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ QUnit.module('MessageBox', moduleConfig, () => {
placeholder: 'Type a message',
autoResizeEnabled: true,
maxHeight: '20em',
valueChangeEvent: 'input'
};

const textArea = TextArea.getInstance(this.$textArea);
Expand All @@ -82,7 +83,6 @@ QUnit.module('MessageBox', moduleConfig, () => {
this.$sendButton.trigger('dxclick');

assert.strictEqual(this.$input.val(), '');
assert.strictEqual(this.$input.val(), '');
});

QUnit.test('textarea should be cleared when the send button is clicked if the input contains a value consisting only of spaces', function(assert) {
Expand Down Expand Up @@ -113,6 +113,20 @@ QUnit.module('MessageBox', moduleConfig, () => {
assert.strictEqual(onMessageSendStub.callCount, 1);
});

QUnit.test('should be fired on enter key if the textarea input contains a value', function(assert) {
const onMessageSendStub = sinon.stub();

this.reinit({ onMessageSend: onMessageSendStub });

keyboardMock(this.$input)
.focus()
.type('new text message')
.keyUp('enter');


assert.strictEqual(onMessageSendStub.callCount, 1);
});

QUnit.test('should not be fired when the send button is clicked if the textarea input does not contain a value', function(assert) {
const onMessageSendStub = sinon.stub();

Expand All @@ -123,6 +137,25 @@ QUnit.module('MessageBox', moduleConfig, () => {
assert.strictEqual(onMessageSendStub.callCount, 0);
});

QUnit.test('should not be fired on enter key if the textarea input does not contain a value (excluding spaces)', function(assert) {
ksercs marked this conversation as resolved.
Show resolved Hide resolved
EugeniyKiyashko marked this conversation as resolved.
Show resolved Hide resolved
const onMessageSendStub = sinon.stub();

this.reinit({ onMessageSend: onMessageSendStub });

keyboardMock(this.$input)
.focus()
.keyUp('enter');

assert.strictEqual(onMessageSendStub.callCount, 0);

keyboardMock(this.$input)
.focus()
.type(' ')
.keyUp('enter');

assert.strictEqual(onMessageSendStub.callCount, 0);
});

QUnit.test('should be possible to update it at runtime', function(assert) {
const eventHandlerStub = sinon.stub();

Expand Down Expand Up @@ -176,6 +209,31 @@ QUnit.module('MessageBox', moduleConfig, () => {

this.$sendButton.trigger('dxclick');
});

QUnit.test('should be fired with correct arguments when enter is pressed', function(assert) {
assert.expect(6);

const text = ' new text message ';

this.reinit({
onMessageSend: (e) => {
const { component, element, event, text } = e;

assert.strictEqual(component, this.instance, 'component field is correct');
assert.strictEqual(isRenderer(element), !!config().useJQuery, 'element is correct');
assert.strictEqual($(element).is(this.$element), true, 'element field is correct');
assert.strictEqual(event.type, 'keyup', 'e.event.type is correct');
assert.strictEqual(event.target, this.$input.get(0), 'event target is correct');
assert.strictEqual(text, text, 'message field is correct');
},
});

keyboardMock(this.$input)
.focus()
.type(text)
.keyDown('enter')
.keyUp('enter');
});
});

QUnit.module('Proxy state options', () => {
Expand Down Expand Up @@ -217,4 +275,67 @@ QUnit.module('MessageBox', moduleConfig, () => {
});
});
});

QUnit.module('Keyboard navigation', () => {
QUnit.test('textarea should not be cleared on enter key if the input contains a value consisting only of spaces', function(assert) {
keyboardMock(this.$input)
.focus()
.type(' ')
.keyDown('enter')
.keyUp('enter');

assert.strictEqual(this.$input.val(), ' ');
});

QUnit.test('textarea should be cleared on enter key when some text is entered', function(assert) {
keyboardMock(this.$input)
.focus()
.type('some text')
.keyDown('enter')
.keyUp('enter');

assert.strictEqual(this.$input.val(), '');
});

QUnit.test('enter keydown event should be prevented if input text has non-space characters', function(assert) {
const enterKeyDownEvent = $.Event('keydown', { key: 'enter' });

keyboardMock(this.$input).type('1');

this.$input.trigger(enterKeyDownEvent);

assert.ok(enterKeyDownEvent.isDefaultPrevented(), 'empty line is not added before sending');
});

QUnit.test('enter keydown event with Shift modificator should not be prevented', function(assert) {
const enterKeyDownEvent = $.Event('keydown', { key: 'enter', shiftKey: true });

keyboardMock(this.$input).focus().type('1');

this.$input.trigger(enterKeyDownEvent);

assert.notOk(enterKeyDownEvent.isDefaultPrevented(), 'empty line is added when shift is used');
});

QUnit.test('enter keydown event should not be prevented if input text consists only from space characters', function(assert) {
const enterKeyDownEvent = $.Event('keydown', { key: 'enter' });

keyboardMock(this.$input).type(' \n \n');

this.$input.trigger(enterKeyDownEvent);

assert.notOk(enterKeyDownEvent.isDefaultPrevented(), 'empty line is added');
});

QUnit.test('textarea should restore its height after enter press when multiline text was entered', function(assert) {
const initialTextAreaHeight = this.$textArea.height();

keyboardMock(this.$input)
.type('1\n2\n3')
.keyDown('enter')
.keyUp('enter');

assert.roughEqual(this.$textArea.height(), initialTextAreaHeight, 0.1, 'textarea height is restored');
});
});
});
Loading