Skip to content

Commit b4fdeca

Browse files
committed
feat(chat): expose drag&drop events, cancelable attachment change event
1 parent 7f22954 commit b4fdeca

File tree

3 files changed

+80
-18
lines changed

3 files changed

+80
-18
lines changed

src/components/chat/chat-input.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { consume } from '@lit/context';
22
import { html, LitElement } from 'lit';
3-
import { query, state } from 'lit/decorators.js';
3+
import { property, query, state } from 'lit/decorators.js';
44
import IgcIconButtonComponent from '../button/icon-button.js';
55
import IgcChipComponent from '../chip/chip.js';
66
import { chatContext } from '../common/context.js';
@@ -55,12 +55,12 @@ export default class IgcChatInputComponent extends LitElement {
5555
@state()
5656
private inputValue = '';
5757

58-
@state()
59-
private attachments: IgcMessageAttachment[] = [];
60-
6158
@state()
6259
private dragClass = '';
6360

61+
@property({ attribute: false })
62+
public attachments: IgcMessageAttachment[] = [];
63+
6464
// Cache for accepted file types
6565
private _acceptedTypesCache: {
6666
extensions: Set<string>;
@@ -142,6 +142,9 @@ export default class IgcChatInputComponent extends LitElement {
142142
);
143143

144144
this.dragClass = hasValidFiles ? 'dragging' : '';
145+
146+
const dragEvent = new CustomEvent('drag-attachment');
147+
this.dispatchEvent(dragEvent);
145148
}
146149

147150
private handleDragOver(e: DragEvent) {
@@ -178,6 +181,9 @@ export default class IgcChatInputComponent extends LitElement {
178181

179182
const validFiles = files.filter((file) => this.isFileTypeAccepted(file));
180183

184+
const dropEvent = new CustomEvent('drop-attachment');
185+
this.dispatchEvent(dropEvent);
186+
181187
this.attachFiles(validFiles);
182188
}
183189

@@ -199,7 +205,6 @@ export default class IgcChatInputComponent extends LitElement {
199205

200206
this.dispatchEvent(messageEvent);
201207
this.inputValue = '';
202-
this.attachments = [];
203208

204209
if (this.textInputElement) {
205210
this.textInputElement.style.height = 'auto';
@@ -232,11 +237,10 @@ export default class IgcChatInputComponent extends LitElement {
232237
thumbnail: isImage ? URL.createObjectURL(file) : undefined,
233238
});
234239
});
235-
this.attachments = [...this.attachments, ...newAttachments];
240+
236241
const attachmentEvent = new CustomEvent('attachment-change', {
237-
detail: this.attachments,
242+
detail: [...this.attachments, ...newAttachments],
238243
});
239-
240244
this.dispatchEvent(attachmentEvent);
241245
}
242246

@@ -288,10 +292,8 @@ export default class IgcChatInputComponent extends LitElement {
288292
}
289293

290294
private removeAttachment(index: number) {
291-
this.attachments = this.attachments.filter((_, i) => i !== index);
292-
293295
const attachmentEvent = new CustomEvent('attachment-change', {
294-
detail: this.attachments,
296+
detail: this.attachments.filter((_, i) => i !== index),
295297
});
296298

297299
this.dispatchEvent(attachmentEvent);

src/components/chat/chat.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,7 +995,7 @@ describe('Chat', () => {
995995
detail: { isTyping: true },
996996
});
997997

998-
aTimeout(1000).then(() => {
998+
aTimeout(3000).then(() => {
999999
expect(eventSpy).calledWith('igcTypingChange', {
10001000
detail: { isTyping: false },
10011001
});
@@ -1039,5 +1039,47 @@ describe('Chat', () => {
10391039
});
10401040
}
10411041
});
1042+
1043+
it('can cancel `igcMessageCreated` event', async () => {
1044+
const inputArea = chat.shadowRoot?.querySelector('igc-chat-input');
1045+
const sendButton = inputArea?.shadowRoot?.querySelector(
1046+
'igc-icon-button[name="send-message"]'
1047+
);
1048+
const textArea = inputArea?.shadowRoot?.querySelector('igc-textarea');
1049+
1050+
chat.addEventListener('igcMessageCreated', (event) => {
1051+
event.preventDefault();
1052+
});
1053+
1054+
if (sendButton && textArea) {
1055+
textArea.setAttribute('value', 'Hello!');
1056+
textArea.dispatchEvent(new Event('input'));
1057+
await elementUpdated(chat);
1058+
simulateClick(sendButton);
1059+
await elementUpdated(chat);
1060+
await clock.tickAsync(500);
1061+
1062+
expect(chat.messages.length).to.equal(0);
1063+
}
1064+
});
1065+
1066+
it('can cancel `igcAttachmentChange` event', async () => {
1067+
const inputArea = chat.shadowRoot?.querySelector('igc-chat-input');
1068+
const fileInput = inputArea?.shadowRoot
1069+
?.querySelector('igc-file-input')
1070+
?.shadowRoot?.querySelector('input') as HTMLInputElement;
1071+
1072+
chat.addEventListener('igcAttachmentChange', (event) => {
1073+
event.preventDefault();
1074+
});
1075+
1076+
simulateFileUpload(fileInput, files);
1077+
await elementUpdated(chat);
1078+
aTimeout(500);
1079+
1080+
expect(
1081+
inputArea?.shadowRoot?.querySelectorAll('igc-chip').length
1082+
).to.equal(0);
1083+
});
10421084
});
10431085
});

