Skip to content

Commit ad57339

Browse files
committed
Merge branch 'dmdimitrov/chat-ai-component' of https://github.com/IgniteUI/igniteui-webcomponents into dmdimitrov/chat-ai-component
2 parents c703225 + 96569e3 commit ad57339

File tree

3 files changed

+192
-1
lines changed

3 files changed

+192
-1
lines changed

src/components/chat/chat-message.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ export default class IgcChatMessageComponent extends LitElement {
8888
}
8989

9090
protected override firstUpdated(): void {
91-
chatMessageAdoptPageStyles(this);
91+
if (this._state.options?.adoptRootStyles) {
92+
chatMessageAdoptPageStyles(this);
93+
}
9294
}
9395

9496
private _getRenderer(name: keyof DefaultMessageRenderers) {

src/components/chat/chat.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,65 @@ describe('Chat', () => {
10171017
).to.equal(0);
10181018
});
10191019
});
1020+
1021+
describe('adoptRootStyles behavior', () => {
1022+
const customStyles = 'custom-background';
1023+
1024+
beforeEach(async () => {
1025+
const styles = document.createElement('style');
1026+
styles.setAttribute('id', 'adopt-styles-test');
1027+
styles.innerHTML = `
1028+
.custom-background {
1029+
background-color: rgb(255, 0, 0);
1030+
}
1031+
`;
1032+
document.head.append(styles);
1033+
});
1034+
1035+
it('correctly applies `adoptRootStyles` when set', async () => {
1036+
chat.options = {
1037+
adoptRootStyles: true,
1038+
renderers: {
1039+
messageContent: ({ message }) =>
1040+
html`<div class=${customStyles}>${message.text}</div>`,
1041+
},
1042+
};
1043+
chat.messages = [{ id: 'id', sender: 'bot', text: 'Hello' }];
1044+
1045+
await elementUpdated(chat);
1046+
1047+
const { messages } = getChatDOM(chat);
1048+
expect(
1049+
getComputedStyle(
1050+
getChatMessageDOM(first(messages)).content.querySelector(
1051+
`.${customStyles}`
1052+
)!
1053+
).backgroundColor
1054+
).equal('rgb(255, 0, 0)');
1055+
});
1056+
1057+
it('skips `adoptRootStyles` when not set', async () => {
1058+
chat.options = {
1059+
renderers: {
1060+
messageContent: ({ message }) =>
1061+
html`<div class=${customStyles}>${message.text}</div>`,
1062+
},
1063+
};
1064+
1065+
chat.messages = [{ id: 'id', sender: 'bot', text: 'Hello' }];
1066+
1067+
await elementUpdated(chat);
1068+
1069+
const { messages } = getChatDOM(chat);
1070+
expect(
1071+
getComputedStyle(
1072+
getChatMessageDOM(first(messages)).content.querySelector(
1073+
`.${customStyles}`
1074+
)!
1075+
).backgroundColor
1076+
).not.equal('rgb(255, 0, 0)');
1077+
});
1078+
});
10201079
});
10211080

10221081
/** Returns an object containing the shadow DOM structure of a chat component. */

src/components/chat/types.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,50 +126,180 @@ export type IgcChatOptions = {
126126
*/
127127
stopTypingDelay?: number;
128128

129+
/**
130+
* A boolean flag that, when `true`, enables a **quick and dirty workaround** for styling
131+
* components rendered within the Shadow DOM, from custom message renderers, by allowing them
132+
* to inherit styles from the document's root. This can be useful for developers who prefer not to handle
133+
* Shadow DOM encapsulation, but it is **not the recommended approach**.
134+
*
135+
* It is highly advised to use standard Web Component styling methods first, in this order:
136+
*
137+
* 1. **CSS Variables and Parts API**: Use the exposed `::part` API with custom CSS variables to style
138+
* your content in your custom renderers.
139+
*
140+
* 2. **`link` elements**: For larger style sheets, link them directly within the Shadow DOM to maintain
141+
* encapsulation.
142+
*
143+
* 3. **Inline `<style>` tags**: Use these for small, self-contained styles within a template.
144+
*
145+
* This property should be used as a **last resort** as it can lead to **style leakage**, where
146+
* global styles unexpectedly bleed into the component, breaking encapsulation and causing
147+
* unpredictable visual issues.
148+
*
149+
* **WARNING**: This is a once time shot. Changing this property in runtime won't reflect
150+
* its value.
151+
*/
152+
adoptRootStyles?: boolean;
153+
154+
/**
155+
* An object containing a collection of custom renderers for different parts of the chat UI.
156+
*/
129157
renderers?: ChatRenderers;
130158
};
131159

160+
/**
161+
* Represents a user's reaction to a specific chat message.
162+
*/
132163
export interface IgcChatMessageReaction {
164+
/**
165+
* The chat message that the reaction is associated with.
166+
*/
133167
message: IgcChatMessage;
168+
/**
169+
* The string representation of the reaction, such as an emoji or a string;
170+
*/
134171
reaction: string;
135172
}
136173

