Skip to content

Commit e614203

Browse files
committed
chore(*): JSDoc for chat components
1 parent 6735e65 commit e614203

File tree

7 files changed

+353
-33
lines changed

7 files changed

+353
-33
lines changed

src/components/chat/chat-message-list.ts

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,60 @@ import { styles } from './themes/message-list.base.css.js';
1616
import type { IgcMessage } from './types.js';
1717

1818
/**
19+
* A chat message list component that displays a list of chat messages grouped by date.
1920
*
2021
* @element igc-chat-message-list
2122
*
23+
* This component:
24+
* - Groups messages by date, labeling groups as "Today", "Yesterday", or the date string.
25+
* - Renders messages using the `<igc-chat-message>` component.
26+
* - Supports keyboard navigation between messages (Home, End, ArrowUp, ArrowDown).
27+
* - Manages focus highlighting on active messages.
28+
* - Automatically scrolls to the bottom when new messages arrive, unless auto-scroll is disabled.
29+
* - Displays a typing indicator if the chat state option `isComposing` is true.
30+
*
31+
* Accessibility:
32+
* - Uses ARIA roles and properties for grouping and active descendant management.
33+
* - Message items have role="option" for assistive technologies.
34+
*
2235
*/
2336
export default class IgcChatMessageListComponent extends LitElement {
37+
/** Tag name of the custom element. */
2438
public static readonly tagName = 'igc-chat-message-list';
2539

40+
/** Styles applied to the component */
2641
public static override styles = styles;
2742

43+
/**
44+
* Consumed chat state context providing messages, options, and user data.
45+
* @private
46+
*/
2847
@consume({ context: chatContext, subscribe: true })
2948
private _chatState?: ChatState;
3049

50+
/**
51+
* Currently active message id used for focus and keyboard navigation.
52+
* @private
53+
*/
3154
@state()
3255
private _activeMessageId = '';
3356

57+
/**
58+
* Registers this component and its dependencies.
59+
* Used internally for component setup.
60+
*/
3461
/* blazorSuppress */
3562
public static register() {
3663
registerComponent(IgcChatMessageListComponent, IgcChatMessageComponent);
3764
}
3865

66+
/**
67+
* Formats a date to a human-readable label.
68+
* Returns 'Today', 'Yesterday', or localized date string.
69+
* @param date - Date object to format
70+
* @returns formatted string
71+
* @private
72+
*/
3973
private formatDate(date: Date): string {
4074
const today = new Date();
4175
const yesterday = new Date(today);
@@ -56,6 +90,12 @@ export default class IgcChatMessageListComponent extends LitElement {
5690
});
5791
}
5892

93+
/**
94+
* Groups messages by their date labels.
95+
* @param messages - Array of messages to group
96+
* @returns Array of groups with date label and messages
97+
* @private
98+
*/
5999
private groupMessagesByDate(
60100
messages: IgcMessage[]
61101
): { date: string; messages: IgcMessage[] }[] {
@@ -75,6 +115,10 @@ export default class IgcChatMessageListComponent extends LitElement {
75115
}));
76116
}
77117

