Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is an issue with verical centering in generic

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions e2e/testcafe-devextreme/tests/chat/messageList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,43 @@ test('Messagelist options showDayHeaders, showUserName and showMessageTimestamp
});
});

test('Message list with editing context menu', async (t) => {
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
const chat = new Chat('#container');

await t
.rightClick(chat.getMessage(2))
.pressKey('down')
.pressKey('down');

await testScreenshot(t, takeScreenshot, 'Messagelist with editing context menu.png', { element: '#container' });

await t
.expect(compareResults.isValid())
.ok(compareResults.errorMessages());
}).before(async () => {
const userFirst = createUser(1, 'First');
const userSecond = createUser(2, 'Second');

const items = [
{ author: userFirst, text: 'AAA' },
{ author: userFirst, text: 'BBB' },
{ author: userSecond, text: 'CCC' },
];

return createWidget('dxChat', {
items,
editing: {
allowUpdating: true,
allowDeleting: true,
},
user: userSecond,
width: 400,
height: 600,
showDayHeaders: false,
});
});

fixture`ChatMessageList: dayHeaders`
.page(url(__dirname, '../container.html'));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
$day-header-font-size,
) {
.dx-chat-messagelist {
.dx-scrollable-content {
padding-inline: $padding;
> .dx-scrollable {
> .dx-scrollable-wrapper {
> .dx-scrollable-container {
> .dx-scrollable-content {
padding-inline: $padding;
}
}
}
}
}

Expand Down Expand Up @@ -63,3 +69,20 @@
color: $messagelist-empty-prompt-color;
}
}

