• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.8
/projects/igniteui-angular/chat/src/chat.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    CUSTOM_ELEMENTS_SCHEMA,
5
    Directive,
6
    effect,
7
    inject,
8
    input,
9
    OnInit,
10
    output,
11
    signal,
12
    TemplateRef,
13
    ViewContainerRef,
14
    OnDestroy,
15
    ViewRef,
16
    computed,
17
} from '@angular/core';
18
import {
19
    IgcChatComponent,
20
    type IgcChatMessageAttachment,
21
    type IgcChatMessage,
22
    type IgcChatOptions,
23
    type ChatRenderContext,
24
    type ChatRenderers,
25
    type ChatAttachmentRenderContext,
26
    type ChatInputRenderContext,
27
    type ChatMessageRenderContext,
28
    type IgcChatMessageReaction,
29
} from 'igniteui-webcomponents';
30

31
type ChatContextUnion =
32
    | ChatAttachmentRenderContext
33
    | ChatMessageRenderContext
34
    | ChatInputRenderContext
35
    | ChatRenderContext;
36

37
type ChatContextType<T extends ChatContextUnion> =
38
    T extends ChatAttachmentRenderContext
39
    ? IgcChatMessageAttachment
40
    : T extends ChatMessageRenderContext
41
    ? IgcChatMessage
42
    : T extends ChatInputRenderContext
43
    ? string
44
    : T extends ChatRenderContext
45
    ? { instance: IgcChatComponent }
46
    : never;
47

48
type ExtractChatContext<T> = T extends (ctx: infer R) => any ? R : never;
49

50
type ChatTemplatesContextMap = {
51
    [K in keyof ChatRenderers]: {
52
        $implicit: ChatContextType<
53
            ExtractChatContext<NonNullable<ChatRenderers[K]>> & ChatContextUnion
54
        >;
55
    };
56
};
57

58
/**
59
 * Template references for customizing chat component rendering.
60
 * Each property corresponds to a specific part of the chat UI that can be customized.
61
 *
62
 * @example
63
 * ```typescript
64
 * templates = {
65
 *   messageContent: this.customMessageTemplate,
66
 *   attachment: this.customAttachmentTemplate
67
 * }
68
 * ```
69
 */
70
export type IgxChatTemplates = {
71
    [K in keyof Omit<ChatRenderers, 'typingIndicator'>]?: TemplateRef<ChatTemplatesContextMap[K]>;
72
};
73

74
/**
75
 * Configuration options for the chat component.
76
 */
77
export type IgxChatOptions = Omit<IgcChatOptions, 'renderers'>;
78

79

80
/**
81
 * Angular wrapper component for the Ignite UI Web Components Chat component.
82
 *
83
 * This component provides an Angular-friendly interface to the igc-chat web component,
84
 * including support for Angular templates, signals, and change detection.
85
 *
86
 * Uses OnPush change detection strategy for optimal performance. All inputs are signals,
87
 * so changes are automatically tracked and propagated to the underlying web component.
88
 *
89
 * @example
90
 * ```typescript
91
 * <igx-chat
92
 *   [messages]="messages"
93
 *   [draftMessage]="draft"
94
 *   [options]="chatOptions"
95
 *   [templates]="chatTemplates"
96
 *   (messageCreated)="onMessageCreated($event)"
97
 * />
98
 * ```
99
 */