118+
/**
119+
* Scrolls the container to the bottom, typically called after new messages are rendered.
120+
* @private
121+
*/
78122
private scrollToBottom() {
79123
requestAnimationFrame(() => {
80124
const container = this.shadowRoot?.host as HTMLElement;
@@ -84,13 +128,23 @@ export default class IgcChatMessageListComponent extends LitElement {
84128
});
85129
}
86130

131+
/**
132+
* Scrolls the view to a specific message by id.
133+
* @param messageId - The id of the message to scroll to
134+
* @private
135+
*/
87136
private scrollToMessage(messageId: string) {
88137
const messageElement = this.shadowRoot?.querySelector(
89138
`#message-${messageId}`
90139
);
91140
messageElement?.scrollIntoView();
92141
}
93142

143+
/**
144+
* Handles focus entering the message list container.
145+
* Sets the active message to the last message for keyboard navigation.
146+
* @private
147+
*/
94148
private handleFocusIn() {
95149
if (!this._chatState?.messages || this._chatState.messages.length === 0) {
96150
return;
@@ -99,10 +153,21 @@ export default class IgcChatMessageListComponent extends LitElement {
99153
this._activeMessageId = lastMessage !== '' ? `message-${lastMessage}` : '';
100154
}
101155

156+
/**
157+
* Handles focus leaving the message list container.
158+
* Clears the active message.
159+
* @private
160+
*/
102161
private handleFocusOut() {
103162
this._activeMessageId = '';
104163
}
105164

165+
/**
166+
* Handles keyboard navigation within the message list.
167+
* Supports Home, End, ArrowUp, ArrowDown keys to move active message focus.
168+
* @param e KeyboardEvent from user input
169+
* @private
170+
*/
106171
private handleKeyDown(e: KeyboardEvent) {
107172
if (!this._chatState?.messages || this._chatState.messages.length === 0) {
108173
return;
@@ -134,25 +199,37 @@ export default class IgcChatMessageListComponent extends LitElement {
134199
}
135200
break;
136201
default:
137-
return; // Exit if the key is not one of the specified keys
202+
return; // Ignore other keys
138203
}
139204

140205
this._activeMessageId = `message-${activeMessageId}`;
141206
this.scrollToMessage(activeMessageId);
142207
}
143208

209+
/**
210+
* Lifecycle: called when the component updates.
211+
* Scrolls to bottom unless auto-scroll is disabled.
212+
*/
144213
protected override updated() {
145214
if (!this._chatState?.options?.disableAutoScroll) {
146215
this.scrollToBottom();
147216
}
148217
}
149218

219+
/**
220+
* Lifecycle: called after first render.
221+
* Scrolls to bottom unless auto-scroll is disabled.
222+
*/
150223
protected override firstUpdated() {
151224
if (!this._chatState?.options?.disableAutoScroll) {
152225
this.scrollToBottom();
153226
}
154227
}
155228

229+
/**
230+
* Renders the typing indicator template if composing.
231+
* @protected
232+
*/
156233
protected *renderLoadingTemplate() {
157234
yield html`${this._chatState?.options?.templates?.composingIndicatorTemplate
158235
? this._chatState.options.templates.composingIndicatorTemplate
@@ -163,6 +240,11 @@ export default class IgcChatMessageListComponent extends LitElement {
163240
</div>`}`;
164241
}
165242

243+
/**
244+
* Main render method.
245+
* Groups messages by date and renders each message.
246+
* Shows the typing indicator if applicable.
247+
*/
166248
protected override render() {
167249
const groupedMessages = this.groupMessagesByDate(
168250
this._chatState?.messages ?? []
@@ -174,10 +256,11 @@ export default class IgcChatMessageListComponent extends LitElement {
174256
aria-activedescendant=${this._activeMessageId}
175257
role="group"
176258
aria-label="Message list"
177-
tabindex='0'
259+
tabindex="0"
178260
@focusin=${this.handleFocusIn}
179261
@focusout=${this.handleFocusOut}
180-
@keydown=${this.handleKeyDown}></div>
262+
@keydown=${this.handleKeyDown}
263+
>
181264
<div part="message-list">
182265
${repeat(
183266
groupedMessages,
@@ -203,11 +286,9 @@ export default class IgcChatMessageListComponent extends LitElement {
203286
)}
204287
`
205288
)}
206-
${
207-
this._chatState?.options?.isComposing
208-
? this.renderLoadingTemplate()
209-
: nothing
210-
}
289+
${this._chatState?.options?.isComposing
290+
? this.renderLoadingTemplate()
291+
: nothing}
211292
</div>
212293
</div>
213294
`;

src/components/chat/chat-message.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,39 @@ import { styles as shared } from './themes/shared/chat-message.common.css.js';
1313
import type { IgcMessage } from './types.js';
1414

1515
/**
16+
* A chat message component for displaying individual messages in `<igc-chat>`.
1617
*
1718
* @element igc-chat-message
1819
*
20+
* This component renders a single chat message including:
21+
* - Message text (sanitized)
22+
* - Attachments (if any)
23+
* - Custom templates for message content and actions (if provided via chat options)
24+
*
25+
* It distinguishes sent messages from received messages by comparing
26+
* the message sender with the current user ID from chat state.
27+
*
28+
* The message text is sanitized with DOMPurify before rendering,
29+
* and can be rendered with a markdown renderer if provided.
1930
*/
2031
export default class IgcChatMessageComponent extends LitElement {
32+
/** Tag name of the custom element. */
2133
public static readonly tagName = 'igc-chat-message';
2234

35+
/** Styles applied to the component. */
2336
public static override styles = [styles, shared];
2437

38+
/**
39+
* Injected chat state context. Provides message data, user info, and options.
40+
* @private
41+
*/
2542
@consume({ context: chatContext, subscribe: true })
2643
private _chatState?: ChatState;
2744

45+
/**
46+
* Registers this component and its dependencies.
47+
* This is used internally to set up the component definitions.
48+
*/
2849
/* blazorSuppress */
2950
public static register() {
3051
registerComponent(
@@ -34,13 +55,28 @@ export default class IgcChatMessageComponent extends LitElement {
3455
);
3556
}
3657

58+
/**
59+
* The chat message to render.
60+
*/
3761
@property({ attribute: false })
3862
public message: IgcMessage | undefined;
3963

64+
/**
65+
* Sanitizes message text to prevent XSS or invalid HTML.
66+
* @param text The raw message text
67+
* @returns Sanitized text safe for HTML rendering
68+
* @private
69+
*/
4070
private sanitizeMessageText(text: string): string {
4171
return DOMPurify.sanitize(text);
4272
}
4373

74+
/**
75+
* Renders the chat message template.
76+
* - Applies 'sent' CSS class if the message sender matches current user.
77+
* - Uses markdown rendering if configured.
78+
* - Renders attachments and custom templates if provided.
79+
*/
4480
protected override render() {
4581
const containerClass = `message-container ${this.message?.sender === this._chatState?.currentUserId ? 'sent' : ''}`;
4682
const sanitizedMessageText = this.sanitizeMessageText(

0 commit comments

Comments
 (0)