@mixin chat-messagelist-contextmenu(
$delete-button-color,
$delete-button-focused-color,
$delete-button-focused-bg,
) {
.dx-messagelist-context-menu-content {
.dx-menu-item:has(.dx-icon-trash) {
color: $delete-button-color;

&.dx-state-focused {
color: $delete-button-focused-color;
background-color: $delete-button-focused-bg;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,7 @@ $chat-typingindicator-circle-bg-color: null !default;
* $type color
*/
$chat-typingindicator-bubble-bg-color: $chat-bubble-background-color-secondary !default;

$chat-messagelist-contextmenu-delete-button-color: $base-danger !default;
$chat-messagelist-contextmenu-delete-button-focused-color: $base-danger !default;
$chat-messagelist-contextmenu-delete-button-focused-bg: $base-hover-bg !default;
5 changes: 5 additions & 0 deletions packages/devextreme-scss/scss/widgets/fluent/chat/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
$chat-messagelist-day-header-first-padding-top,
$chat-messagelist-day-header-font-size,
);
@include chat-messagelist-contextmenu(
$chat-messagelist-contextmenu-delete-button-color,
$chat-messagelist-contextmenu-delete-button-focused-color,
$chat-messagelist-contextmenu-delete-button-focused-bg,
);
@include chat-typingindicator(
$chat-typingindicator-template,
$chat-typingindicator-padding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,7 @@ $chat-typingindicator-circle-bg-color: null !default;
* $type color
*/
$chat-typingindicator-bubble-bg-color: $chat-bubble-background-color-secondary !default;

$chat-messagelist-contextmenu-delete-button-color: $base-danger !default;
$chat-messagelist-contextmenu-delete-button-focused-color: $base-inverted-text-color !default;
$chat-messagelist-contextmenu-delete-button-focused-bg: $base-danger !default;
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
$chat-messagelist-day-header-first-padding-top,
$chat-messagelist-day-header-font-size,
);
@include chat-messagelist-contextmenu(
$chat-messagelist-contextmenu-delete-button-color,
$chat-messagelist-contextmenu-delete-button-focused-color,
$chat-messagelist-contextmenu-delete-button-focused-bg,
);
@include chat-typingindicator(
$chat-typingindicator-template,
$chat-typingindicator-padding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ $chat-typingindicator-circle-bg-color: rgba($base-inverted-bg, 0.4) !default;
@else if $mode == "dark" {
$chat-bubble-background-color-primary: rgba(lighten($base-accent, 19.22), 0.08) !default;
}

$chat-messagelist-contextmenu-delete-button-color: $base-danger !default;
$chat-messagelist-contextmenu-delete-button-focused-color: $base-danger !default;
$chat-messagelist-contextmenu-delete-button-focused-bg: $base-hover-bg !default;
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
$chat-messagelist-day-header-first-padding-top,
$chat-messagelist-day-header-font-size,
);
@include chat-messagelist-contextmenu(
$chat-messagelist-contextmenu-delete-button-color,
$chat-messagelist-contextmenu-delete-button-focused-color,
$chat-messagelist-contextmenu-delete-button-focused-bg,
);
@include chat-typingindicator(
$chat-typingindicator-template,
$chat-typingindicator-padding,
Expand Down
116 changes: 112 additions & 4 deletions packages/devextreme/js/__internal/ui/chat/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Guid } from '@js/common';
import type { AsyncCancelable, EventInfo } from '@js/common/core/events';
import messageLocalization from '@js/common/core/localization/message';
import type { DataSourceOptions } from '@js/common/data';
import registerComponent from '@js/core/component_registrator';
Expand All @@ -22,14 +23,40 @@ import type {
TypingStartEvent as MessageBoxTypingStartEvent,
} from '@ts/ui/chat/messagebox';
import MessageBox from '@ts/ui/chat/messagebox';
import type { MessageTemplate, Properties as MessageListProperties } from '@ts/ui/chat/messagelist';
import type {
MessageEditingEvent,
MessageTemplate,
Properties as MessageListProperties,
} from '@ts/ui/chat/messagelist';
import MessageList from '@ts/ui/chat/messagelist';
import type { DataChange } from '@ts/ui/collection/collection_widget.base';

const CHAT_CLASS = 'dx-chat';
const TEXTEDITOR_INPUT_CLASS = 'dx-texteditor-input';

class Chat extends Widget<Properties> {
export type MessageDeletingEvent = AsyncCancelable & EventInfo<Chat> & {
readonly message: Message;
};

export type MessageEditingStartEvent = AsyncCancelable & EventInfo<Chat> & {
readonly message: Message;
};
export interface ChatProperties extends Properties {
editing: {
allowUpdating: boolean | ((options: {
component: Chat;
message: Message;
}) => boolean);
allowDeleting: boolean | ((options: {
component: Chat;
message: Message;
}) => boolean);
};
onMessageEditingStart?: (e: MessageEditingStartEvent) => void;
onMessageDeleting?: (e: MessageEditingStartEvent) => void;
}

class Chat extends Widget<ChatProperties> {
_messageBox!: MessageBox;

_messageList!: MessageList;
Expand All @@ -42,11 +69,19 @@ class Chat extends Widget<Properties> {

_typingEndAction?: (e: Partial<TypingEndEvent>) => void;

_getDefaultOptions(): Properties {
_messageEditingStartAction?: (e: Partial<MessageEnteredEvent>) => void;

_messageDeletingAction?: (e: Partial<MessageEnteredEvent>) => void;

_getDefaultOptions(): ChatProperties {
return {
...super._getDefaultOptions(),
showDayHeaders: true,
activeStateEnabled: true,
editing: {
allowUpdating: false,
allowDeleting: false,
},
focusStateEnabled: true,
hoverStateEnabled: true,
items: [],
Expand Down Expand Up @@ -76,6 +111,8 @@ class Chat extends Widget<Properties> {
this._refreshDataSource();

this._createMessageEnteredAction();
this._createMessageEditingStartAction();
this._createMessageDeletingAction();
this._createTypingStartAction();
this._createTypingEndAction();
}
Expand Down Expand Up @@ -153,6 +190,8 @@ class Chat extends Widget<Properties> {
const options: MessageListProperties = {
items,
currentUserId,
allowUpdating: (message: Message): boolean => this._allowEditAction(message),
allowDeleting: (message: Message): boolean => this._allowDeleteAction(message),
messageTemplate: this._getMessageTemplate(),
showDayHeaders,
showAvatar,
Expand All @@ -162,11 +201,46 @@ class Chat extends Widget<Properties> {
messageTimestampFormat,
typingUsers,
isLoading,
onMessageEditingStart: (e) => {
this._messageEditingStartHandler(e);
},
onMessageDeleting: (e) => {
this._messageDeletingHandler(e);
},
onKeyHandled: () => {
this.focus();
},
};

return options;
}

protected _allowEditAction(message: Message): boolean {
const { editing } = this.option();
const { allowUpdating } = editing;

if (typeof allowUpdating === 'function') {
return allowUpdating({
component: this,
message,
});
}
return allowUpdating;
}

protected _allowDeleteAction(message: Message): boolean {
const { editing } = this.option();
const { allowDeleting } = editing;

if (typeof allowDeleting === 'function') {
return allowDeleting({
component: this,
message,
});
}
return allowDeleting;
}

_getMessageTemplate(): MessageTemplate {
const { messageTemplate } = this.option();
if (messageTemplate) {
Expand All @@ -186,6 +260,18 @@ class Chat extends Widget<Properties> {
return null;
}

_messageEditingStartHandler(e: MessageEditingEvent): void {
const { message, event } = e;

this._messageEditingStartAction?.({ message, event });
}

_messageDeletingHandler(e: MessageEditingEvent): void {
const { message, event } = e;

this._messageDeletingAction?.({ message, event });
}

_renderAlertList(): void {
const $errors = $('<div>');

Expand Down Expand Up @@ -249,6 +335,20 @@ class Chat extends Widget<Properties> {
);
}

_createMessageEditingStartAction(): void {
this._messageEditingStartAction = this._createActionByOption(
'onMessageEditingStart',
{ excludeValidators: ['disabled'] },
);
}

_createMessageDeletingAction(): void {
this._messageDeletingAction = this._createActionByOption(
'onMessageDeleting',
{ excludeValidators: ['disabled'] },
);
}

_createTypingStartAction(): void {
this._typingStartAction = this._createActionByOption(
'onTypingStart',
Expand Down Expand Up @@ -308,7 +408,7 @@ class Chat extends Widget<Properties> {
return $input;
}

_optionChanged(args: OptionChanged<Properties>): void {
_optionChanged(args: OptionChanged<ChatProperties>): void {
const { name, value } = args;

switch (name) {
Expand All @@ -323,6 +423,8 @@ class Chat extends Widget<Properties> {
this._messageList.option('currentUserId', author?.id);
break;
}
case 'editing':
break;
case 'items':
this._messageList.option(name, value);
this._updateMessageBoxAria();
Expand All @@ -337,6 +439,12 @@ class Chat extends Widget<Properties> {
case 'onMessageEntered':
this._createMessageEnteredAction();
break;
case 'onMessageEditingStart':
this._createMessageEditingStartAction();
break;
case 'onMessageDeleting':
this._createMessageDeletingAction();
break;
case 'onTypingStart':
this._createTypingStartAction();
break;
Expand Down
Loading
Loading