@@ -16,26 +16,60 @@ import { styles } from './themes/message-list.base.css.js';
1616import 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 */
2336export 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 ` ;
0 commit comments