src/components/chat/chat.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ContextProvider } from '@lit/context';
22
import { html, LitElement } from 'lit';
3-
import { property } from 'lit/decorators.js';
3+
import { property, state } from 'lit/decorators.js';
44
import IgcButtonComponent from '../button/button.js';
55
import { chatContext } from '../common/context.js';
66
import { watch } from '../common/decorators/watch.js';
@@ -20,9 +20,11 @@ export interface IgcChatComponentEventMap {
2020
igcMessageCreated: CustomEvent<IgcMessage>;
2121
igcAttachmentClick: CustomEvent<IgcMessageAttachment>;
2222
igcAttachmentChange: CustomEvent<IgcMessageAttachment>;
23+
igcAttachmentDrag: CustomEvent<any>;
24+
igcAttachmentDrop: CustomEvent<any>;
2325
igcTypingChange: CustomEvent<boolean>;
24-
igcInputFocus: CustomEvent<void>;
25-
igcInputBlur: CustomEvent<void>;
26+
igcInputFocus: CustomEvent<any>;
27+
igcInputBlur: CustomEvent<any>;
2628
igcInputChange: CustomEvent<string>;
2729
igcMessageCopied: CustomEvent<IgcMessage>;
2830
}
@@ -55,6 +57,10 @@ export default class IgcChatComponent extends EventEmitterMixin<
5557
context: chatContext,
5658
initialValue: this,
5759
});
60+
61+
@state()
62+
private inputAttachments: IgcMessageAttachment[] = [];
63+
5864
@property({ type: String, reflect: true, attribute: 'current-user-id' })
5965
public currentUserId = 'user';
6066

@@ -101,6 +107,16 @@ export default class IgcChatComponent extends EventEmitterMixin<
101107
this.emitEvent('igcAttachmentClick', { detail: attachmentArgs });
102108
}
103109

110+
private handleAttachmentChange(e: CustomEvent) {
111+
const allowed = this.emitEvent('igcAttachmentChange', {
112+
detail: e.detail,
113+
cancelable: true,
114+
});
115+
if (allowed) {
116+
this.inputAttachments = [...e.detail];
117+
}
118+
}
119+
104120
private addMessage(message: {
105121
id?: string;
106122
text: string;
@@ -122,6 +138,7 @@ export default class IgcChatComponent extends EventEmitterMixin<
122138

123139
if (allowed) {
124140
this.messages = [...this.messages, newMessage];
141+
this.inputAttachments = [];
125142
}
126143
}
127144

@@ -158,16 +175,17 @@ export default class IgcChatComponent extends EventEmitterMixin<
158175
</slot>
159176
</div>
160177
<igc-chat-input
178+
.attachments=${this.inputAttachments}
161179
@message-created=${this.handleSendMessage}
162180
@typing-change=${(e: CustomEvent) => {
163181
this.emitEvent('igcTypingChange', { detail: e.detail });
164182
}}
165183
@input-change=${(e: CustomEvent) => {
166184
this.emitEvent('igcInputChange', { detail: e.detail });
167185
}}
168-
@attachment-change=${(e: CustomEvent) => {
169-
this.emitEvent('igcAttachmentChange', { detail: e.detail });
170-
}}
186+
@attachment-change=${this.handleAttachmentChange}
187+
@drop-attachment=${() => this.emitEvent('igcAttachmentDrop')}
188+
@drag-attachment=${() => this.emitEvent('igcAttachmentDrag')}
171189
@focus-input=${() => {
172190
this.emitEvent('igcInputFocus');
173191
}}

0 commit comments

Comments
 (0)