100
@Component({
101
    selector: 'igx-chat',
102
    standalone: true,
103
    changeDetection: ChangeDetectionStrategy.OnPush,
104
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
105
    templateUrl: './chat.component.html'
106
})
107
export class IgxChatComponent implements OnInit, OnDestroy {
3✔
108
    //#region Internal state
109

UNCOV
110
    private readonly _view = inject(ViewContainerRef);
×
UNCOV
111
    private readonly _templateViewRefs = new Map<TemplateRef<any>, Set<ViewRef>>();
×
UNCOV
112
    private _oldTemplates: IgxChatTemplates = {};
×
113

UNCOV
114
    protected readonly _transformedTemplates = signal<ChatRenderers>({});
×
115

UNCOV
116
    protected readonly _mergedOptions = computed<IgcChatOptions>(() => {
×
UNCOV
117
        const options = this.options();
×
UNCOV
118
        const transformedTemplates = this._transformedTemplates();
×
UNCOV
119
        return {
×
120
            ...options,
121
            renderers: transformedTemplates
122
        };
123
    });
124

125
    //#endregion
126

127
    //#region Inputs
128

129
    /** Array of chat messages to display */
UNCOV
130
    public readonly messages = input<IgcChatMessage[]>([]);
×
131

132
    /** Draft message with text and optional attachments */
UNCOV
133
    public readonly draftMessage = input<
×
134
        { text: string; attachments?: IgcChatMessageAttachment[] } | undefined
135
    >({ text: '' });
136

137
    /** Configuration options for the chat component */
UNCOV
138
    public readonly options = input<IgxChatOptions>({});
×
139

140
    /** Custom templates for rendering chat elements */
UNCOV
141
    public readonly templates = input<IgxChatTemplates>({});
×
142

143
    //#endregion
144

145
    //#region Outputs
146

147
    /** Emitted when a new message is created */
UNCOV
148
    public readonly messageCreated = output<IgcChatMessage>();
×
149

150
    /** Emitted when a user reacts to a message */
UNCOV
151
    public readonly messageReact = output<IgcChatMessageReaction>();
×
152

153
    /** Emitted when an attachment is clicked */
UNCOV
154
    public readonly attachmentClick = output<IgcChatMessageAttachment>();
×
155

156
    /** Emitted when attachment drag starts */
UNCOV
157
    public readonly attachmentDrag = output<void>();
×
158

159
    /** Emitted when attachment is dropped */
UNCOV
160
    public readonly attachmentDrop = output<void>();
×
161

162
    /** Emitted when typing indicator state changes */
UNCOV
163
    public readonly typingChange = output<boolean>();
×
164

165
    /** Emitted when the input receives focus */
UNCOV
166
    public readonly inputFocus = output<void>();
×
167

168
    /** Emitted when the input loses focus */
UNCOV
169
    public readonly inputBlur = output<void>();
×
170

171
    /** Emitted when the input value changes */
UNCOV
172
    public readonly inputChange = output<string>();
×
173

174
    //#endregion
175

176
    /** @internal */
177
    public ngOnInit(): void {
UNCOV
178
        IgcChatComponent.register();
×
179
    }
180

181
    /** @internal */
182
    public ngOnDestroy(): void {
UNCOV
183
        for (const viewSet of this._templateViewRefs.values()) {
×
UNCOV
184
            viewSet.forEach(viewRef => viewRef.destroy());
×
185
        }
UNCOV
186
        this._templateViewRefs.clear();
×
187
    }
188

189
    constructor() {
190
        // Templates changed - update transformed templates and viewRefs
UNCOV
191
        effect(() => {
×
UNCOV
192
            const templates = this.templates();
×
UNCOV
193
            this._setTemplates(templates ?? {});
×
194
        });
195
    }
196

197
    private _setTemplates(newTemplates: IgxChatTemplates): void {
UNCOV
198
        const templateCopies: ChatRenderers = {};
×
UNCOV
199
        const newTemplateKeys = Object.keys(newTemplates) as Array<keyof IgxChatTemplates>;
×
200

UNCOV
201
        const oldTemplates = this._oldTemplates;
×
UNCOV
202
        const oldTemplateKeys = Object.keys(oldTemplates) as Array<keyof IgxChatTemplates>;
×
203

UNCOV
204
        for (const key of oldTemplateKeys) {
×
205
            const oldRef = oldTemplates[key];
×
206
            const newRef = newTemplates[key];
×
207

208
            if (oldRef && oldRef !== newRef) {
×
209
                const obsolete = this._templateViewRefs.get(oldRef);
×
210
                if (obsolete) {
×
211
                    obsolete.forEach(viewRef => viewRef.destroy());
×
212
                    this._templateViewRefs.delete(oldRef);
×
213
                }
214
            }
215
        }
216

UNCOV
217
        this._oldTemplates = {};
×
218

UNCOV
219
        for (const key of newTemplateKeys) {
×
UNCOV
220
            const ref = newTemplates[key];
×
UNCOV
221
            if (ref) {
×
UNCOV
222
                (this._oldTemplates as Record<string, TemplateRef<unknown>>)[key] = ref;
×
UNCOV
223
                templateCopies[key] = this._createTemplateRenderer(ref);
×
224
            }
225
        }
226

UNCOV
227
        this._transformedTemplates.set(templateCopies);
×
228
    }
229

230
    private _createTemplateRenderer<K extends keyof IgxChatTemplates>(ref: NonNullable<IgxChatTemplates[K]>) {
231
        type ChatContext = ExtractChatContext<NonNullable<ChatRenderers[K]>>;
232

UNCOV
233
        if (!this._templateViewRefs.has(ref)) {
×
UNCOV
234
            this._templateViewRefs.set(ref, new Set<ViewRef>());
×
235
        }
236

UNCOV
237
        const viewSet = this._templateViewRefs.get(ref)!;
×
238

UNCOV
239
        return (ctx: ChatContext) => {
×
UNCOV
240
            const context = ctx as ChatContextUnion;
×
241
            let angularContext: any;
242

UNCOV
243
            if ('message' in context && 'attachment' in context) {
×
244
                angularContext = { $implicit: context.attachment };
×
UNCOV
245
            } else if ('message' in context) {
×
UNCOV
246
                angularContext = { $implicit: context.message };
×
247
            } else if ('value' in context) {
×
248
                angularContext = {
×
249
                    $implicit: context.value,
250
                    attachments: context.attachments
251
                };
252
            } else {
253
                angularContext = { $implicit: { instance: context.instance } };
×
254
            }
255

UNCOV
256
            const viewRef = this._view.createEmbeddedView(ref, angularContext);
×
UNCOV
257
            viewSet.add(viewRef);
×
258

UNCOV
259
            return viewRef.rootNodes;
×
260
        }
261
    }
262
}
263

