-
Notifications
You must be signed in to change notification settings - Fork 160
feat: add chat component Angular wrapper #16255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
c96c716
feat: Added chat component Angular wrapper
rkaraivanov feb599b
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
ChronosSF b1a45c0
chore(*): pushing updated package.lock
ChronosSF e0939f4
fix: Old templates refs handling when there is a dynamic change
rkaraivanov 23490fb
Merge branch 'rkaraivanov/wc-chat-wrapper' of https://github.com/Igni…
rkaraivanov 91e241c
feat: Added chat template directives
rkaraivanov fe80cc1
chore(*): Added a dev sample for the Chat component
gedinakova 94ee98a
fix: Markdown render and shiki initialization
rkaraivanov 9d0675e
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov 8fcb12c
chore: Dedupe and regenerate package-lock
rkaraivanov 2ca4602
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
gedinakova 67389ae
fix(CHat): Adjusted suggestions to dev context.
gedinakova 2d6086f
chore(*): Updated package-lock.
gedinakova 80fe475
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
rkaraivanov f0da68c
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov a0c1e68
fix(chat): Added ng-content to support underlying slots
rkaraivanov 83a600a
Merge branch 'rkaraivanov/wc-chat-wrapper' of https://github.com/Igni…
rkaraivanov 5bdbb25
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov 945c9a7
fix(*): Added messageReact event
gedinakova bba26ce
Merge branch 'rkaraivanov/wc-chat-wrapper' of https://github.com/Igni…
gedinakova 99be9cb
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
gedinakova 6e6215b
refactor: Moved markdown pipe into a separate entry point
rkaraivanov f7465a2
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
rkaraivanov 7c66dbf
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
rkaraivanov c900ab7
refactor: Chat wrapper improvements
rkaraivanov 6ab561a
fix: Rename extras to chat-extras for clarity
rkaraivanov aa9836e
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov cece274
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
ChronosSF 115fbe3
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
ChronosSF 09c5144
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
gedinakova 0250924
Merge branch 'master' of https://github.com/IgniteUI/igniteui-angular…
gedinakova 0ca2138
Merge branch 'rkaraivanov/wc-chat-wrapper' of https://github.com/Igni…
gedinakova 8f48e77
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov be91457
chore: Regenerate package-lock.json
rkaraivanov 6cbcadd
refactor: Chat component wrapper moved to its own library
rkaraivanov d750a12
chore: update package-lock.json yet again
rkaraivanov 538eeb8
chore: hecking ng-package convention
rkaraivanov 6c1ec77
chore: path fixes
rkaraivanov 4eefc55
Merge branch 'master' into rkaraivanov/wc-chat-wrapper
ChronosSF a4d36b8
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov bafb381
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov 6a1a52c
fix: Removed inline styles from chat component
rkaraivanov efaa982
refactor: Minor improvements to chat component
rkaraivanov 4a99550
fix: Added igniteui-webcomponents and markdown dependencies
rkaraivanov ead65cb
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
rkaraivanov 3112005
chore: update dependency versions in dependency-handler.ts
rkaraivanov 88f50cb
Merge remote-tracking branch 'origin/master' into rkaraivanov/wc-chat…
damyanpetev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from './src/public_api'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {} |
57 changes: 57 additions & 0 deletions
57
projects/igniteui-angular/chat-extras/src/markdown-pipe.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { DomSanitizer } from '@angular/platform-browser'; | ||
| import { TestBed } from '@angular/core/testing'; | ||
| import { IgxChatMarkdownService } from './markdown-service'; | ||
| import { MarkdownPipe } from './markdown-pipe'; | ||
| import Spy = jasmine.Spy; | ||
|
|
||
| // Mock the Service: We only care that the pipe calls the service and gets an HTML string. | ||
| // We provide a *known* unsafe HTML string to ensure sanitization is working. | ||
| const mockUnsafeHtml = ` | ||
| <pre class="shiki" style="color: var(--shiki-fg);"><code><span style="color: #FF0000;">unsafe</span></code></pre> | ||
| <img src="x" onerror="alert(1)"> | ||
| `; | ||
|
|
||
| class MockChatMarkdownService { | ||
| public async parse(_: string): Promise<string> { | ||
| return mockUnsafeHtml; | ||
| } | ||
| } | ||
|
|
||
| describe('MarkdownPipe', () => { | ||
| let pipe: MarkdownPipe; | ||
| let sanitizer: DomSanitizer; | ||
| let bypassSpy: Spy; | ||
|
|
||
| beforeEach(() => { | ||
| TestBed.configureTestingModule({ | ||
| providers: [ | ||
| MarkdownPipe, | ||
| { provide: IgxChatMarkdownService, useClass: MockChatMarkdownService }, | ||
| ], | ||
| }); | ||
|
|
||
| pipe = TestBed.inject(MarkdownPipe); | ||
| sanitizer = TestBed.inject(DomSanitizer); | ||
| bypassSpy = spyOn(sanitizer, 'bypassSecurityTrustHtml').and.callThrough(); | ||
| }); | ||
|
|
||
| it('should be created', () => { | ||
| expect(pipe).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should call the service, sanitize content, and return SafeHtml', async () => { | ||
| await pipe.transform('some markdown'); | ||
|
|
||
| expect(bypassSpy).toHaveBeenCalledTimes(1); | ||
|
|
||
| const sanitizedString = bypassSpy.calls.mostRecent().args[0]; | ||
|
|
||
| expect(sanitizedString).not.toContain('onerror'); | ||
| expect(sanitizedString).toContain('style="color: var(--shiki-fg);"'); | ||
| }); | ||
|
|
||
| it('should handle undefined input text', async () => { | ||
| await pipe.transform(undefined); | ||
| expect(sanitizer.bypassSecurityTrustHtml).toHaveBeenCalled(); | ||
| }); | ||
| }); |
18 changes: 18 additions & 0 deletions
18
projects/igniteui-angular/chat-extras/src/markdown-pipe.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import DOMPurify from 'dompurify'; | ||
| import { inject, Pipe, type PipeTransform } from '@angular/core'; | ||
| import { IgxChatMarkdownService } from './markdown-service'; | ||
| import { DomSanitizer, type SafeHtml } from '@angular/platform-browser'; | ||
|
|
||
|
|
||
| @Pipe({ name: 'fromMarkdown' }) | ||
| export class MarkdownPipe implements PipeTransform { | ||
| private _service = inject(IgxChatMarkdownService); | ||
| private _sanitizer = inject(DomSanitizer); | ||
|
|
||
|
|
||
| public async transform(text?: string): Promise<SafeHtml> { | ||
| return this._sanitizer.bypassSecurityTrustHtml(DOMPurify.sanitize( | ||
| await this._service.parse(text ?? '') | ||
| )); | ||
| } | ||
| } |
41 changes: 41 additions & 0 deletions
41
projects/igniteui-angular/chat-extras/src/markdown-service.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { TestBed } from '@angular/core/testing'; | ||
| import { IgxChatMarkdownService } from './markdown-service'; | ||
|
|
||
| describe('IgxChatMarkdownService', () => { | ||
| let service: IgxChatMarkdownService; | ||
|
|
||
| beforeEach(() => { | ||
| TestBed.configureTestingModule({}); | ||
| service = TestBed.inject(IgxChatMarkdownService); | ||
| }); | ||
|
|
||
| it('should be created', () => { | ||
| expect(service).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should parse basic markdown to HTML', async () => { | ||
| const markdown = '**Hello** *World*'; | ||
| const expectedHtml = '<p><strong>Hello</strong> <em>World</em></p>\n'; | ||
|
|
||
| const result = await service.parse(markdown); | ||
| expect(result).toBe(expectedHtml); | ||
| }); | ||
|
|
||
| it('should parse a code block with shiki highlighting', async () => { | ||
| const markdown = '```typescript\nconst x = 5;\n```'; | ||
| const result = await service.parse(markdown); | ||
|
|
||
| expect(result).toContain('<pre class="shiki shiki-themes github-light github-dark"'); | ||
| expect(result).toContain('const'); | ||
| expect(result).toMatch(/--shiki-.*?/); | ||
| expect(result).toContain('code'); | ||
| }); | ||
|
|
||
| it('should apply custom link extension with target="_blank"', async () => { | ||
| const markdown = '[Infragistics](https://www.infragistics.com)'; | ||
| const expectedLink = '<p><a href="https://www.infragistics.com" target="_blank" rel="noopener noreferrer" >Infragistics</a></p>'; | ||
|
|
||
| const result = await service.parse(markdown); | ||
| expect(result).toContain(expectedLink); | ||
| }); | ||
| }); |
67 changes: 67 additions & 0 deletions
67
projects/igniteui-angular/chat-extras/src/markdown-service.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| import { Injectable } from '@angular/core'; | ||
| import { Marked } from 'marked'; | ||
| import markedShiki from 'marked-shiki'; | ||
| import { bundledThemes, createHighlighter } from 'shiki/bundle/web'; | ||
rkaraivanov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| const DEFAULT_LANGUAGES = ['javascript', 'typescript', 'html', 'css']; | ||
| const DEFAULT_THEMES = { | ||
| light: 'github-light', | ||
| dark: 'github-dark' | ||
| }; | ||
|
|
||
| @Injectable({ providedIn: 'root' }) | ||
| export class IgxChatMarkdownService { | ||
|
|
||
| private _instance: Marked; | ||
| private _isInitialized: Promise<void>; | ||
|
|
||
| private _initializeMarked(): void { | ||
| this._instance = new Marked({ | ||
| breaks: true, | ||
| gfm: true, | ||
| extensions: [ | ||
| { | ||
| name: 'link', | ||
| renderer({ href, title, text }) { | ||
| return `<a href="${href}" target="_blank" rel="noopener noreferrer" ${title ? `title="${title}"` : ''}>${text}</a>`; | ||
| } | ||
| } | ||
| ] | ||
| }); | ||
| } | ||
|
|
||
| private async _initializeShiki(): Promise<void> { | ||
| const highlighter = await createHighlighter({ | ||
| langs: DEFAULT_LANGUAGES, | ||
| themes: Object.keys(bundledThemes) | ||
| }); | ||
|
|
||
| this._instance.use( | ||
| markedShiki({ | ||
| highlight(code, lang, _) { | ||
| try { | ||
| return highlighter.codeToHtml(code, { | ||
| lang, | ||
| themes: DEFAULT_THEMES, | ||
| }); | ||
|
|
||
| } catch { | ||
| return `<pre><code>${code}</code></pre>`; | ||
| } | ||
| } | ||
| }) | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| constructor() { | ||
| this._initializeMarked(); | ||
| this._isInitialized = this._initializeShiki(); | ||
| } | ||
|
|
||
| public async parse(text: string): Promise<string> { | ||
| await this._isInitialized; | ||
| return await this._instance.parse(text); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { MarkdownPipe } from './markdown-pipe'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from './src/public_api'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| <igc-chat | ||
| [messages]="messages()" | ||
| [draftMessage]="draftMessage()" | ||
| [options]="_mergedOptions()" | ||
| (igcMessageCreated)="messageCreated.emit($event.detail)" | ||
| (igcMessageReact)="messageReact.emit($event.detail)" | ||
| (igcAttachmentClick)="attachmentClick.emit($event.detail)" | ||
| (igcAttachmentDrag)="attachmentDrag.emit()" | ||
| (igcAttachmentDrop)="attachmentDrop.emit()" | ||
| (igcTypingChange)="typingChange.emit($event.detail)" | ||
| (igcInputFocus)="inputFocus.emit()" | ||
| (igcInputBlur)="inputBlur.emit()" | ||
| (igcInputChange)="inputChange.emit($event.detail)" | ||
| > | ||
| <ng-content></ng-content> | ||
| </igc-chat> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.