174+
/**
175+
* A collection of optional rendering functions that allow for custom UI rendering.
176+
* Each property is a function that takes a context object and returns a template result.
177+
*/
137178
export interface ChatRenderers {
179+
/**
180+
* Custom renderer for a single chat message attachment.
181+
*/
138182
attachment?: ChatTemplateRenderer<ChatAttachmentRenderContext>;
183+
/**
184+
* Custom renderer for the content of an attachment.
185+
*/
139186
attachmentContent?: ChatTemplateRenderer<ChatAttachmentRenderContext>;
187+
/**
188+
* Custom renderer for the header of an attachment.
189+
*/
140190
attachmentHeader?: ChatTemplateRenderer<ChatAttachmentRenderContext>;
191+
/**
192+
* Custom renderer for the file upload button in the input area.
193+
*/
141194
fileUploadButton?: ChatTemplateRenderer<ChatRenderContext>;
195+
/**
196+
* Custom renderer for the main chat input field.
197+
*/
142198
input?: ChatTemplateRenderer<ChatInputRenderContext>;
199+
/**
200+
* Custom renderer for the actions container within the input area.
201+
*/
143202
inputActions?: ChatTemplateRenderer<ChatRenderContext>;
203+
/**
204+
* Custom renderer for the actions at the end of the input area.
205+
*/
144206
inputActionsEnd?: ChatTemplateRenderer<ChatRenderContext>;
207+
/**
208+
* Custom renderer for the actions at the start of the input area.
209+
*/
145210
inputActionsStart?: ChatTemplateRenderer<ChatRenderContext>;
211+
/**
212+
* Custom renderer for the attachment previews within the input field.
213+
*/
146214
inputAttachments?: ChatTemplateRenderer<ChatInputRenderContext>;
215+
/**
216+
* Custom renderer for an entire chat message bubble.
217+
*/
147218
message?: ChatTemplateRenderer<ChatMessageRenderContext>;
219+
/**
220+
* Custom renderer for message-specific actions (e.g., reply or delete buttons).
221+
*/
148222
messageActions?: ChatTemplateRenderer<ChatMessageRenderContext>;
223+
/**
224+
* Custom renderer for the attachments associated with a message.
225+
*/
149226
messageAttachments?: ChatTemplateRenderer<ChatMessageRenderContext>;
227+
/**
228+
* Custom renderer for the main text and content of a message.
229+
*/
150230
messageContent?: ChatTemplateRenderer<ChatMessageRenderContext>;
231+
/**
232+
* Custom renderer for the header of a message, including sender and timestamp.
233+
*/
151234
messageHeader?: ChatTemplateRenderer<ChatMessageRenderContext>;
235+
/**
236+
* Custom renderer for the "is typing" indicator.
237+
*/
152238
typingIndicator?: ChatTemplateRenderer<ChatRenderContext>;
239+
/**
240+
* Custom renderer for the message send button.
241+
*/
153242
sendButton?: ChatTemplateRenderer<ChatRenderContext>;
243+
/**
244+
* Custom renderer for the prefix text shown before suggestions.
245+
*/
154246
suggestionPrefix?: ChatTemplateRenderer<ChatRenderContext>;
155247
}
156248

249+
/**
250+
* A generic type for a function that serves as a custom renderer.
251+
* It takes a context object of type T and returns a template result.
252+
*/
157253
export type ChatTemplateRenderer<T> = (ctx: T) => unknown;
254+
255+
/**
256+
* A string literal type defining the two possible positions for chat suggestions.
257+
*/
158258
export type ChatSuggestionsPosition = 'below-input' | 'below-messages';
159259

260+
/**
261+
* The base context object passed to custom renderer functions, containing the chat component instance.
262+
*/
160263
export interface ChatRenderContext {
264+
/**
265+
* The instance of the IgcChatComponent.
266+
*/
161267
instance: IgcChatComponent;
162268
}
163269

270+
/**
271+
* The context object for renderers that deal with the chat input area.
272+
* It extends the base context with input-specific properties.
273+
*/
164274
export interface ChatInputRenderContext extends ChatRenderContext {
275+
/**
276+
* The list of attachments currently in the input area.
277+
*/
165278
attachments: IgcChatMessageAttachment[];
279+
/**
280+
* The current value of the input field.
281+
*/
166282
value: string;
167283
}
168284

285+
/**
286+
* The context object for renderers that deal with a specific chat message.
287+
* It extends the base context with the message data.
288+
*/
169289
export interface ChatMessageRenderContext extends ChatRenderContext {
290+
/**
291+
* The specific chat message being rendered.
292+
*/
170293
message: IgcChatMessage;
171294
}
172295

296+
/**
297+
* The context object for renderers that deal with a specific attachment within a message.
298+
* It extends the message context with the attachment data.
299+
*/
173300
export interface ChatAttachmentRenderContext extends ChatMessageRenderContext {
301+
/**
302+
* The specific attachment being rendered.
303+
*/
174304
attachment: IgcChatMessageAttachment;
175305
}

0 commit comments

Comments
 (0)