264
/**
265
 * Context provided to the chat input template.
266
 */
267
export interface ChatInputContext {
268
    /** The current input value */
269
    $implicit: string;
270
    /** Array of attachments associated with the input */
271
    attachments: IgcChatMessageAttachment[];
272
}
273

274
/**
275
 * Directive providing type information for chat message template contexts.
276
 * Use this directive on ng-template elements that render chat messages.
277
 *
278
 * @example
279
 * ```html
280
 * <ng-template igxChatMessageContext let-message>
281
 *   <div>{{ message.text }}</div>
282
 * </ng-template>
283
 * ```
284
 */
285
@Directive({ selector: '[igxChatMessageContext]', standalone: true })
286
export class IgxChatMessageContextDirective {
3✔
287

288
    public static ngTemplateContextGuard(_: IgxChatMessageContextDirective, ctx: unknown): ctx is { $implicit: IgcChatMessage } {
289
        return true;
×
290
    }
291
}
292

293
/**
294
 * Directive providing type information for chat attachment template contexts.
295
 * Use this directive on ng-template elements that render message attachments.
296
 *
297
 * @example
298
 * ```html
299
 * <ng-template igxChatAttachmentContext let-attachment>
300
 *   <img [src]="attachment.url" />
301
 * </ng-template>
302
 * ```
303
 */
304
@Directive({ selector: '[igxChatAttachmentContext]', standalone: true })
305
export class IgxChatAttachmentContextDirective {
3✔
306

307
    public static ngTemplateContextGuard(_: IgxChatAttachmentContextDirective, ctx: unknown): ctx is { $implicit: IgcChatMessageAttachment } {
308
        return true;
×
309
    }
310
}
311

312
/**
313
 * Directive providing type information for chat input template contexts.
314
 * Use this directive on ng-template elements that render the chat input.
315
 *
316
 * @example
317
 * ```html
318
 * <ng-template igxChatInputContext let-value let-attachments="attachments">
319
 *   <input [value]="value" />
320
 * </ng-template>
321
 * ```
322
 */
323
@Directive({ selector: '[igxChatInputContext]', standalone: true })
324
export class IgxChatInputContextDirective {
3✔
325

326
    public static ngTemplateContextGuard(_: IgxChatInputContextDirective, ctx: unknown): ctx is ChatInputContext {
327
        return true;
×
328
    }
329
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc