@chat21/chat21-web-widget 5.1.34-rc1 → 5.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +13 -23
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
  3. package/CHANGELOG.md +8 -129
  4. package/Dockerfile +5 -4
  5. package/angular.json +3 -21
  6. package/docs/changelog/this-branch.md +0 -36
  7. package/env.sample +2 -3
  8. package/nginx.conf +2 -22
  9. package/package.json +3 -10
  10. package/src/app/app.component.html +2 -2
  11. package/src/app/app.component.scss +14 -25
  12. package/src/app/app.component.spec.ts +6 -21
  13. package/src/app/app.component.ts +9 -10
  14. package/src/app/app.module.ts +0 -13
  15. package/src/app/component/conversation-detail/conversation/conversation.component.html +11 -25
  16. package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -40
  17. package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +75 -644
  18. package/src/app/component/conversation-detail/conversation/conversation.component.ts +14 -100
  19. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +13 -25
  20. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +5 -123
  21. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +0 -1
  22. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +10 -23
  23. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -19
  24. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +149 -242
  25. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +5 -8
  26. package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +3 -53
  27. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +96 -200
  28. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +6 -211
  29. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +78 -452
  30. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +76 -291
  31. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +53 -113
  32. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +4 -12
  33. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +29 -274
  34. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +9 -23
  35. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +8 -80
  36. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +23 -29
  37. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +16 -185
  38. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +14 -34
  39. package/src/app/component/error-alert/error-alert.component.spec.ts +5 -65
  40. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +7 -16
  41. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +0 -21
  42. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +7 -89
  43. package/src/app/component/form/form-builder/form-builder.component.html +1 -1
  44. package/src/app/component/form/form-builder/form-builder.component.spec.ts +21 -163
  45. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +4 -8
  46. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +5 -10
  47. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +16 -90
  48. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +0 -26
  49. package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +11 -45
  50. package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +6 -24
  51. package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +5 -14
  52. package/src/app/component/form/inputs/form-text/form-text.component.html +12 -14
  53. package/src/app/component/form/inputs/form-text/form-text.component.scss +1 -11
  54. package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +17 -113
  55. package/src/app/component/form/inputs/form-text/form-text.component.ts +3 -35
  56. package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +11 -13
  57. package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +5 -6
  58. package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +13 -149
  59. package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +0 -26
  60. package/src/app/component/form/prechat-form/prechat-form.component.html +11 -14
  61. package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +10 -102
  62. package/src/app/component/form/prechat-form/prechat-form.component.ts +1 -8
  63. package/src/app/component/home/home.component.html +31 -38
  64. package/src/app/component/home/home.component.scss +2 -4
  65. package/src/app/component/home/home.component.spec.ts +11 -226
  66. package/src/app/component/home-conversations/home-conversations.component.html +26 -30
  67. package/src/app/component/home-conversations/home-conversations.component.scss +0 -3
  68. package/src/app/component/home-conversations/home-conversations.component.spec.ts +36 -212
  69. package/src/app/component/last-message/last-message.component.html +9 -15
  70. package/src/app/component/last-message/last-message.component.scss +2 -16
  71. package/src/app/component/last-message/last-message.component.spec.ts +23 -204
  72. package/src/app/component/last-message/last-message.component.ts +1 -4
  73. package/src/app/component/launcher-button/launcher-button.component.html +13 -8
  74. package/src/app/component/launcher-button/launcher-button.component.spec.ts +8 -104
  75. package/src/app/component/list-all-conversations/list-all-conversations.component.html +17 -12
  76. package/src/app/component/list-all-conversations/list-all-conversations.component.scss +0 -2
  77. package/src/app/component/list-conversations/list-conversations.component.html +22 -22
  78. package/src/app/component/menu-options/menu-options.component.html +20 -30
  79. package/src/app/component/menu-options/menu-options.component.spec.ts +9 -125
  80. package/src/app/component/message/audio/audio.component.html +15 -13
  81. package/src/app/component/message/audio/audio.component.spec.ts +5 -140
  82. package/src/app/component/message/audio/audio.component.ts +5 -1
  83. package/src/app/component/message/avatar/avatar.component.html +2 -2
  84. package/src/app/component/message/avatar/avatar.component.spec.ts +7 -99
  85. package/src/app/component/message/bubble-message/bubble-message.component.html +51 -38
  86. package/src/app/component/message/bubble-message/bubble-message.component.scss +1 -54
  87. package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +57 -154
  88. package/src/app/component/message/bubble-message/bubble-message.component.ts +11 -89
  89. package/src/app/component/message/buttons/action-button/action-button.component.html +4 -3
  90. package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +5 -49
  91. package/src/app/component/message/buttons/link-button/link-button.component.scss +8 -5
  92. package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +5 -50
  93. package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +5 -44
  94. package/src/app/component/message/carousel/carousel.component.html +16 -29
  95. package/src/app/component/message/carousel/carousel.component.scss +8 -20
  96. package/src/app/component/message/carousel/carousel.component.spec.ts +3 -80
  97. package/src/app/component/message/carousel/carousel.component.ts +0 -16
  98. package/src/app/component/message/frame/frame.component.html +4 -9
  99. package/src/app/component/message/frame/frame.component.spec.ts +15 -34
  100. package/src/app/component/message/frame/frame.component.ts +2 -7
  101. package/src/app/component/message/html/html.component.html +1 -1
  102. package/src/app/component/message/html/html.component.scss +1 -1
  103. package/src/app/component/message/html/html.component.spec.ts +7 -24
  104. package/src/app/component/message/image/image.component.html +10 -12
  105. package/src/app/component/message/image/image.component.scss +0 -16
  106. package/src/app/component/message/image/image.component.spec.ts +15 -101
  107. package/src/app/component/message/image/image.component.ts +51 -90
  108. package/src/app/component/message/info-message/info-message.component.spec.ts +14 -26
  109. package/src/app/component/message/like-unlike/like-unlike.component.html +9 -7
  110. package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +3 -31
  111. package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +17 -38
  112. package/src/app/component/message/text/text.component.html +3 -3
  113. package/src/app/component/message/text/text.component.scss +86 -80
  114. package/src/app/component/message/text/text.component.spec.ts +13 -106
  115. package/src/app/component/message-attachment/message-attachment.component.spec.ts +13 -134
  116. package/src/app/component/selection-department/selection-department.component.html +23 -21
  117. package/src/app/component/selection-department/selection-department.component.spec.ts +14 -159
  118. package/src/app/component/selection-department/selection-department.component.ts +1 -8
  119. package/src/app/component/send-button/send-button.component.html +13 -5
  120. package/src/app/component/send-button/send-button.component.spec.ts +2 -2
  121. package/src/app/component/star-rating-widget/star-rating-widget.component.html +81 -51
  122. package/src/app/directives/tooltip.directive.spec.ts +4 -8
  123. package/src/app/modals/confirm-close/confirm-close.component.html +8 -20
  124. package/src/app/modals/confirm-close/confirm-close.component.scss +0 -3
  125. package/src/app/modals/confirm-close/confirm-close.component.spec.ts +4 -13
  126. package/src/app/modals/confirm-close/confirm-close.component.ts +1 -8
  127. package/src/app/pipe/html-entites-encode.pipe.spec.ts +2 -35
  128. package/src/app/pipe/marked.pipe.spec.ts +2 -38
  129. package/src/app/pipe/marked.pipe.ts +41 -51
  130. package/src/app/providers/app-config.service.ts +2 -4
  131. package/src/app/providers/brand.service.spec.ts +2 -23
  132. package/src/app/providers/brand.service.ts +1 -1
  133. package/src/app/providers/global-settings.service.spec.ts +14 -1009
  134. package/src/app/providers/global-settings.service.ts +2 -82
  135. package/src/app/providers/translator.service.ts +6 -26
  136. package/src/app/sass/_variables.scss +0 -3
  137. package/src/app/sass/animations.scss +1 -19
  138. package/src/app/utils/globals.ts +1 -21
  139. package/src/app/utils/utils-resources.ts +1 -1
  140. package/src/assets/i18n/en.json +99 -106
  141. package/src/assets/i18n/es.json +100 -107
  142. package/src/assets/i18n/fr.json +100 -107
  143. package/src/assets/i18n/it.json +98 -107
  144. package/src/assets/twp/index-dev.html +0 -18
  145. package/src/chat21-core/models/message.ts +1 -2
  146. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +2 -3
  147. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +0 -12
  148. package/src/chat21-core/providers/scripts/script.service.spec.ts +2 -12
  149. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  150. package/src/chat21-core/utils/utils-message.ts +0 -7
  151. package/src/chat21-core/utils/utils.ts +2 -5
  152. package/src/widget-config-template.json +1 -4
  153. package/src/widget-config.json +1 -4
  154. package/tsconfig.json +0 -5
  155. package/.angular-mcp-cache/package.json +0 -1
  156. package/.cursor/angular18-accessibility-auditor-skill.md +0 -442
  157. package/.cursor/mcp.json +0 -15
  158. package/.github/workflows/build.yml +0 -22
  159. package/.github/workflows/playwright.yml +0 -27
  160. package/mocks/voice-websocket-mock/server.cjs +0 -245
  161. package/playwright.config.ts +0 -41
  162. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +0 -46
  163. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +0 -83
  164. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +0 -192
  165. package/src/app/component/form/prechat-form-test-mock.ts +0 -35
  166. package/src/app/component/message/audio-sync/audio-sync.component.html +0 -18
  167. package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -65
  168. package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +0 -103
  169. package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -643
  170. package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +0 -117
  171. package/src/app/providers/tts-audio-playback-coordinator.service.ts +0 -109
  172. package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +0 -12
  173. package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +0 -171
  174. package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +0 -39
  175. package/src/app/providers/voice/audio.types.ts +0 -40
  176. package/src/app/providers/voice/vad.service.spec.ts +0 -28
  177. package/src/app/providers/voice/vad.service.ts +0 -70
  178. package/src/app/providers/voice/voice-streaming.service.spec.ts +0 -23
  179. package/src/app/providers/voice/voice-streaming.service.ts +0 -702
  180. package/src/app/providers/voice/voice-streaming.types.ts +0 -112
  181. package/src/app/providers/voice/voice.service.spec.ts +0 -227
  182. package/src/app/providers/voice/voice.service.ts +0 -973
  183. package/src/app/shims/onnxruntime-web-wasm.ts +0 -4
  184. package/src/assets/onnx/ort-wasm-simd-threaded.mjs +0 -59
  185. package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
  186. package/src/assets/sounds/keyboard.mp3 +0 -0
  187. package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
  188. package/src/assets/vad/silero_vad_legacy.onnx +0 -0
  189. package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
  190. package/src/chat21-core/providers/chat-manager.spec.ts +0 -72
  191. package/tests/widget-form-rich.spec.ts +0 -67
  192. package/tests/widget-index-dev-settings.spec.ts +0 -52
  193. package/tests/widget-twp-iframe.spec.ts +0 -39
@@ -1,207 +1,38 @@
1
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
- import { FormsModule } from '@angular/forms';
3
- import { By } from '@angular/platform-browser';
1
+ import { DomSanitizer } from '@angular/platform-browser';
2
+ import { LoggerInstance } from './../../../../chat21-core/providers/logger/loggerInstance';
3
+ import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
4
+ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
4
5
 
5
- import { MIN_WIDTH_IMAGES } from 'src/app/utils/constants';
6
6
  import { ConversationPreviewComponent } from './conversation-preview.component';
7
- import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
8
- import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
7
+ import { LogLevel } from 'src/chat21-core/utils/constants';
9
8
  import { NGXLogger } from 'ngx-logger';
9
+ import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
10
+ const mockService = jasmine.createSpyObj('LoggerService', ['setLoggerConfig', "debug", "log", "warn", "info", "error" ]);
10
11
 
11
12
  describe('ConversationPreviewComponent', () => {
12
13
  let component: ConversationPreviewComponent;
13
14
  let fixture: ComponentFixture<ConversationPreviewComponent>;
14
- const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
15
- const customLogger = new CustomLogger(ngxlogger);
16
-
17
- const imageAttachment = (name = 'a.png') => ({
18
- file: new File(['x'], name, { type: 'image/png' }),
19
- metadata: { width: 400, height: 200, type: 'image/png', uid: 'u1', src: 'blob:mock' },
20
- });
15
+ let ngxlogger: NGXLogger;
16
+ let customLogger = new CustomLogger(ngxlogger)
21
17
 
22
18
  beforeEach(waitForAsync(() => {
23
- LoggerInstance.setInstance(customLogger);
24
19
  TestBed.configureTestingModule({
25
- declarations: [ConversationPreviewComponent],
26
- imports: [FormsModule],
27
- })
28
- .overrideComponent(ConversationPreviewComponent, {
29
- set: {
30
- template: `
31
- <div #scrollMe id="c21-contentScroll-preview" style="height:39%">preview</div>
32
- <textarea id="chat21-main-message-context-preview" [(ngModel)]="textInputTextArea"></textarea>
33
- `,
34
- },
35
- })
36
- .compileComponents();
20
+ declarations: [ ConversationPreviewComponent ],
21
+ providers: [LoggerService]
22
+ }).compileComponents();
37
23
  }));
38
24
 
39
25
  beforeEach(() => {
40
26
  fixture = TestBed.createComponent(ConversationPreviewComponent);
41
27
  component = fixture.componentInstance;
42
- component.attachments = [imageAttachment()] as any;
43
- component.baseLocation = '';
44
- component.translationMap = new Map([['LABEL_PLACEHOLDER', 'Placeholder']]);
45
- component.stylesMap = new Map([['themeColor', '#000']]);
46
- component.textInputTextArea = '';
28
+ LoggerInstance.setInstance(customLogger)
29
+ let logger = LoggerInstance.getInstance()
30
+ component['logger']= logger
47
31
  fixture.detectChanges();
48
32
  });
49
33
 
50
34
  it('should create', () => {
35
+ console.log('ConversationPreviewComponent --->', component)
51
36
  expect(component).toBeTruthy();
52
37
  });
53
-
54
- describe('getMetadataSize', () => {
55
- it('should scale wide images to preview max width', () => {
56
- const s = component.getMetadataSize({ width: 500, height: 100 });
57
- expect(s.width).toBe(230);
58
- expect(s.height).toBeCloseTo(46, 5);
59
- });
60
-
61
- it('should bump very narrow images to MIN_WIDTH_IMAGES', () => {
62
- const s = component.getMetadataSize({ width: 40, height: 80 });
63
- expect(s.width).toBe(MIN_WIDTH_IMAGES);
64
- });
65
-
66
- it('should cap tall images by max preview height', () => {
67
- const s = component.getMetadataSize({ width: 100, height: 400 });
68
- expect(s.height).toBe(150);
69
- });
70
-
71
- it('should default undefined metadata then cap tall square to preview max height', () => {
72
- const s = component.getMetadataSize({});
73
- // Both sides start at 230; height branch caps at 150 and scales width to match aspect ratio.
74
- expect(s.height).toBe(150);
75
- expect(s.width).toBe(150);
76
- });
77
- });
78
-
79
- describe('readAsDataURL', () => {
80
- it('should set fileSelected for raster images', () => {
81
- component.fileSelected = undefined as any;
82
- component.readAsDataURL(imageAttachment());
83
- expect(component.fileSelected).toBeDefined();
84
- expect(component.sizeImage).toBeDefined();
85
- });
86
-
87
- it('should sanitize SVG src', () => {
88
- const svg = {
89
- file: new File(['<svg/>'], 'a.svg', { type: 'image/svg+xml' }),
90
- metadata: { width: 10, height: 10, type: 'image/svg+xml', uid: 's1', src: 'data:image/svg+xml;base64,PHN2Zy8+' },
91
- };
92
- component.fileSelected = undefined as any;
93
- component.readAsDataURL(svg);
94
- expect(component.fileSelected).toBeDefined();
95
- });
96
-
97
- it('should route non-image files to createFile', () => {
98
- spyOn(component, 'createFile').and.returnValue(Promise.resolve());
99
- const pdf = {
100
- file: new File(['%PDF'], 'doc.pdf', { type: 'application/pdf' }),
101
- metadata: { uid: 'p1', type: 'application/pdf' },
102
- };
103
- component.readAsDataURL(pdf);
104
- expect(component.createFile).toHaveBeenCalledWith(pdf);
105
- });
106
- });
107
-
108
- describe('createFile', () => {
109
- it('should request placeholder asset from baseLocation', async () => {
110
- const blob = new Blob(['x'], { type: 'image/png' });
111
- spyOn(window, 'fetch').and.returnValue(Promise.resolve({ blob: () => Promise.resolve(blob) } as Response));
112
- component.baseLocation = 'https://app.test';
113
- const att = {
114
- file: new File(['z'], 'note.txt', { type: 'text/plain' }),
115
- metadata: { uid: 'f1', type: 'text/plain' },
116
- };
117
- await component.createFile(att);
118
- expect(window.fetch).toHaveBeenCalledWith('https://app.test/assets/images/file-alt-solid.png');
119
- });
120
- });
121
-
122
- describe('keyboard and actions', () => {
123
- it('onkeydown Escape should close modal', () => {
124
- spyOn(component.onCloseModalPreview, 'emit');
125
- component.onkeydown({ which: 27, keyCode: 27 } as KeyboardEvent);
126
- expect(component.onCloseModalPreview.emit).toHaveBeenCalled();
127
- });
128
-
129
- it('onkeydown Enter with text should emit send and restore', () => {
130
- spyOn(component.onSendAttachment, 'emit');
131
- spyOn(component as any, 'restoreTextArea');
132
- const ta = fixture.debugElement.query(By.css('#chat21-main-message-context-preview')).nativeElement as HTMLTextAreaElement;
133
- ta.value = ' caption ';
134
- const ev = new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13 });
135
- spyOn(ev, 'preventDefault');
136
- component.onkeydown(ev);
137
- expect(ev.preventDefault).toHaveBeenCalled();
138
- expect(component.onSendAttachment.emit).toHaveBeenCalledWith(' caption ');
139
- expect((component as any).restoreTextArea).toHaveBeenCalled();
140
- });
141
-
142
- it('onkeydown Enter with only whitespace should not emit', () => {
143
- spyOn(component.onSendAttachment, 'emit');
144
- const ta = fixture.debugElement.query(By.css('#chat21-main-message-context-preview')).nativeElement as HTMLTextAreaElement;
145
- ta.value = ' ';
146
- component.onkeydown({ which: 13, keyCode: 13, preventDefault: () => {} } as any);
147
- expect(component.onSendAttachment.emit).not.toHaveBeenCalled();
148
- });
149
-
150
- it('onClickClose should emit', () => {
151
- spyOn(component.onCloseModalPreview, 'emit');
152
- component.onClickClose();
153
- expect(component.onCloseModalPreview.emit).toHaveBeenCalled();
154
- });
155
-
156
- it('onSendPressed should emit current model text', () => {
157
- spyOn(component.onSendAttachment, 'emit');
158
- component.textInputTextArea = 'hi';
159
- component.onSendPressed(new Event('click'));
160
- expect(component.onSendAttachment.emit).toHaveBeenCalledWith('hi');
161
- });
162
- });
163
-
164
- describe('textarea sizing', () => {
165
- it('onTextAreaChange should call resize helpers', () => {
166
- spyOn(component, 'resizeInputField');
167
- spyOn(component, 'resizeModalHeight');
168
- component.onTextAreaChange();
169
- expect(component.resizeInputField).toHaveBeenCalled();
170
- expect(component.resizeModalHeight).toHaveBeenCalled();
171
- });
172
-
173
- it('resizeInputField should grow with multiline content', () => {
174
- const ta = fixture.debugElement.query(By.css('#chat21-main-message-context-preview')).nativeElement as HTMLTextAreaElement;
175
- ta.value = 'a\nb\nc';
176
- ta.style.height = '20px';
177
- Object.defineProperty(ta, 'scrollHeight', { configurable: true, value: 80 });
178
- Object.defineProperty(ta, 'offsetHeight', { configurable: true, value: 20 });
179
- component.resizeInputField();
180
- expect(parseInt(ta.style.height, 10)).toBeGreaterThan(20);
181
- });
182
-
183
- it('resizeModalHeight should adjust host when textarea grows', () => {
184
- const ta = fixture.debugElement.query(By.css('#chat21-main-message-context-preview')).nativeElement as HTMLTextAreaElement;
185
- ta.style.height = '40px';
186
- component.scrollMe = { nativeElement: { style: { height: '' } } } as any;
187
- component.resizeModalHeight();
188
- expect((component.scrollMe.nativeElement.style.height as string).length).toBeGreaterThan(0);
189
- });
190
- });
191
-
192
- describe('onImageRenderedFN', () => {
193
- it('should clear pending load flag', () => {
194
- component.isFilePendingToLoad = true;
195
- component.onImageRenderedFN({});
196
- expect(component.isFilePendingToLoad).toBe(false);
197
- });
198
- });
199
-
200
- describe('onPaste', () => {
201
- it('should call resizeInputField', () => {
202
- spyOn(component, 'resizeInputField');
203
- component.onPaste(new ClipboardEvent('paste'));
204
- expect(component.resizeInputField).toHaveBeenCalled();
205
- });
206
- });
207
38
  });
@@ -1,4 +1,4 @@
1
- import { Component, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, ViewChild } from '@angular/core';
1
+ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
2
2
  import { DomSanitizer } from '@angular/platform-browser';
3
3
  import { MIN_WIDTH_IMAGES } from 'src/app/utils/constants';
4
4
  import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
@@ -244,47 +244,27 @@ export class ConversationPreviewComponent implements OnInit {
244
244
  this.setFocusOnId('chat21-main-message-context-preview');
245
245
  }
246
246
 
247
- /**
248
- * Single keyboard handler for the preview textarea:
249
- * - Enter (no modifier) -> send the attachment with the typed message
250
- * - Shift / Alt / Ctrl / Meta + Enter -> insert a newline (default browser behavior)
251
- * - Escape -> close the preview modal
252
- * Tab navigation is handled by CDK cdkTrapFocus on the dialog wrapper.
253
- * @param event
254
- */
255
- onkeydown(event: KeyboardEvent) {
247
+ /*
248
+ * @param event
249
+ */
250
+ onkeypress(event) {
256
251
  const keyCode = event.which || event.keyCode;
257
-
258
- if (keyCode === 27) { // Esc
259
- this.onClickClose();
260
- return;
261
- }
262
-
263
- if (keyCode === 13) { // ENTER
264
- const hasModifier = event.metaKey || event.shiftKey || event.altKey || event.ctrlKey;
265
- if (hasModifier) {
266
- return;
267
- }
268
-
269
- event.preventDefault();
270
- const target = document.getElementById('chat21-main-message-context-preview') as HTMLInputElement;
271
- if (target) {
272
- this.textInputTextArea = target.value;
273
- }
252
+ this.textInputTextArea = ((document.getElementById('chat21-main-message-context-preview') as HTMLInputElement).value);
253
+ if (keyCode === 13) {
274
254
  if (this.textInputTextArea && this.textInputTextArea.trim() !== '') {
275
255
  this.onSendAttachment.emit(this.textInputTextArea);
276
256
  this.restoreTextArea();
277
257
  }
278
- return;
258
+ } else if (keyCode === 9) {
259
+ event.preventDefault();
279
260
  }
280
261
  }
281
262
 
282
- /** Component-level Esc handler to close the modal even when focus is not on the textarea. */
283
- @HostListener('keydown.escape', ['$event'])
284
- onHostEscape(event: KeyboardEvent){
285
- event.preventDefault();
286
- event.stopPropagation();
287
- this.onClickClose();
263
+ onkeydown(event){
264
+ const keyCode = event.which || event.keyCode;
265
+ if (keyCode === 27) { // Esc keyboard code
266
+ this.onClickClose()
267
+ }
288
268
  }
289
269
 
290
270
  onPaste(event){
@@ -1,85 +1,25 @@
1
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { By } from '@angular/platform-browser';
3
2
 
4
- import { CustomTranslateService } from 'src/chat21-core/providers/custom-translate.service';
5
- import { FILE_SIZE_LIMIT } from 'src/app/utils/constants';
6
3
  import { ErrorAlertComponent } from './error-alert.component';
7
4
 
8
5
  describe('ErrorAlertComponent', () => {
9
6
  let component: ErrorAlertComponent;
10
7
  let fixture: ComponentFixture<ErrorAlertComponent>;
11
- let translateStub: { translateLanguage: jasmine.Spy };
12
8
 
13
9
  beforeEach(async () => {
14
- translateStub = {
15
- translateLanguage: jasmine.createSpy('translateLanguage').and.callFake((keys: string[]) => {
16
- const m = new Map<string, string>();
17
- keys.forEach((k) => m.set(k, `Errore: {{FILE_SIZE_LIMIT}} MB (chiave ${k})`));
18
- return m;
19
- }),
20
- };
21
-
22
10
  await TestBed.configureTestingModule({
23
- declarations: [ErrorAlertComponent],
24
- providers: [{ provide: CustomTranslateService, useValue: translateStub }],
25
- }).compileComponents();
11
+ declarations: [ ErrorAlertComponent ]
12
+ })
13
+ .compileComponents();
26
14
 
27
15
  fixture = TestBed.createComponent(ErrorAlertComponent);
28
16
  component = fixture.componentInstance;
17
+ fixture.detectChanges();
29
18
  });
30
19
 
31
20
  it('should create', () => {
32
- fixture.detectChanges();
33
21
  expect(component).toBeTruthy();
34
22
  });
23
+ });
35
24
 
36
- describe('inputs and translation key path', () => {
37
- it('should resolve errorKeyMessage via CustomTranslateService and interpolate constants + errorParams', () => {
38
- component.errorKeyMessage = 'MY_KEY';
39
- component.errorParams = { FILE_SIZE_LIMIT: 99 };
40
- component.ngOnInit();
41
- expect(translateStub.translateLanguage).toHaveBeenCalledWith(['MY_KEY']);
42
- expect(component.errorMessage).toContain('99');
43
- expect(component.errorMessage).toContain('(chiave MY_KEY)');
44
- });
45
-
46
- it('should render translated message in the alert DOM', () => {
47
- component.errorKeyMessage = 'NET_ERR';
48
- component.ngOnInit();
49
- fixture.detectChanges();
50
- const content = fixture.debugElement.query(By.css('.alert-content')).nativeElement as HTMLElement;
51
- expect(content.textContent?.trim().length).toBeGreaterThan(0);
52
- expect(fixture.nativeElement.querySelector('#alert-container')).toBeTruthy();
53
- });
54
- });
55
-
56
- describe('plain errorMessage and interpolation', () => {
57
- it('should use raw errorMessage when errorKeyMessage is empty', () => {
58
- component.errorKeyMessage = '';
59
- component.errorMessage = 'Connessione persa';
60
- component.ngOnInit();
61
- expect(translateStub.translateLanguage).not.toHaveBeenCalled();
62
- expect(component.errorMessage).toBe('Connessione persa');
63
- });
64
-
65
- it('should replace {{placeholders}} from merged CONSTANTS and errorParams', () => {
66
- component.errorKeyMessage = '';
67
- component.errorMessage = 'Limite {{FILE_SIZE_LIMIT}} MB, extra {{custom}} e {{missing}}';
68
- component.errorParams = { custom: 'X' };
69
- component.ngOnInit();
70
- expect(component.errorMessage).toContain(String(FILE_SIZE_LIMIT));
71
- expect(component.errorMessage).toContain('X');
72
- expect(component.errorMessage).toContain('{{missing}}');
73
- });
74
25
 
75
- it('should leave errorMessage empty when neither key nor plain message is set (widget happy path)', () => {
76
- component.errorKeyMessage = '';
77
- component.errorMessage = '';
78
- component.ngOnInit();
79
- expect(component.errorMessage).toBe('');
80
- fixture.detectChanges();
81
- const content = fixture.nativeElement.querySelector('.alert-content') as HTMLElement;
82
- expect(content.textContent?.trim()).toBe('');
83
- });
84
- });
85
- });
@@ -9,30 +9,21 @@
9
9
 
10
10
  <!-- ******* EYE-CATCHER CLOSE BTN IF IS-MOBILE IS FALSE ******** -->
11
11
  <div class="eye-catcher-card-close-btn-wrapper">
12
- <button type="button"
13
- class="eye-catcher-card-close-btn c21-button-clean"
14
- [attr.aria-label]="g.BUTTON_CLOSE_TO_ICON || 'Close'"
15
- (click)="closeEyeCatcherCard()">
16
- <svg class="eye-catcher-card-close-btn-img" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px">
12
+ <div class="eye-catcher-card-close-btn" (click)="closeEyeCatcherCard()">
13
+ <svg class="eye-catcher-card-close-btn-img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="24px" height="24px">
17
14
  <path d="M0 0h24v24H0V0z" fill="none"/>
18
15
  <path d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"/>
19
16
  </svg>
20
- </button>
17
+ </div>
21
18
  </div>
22
19
 
23
- <button type="button"
24
- class="eye-catcher_column ec_left c21-button-clean"
25
- [attr.aria-label]="g.START_A_CONVERSATION || g.BUTTON_OPEN_CHAT"
26
- (click)="openChatFromEyeCatcherCard()">
20
+ <div class="eye-catcher_column ec_left" (click)="openChatFromEyeCatcherCard()">
27
21
  <div [@rotatedState]='state' class="eye-catcher-card-emoticons-wrapper">
28
22
  <span *ngIf="emoticon" class="chat21-card-emoticons">{{emoticon}}</span>
29
23
  </div>
30
- </button>
24
+ </div>
31
25
 
32
- <button type="button"
33
- class="eye-catcher_column ec_right c21-button-clean"
34
- [attr.aria-label]="g.START_A_CONVERSATION || g.BUTTON_OPEN_CHAT"
35
- (click)="openChatFromEyeCatcherCard()">
26
+ <div class="eye-catcher_column ec_right" (click)="openChatFromEyeCatcherCard()">
36
27
  <div class="eye-catcher-card-text-wrapper">
37
28
  <p class="eye-catcher-card-title">{{title}}</p>
38
29
 
@@ -46,7 +37,7 @@
46
37
  </p>
47
38
  </ng-template>
48
39
  </div>
49
- </button>
40
+ </div>
50
41
 
51
42
  </div>
52
43
 
@@ -12,27 +12,6 @@
12
12
  /* height: 300px; */
13
13
  /* Should be removed. Only for demonstration */
14
14
  }
15
-
16
- button.eye-catcher-card-close-btn {
17
- -webkit-appearance: none;
18
- appearance: none;
19
- border: none;
20
- padding: 0;
21
- display: block;
22
- }
23
-
24
- button.eye-catcher_column {
25
- -webkit-appearance: none;
26
- appearance: none;
27
- border: none;
28
- background: transparent;
29
- text-align: inherit;
30
- font: inherit;
31
- padding: 0;
32
- margin: 0;
33
- cursor: pointer;
34
- color: inherit;
35
- }
36
15
 
37
16
  .ec_right::-webkit-scrollbar {
38
17
  display: none;
@@ -1,10 +1,5 @@
1
1
  import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
2
2
  import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
- import { By } from '@angular/platform-browser';
4
-
5
- import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
6
- import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
7
- import { NGXLogger } from 'ngx-logger';
8
3
  import { Globals } from '../../utils/globals';
9
4
 
10
5
  import { EyeeyeCatcherCardComponent } from './eyeeye-catcher-card.component';
@@ -12,102 +7,25 @@ import { EyeeyeCatcherCardComponent } from './eyeeye-catcher-card.component';
12
7
  describe('EyeeyeCatcherCardComponent', () => {
13
8
  let component: EyeeyeCatcherCardComponent;
14
9
  let fixture: ComponentFixture<EyeeyeCatcherCardComponent>;
15
- let globals: Globals;
16
- const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
17
10
 
18
11
  beforeEach(waitForAsync(() => {
19
- LoggerInstance.setInstance(new CustomLogger(ngxlogger));
20
12
  TestBed.configureTestingModule({
21
- declarations: [EyeeyeCatcherCardComponent],
22
- imports: [BrowserAnimationsModule],
23
- providers: [Globals],
24
- }).compileComponents();
13
+ declarations: [ EyeeyeCatcherCardComponent ],
14
+ imports: [
15
+ BrowserAnimationsModule
16
+ ],
17
+ providers: [ Globals ]
18
+ })
19
+ .compileComponents();
25
20
  }));
26
21
 
27
22
  beforeEach(() => {
28
23
  fixture = TestBed.createComponent(EyeeyeCatcherCardComponent);
29
24
  component = fixture.componentInstance;
30
- globals = TestBed.inject(Globals);
31
- globals.initDefafultParameters();
32
- globals.CALLOUT_TITLE_PLACEHOLDER = '👋 Ciao utente';
33
- globals.calloutTitle = '';
34
- globals.calloutMsg = 'Messaggio callout';
35
- globals.isOpen = false;
36
- globals.calloutStaus = true;
37
- spyOn(globals, 'setParameter').and.callThrough();
38
25
  fixture.detectChanges();
39
26
  });
40
27
 
41
28
  it('should create', () => {
42
29
  expect(component).toBeTruthy();
43
30
  });
44
-
45
- describe('ngOnInit and layout state', () => {
46
- it('should set initial state and hide callout via Globals', () => {
47
- expect(component.state).toBe('default');
48
- expect(globals.setParameter).toHaveBeenCalledWith('displayEyeCatcherCard', 'none');
49
- expect(component.displayEyeCatcherCardCloseBtnWrapper).toBe('none');
50
- });
51
-
52
- it('should render callout card container with alignment class from Globals', () => {
53
- globals.align = 'left';
54
- fixture.detectChanges();
55
- const card = fixture.debugElement.query(By.css('.eye-catcher-card'));
56
- expect(card.nativeElement.classList.contains('c21-align-left')).toBe(true);
57
- });
58
- });
59
-
60
- describe('openEyeCatcher and outputs', () => {
61
- it('when chat closed and callout enabled should show card and emit onCloseEyeCatcherCard(true)', () => {
62
- globals.isOpen = false;
63
- globals.calloutStaus = true;
64
- spyOn(component.onCloseEyeCatcherCard, 'emit');
65
- component.openEyeCatcher();
66
- expect(component.onCloseEyeCatcherCard.emit).toHaveBeenCalledWith(true);
67
- expect(globals.setParameter).toHaveBeenCalledWith('displayEyeCatcherCard', 'block');
68
- expect(component.displayEyeCatcherCardCloseBtnWrapper).toBe('block');
69
- });
70
-
71
- it('when chat already open should not emit or display card', () => {
72
- globals.isOpen = true;
73
- globals.calloutStaus = true;
74
- spyOn(component.onCloseEyeCatcherCard, 'emit');
75
- component.openEyeCatcher();
76
- expect(component.onCloseEyeCatcherCard.emit).not.toHaveBeenCalled();
77
- });
78
-
79
- it('when callout is disabled should not show card even if chat is closed', () => {
80
- globals.isOpen = false;
81
- globals.calloutStaus = false;
82
- spyOn(component.onCloseEyeCatcherCard, 'emit');
83
- component.openEyeCatcher();
84
- expect(component.onCloseEyeCatcherCard.emit).not.toHaveBeenCalled();
85
- expect(component.displayEyeCatcherCardCloseBtnWrapper).toBe('none');
86
- });
87
- });
88
-
89
- describe('checkIsEmoji, openChatFromEyeCatcherCard, closeEyeCatcherCard', () => {
90
- it('checkIsEmoji should split leading emoji into emoticon and title', () => {
91
- globals.calloutTitle = '🚀 Supporto';
92
- component.checkIsEmoji();
93
- expect(component.emoticon).toBe('🚀');
94
- expect(component.title).toContain('Supporto');
95
- });
96
-
97
- it('openChatFromEyeCatcherCard should hide card and emit onOpenChat', () => {
98
- spyOn(component.onOpenChat, 'emit');
99
- component.openChatFromEyeCatcherCard();
100
- expect(globals.setParameter).toHaveBeenCalledWith('displayEyeCatcherCard', 'none');
101
- expect(component.onOpenChat.emit).toHaveBeenCalled();
102
- });
103
-
104
- it('closeEyeCatcherCard should emit false, hide card and disable callout', () => {
105
- spyOn(component.onCloseEyeCatcherCard, 'emit');
106
- component.closeEyeCatcherCard();
107
- expect(component.onCloseEyeCatcherCard.emit).toHaveBeenCalledWith(false);
108
- expect(globals.setParameter).toHaveBeenCalledWith('displayEyeCatcherCard', 'none');
109
- expect(globals.setParameter).toHaveBeenCalledWith('calloutStaus', false, true);
110
- expect(component.displayEyeCatcherCardCloseBtnWrapper).toBe('none');
111
- });
112
- });
113
31
  });
@@ -39,7 +39,7 @@
39
39
  </form>
40
40
 
41
41
  <div class="c21-new-conversation">
42
- <button [tabindex]="lastTabIndex" type="button" class="c21-button-primary"
42
+ <button [tabindex]="lastTabIndex" class="c21-button-primary"
43
43
  (click)="onSubmitPreChatForm()"
44
44
  (keydown.enter)="onEnterButtonPressed($event)"
45
45
  [ngStyle]="{'background-color': stylesMap?.get('themeColor'), 'border-color': stylesMap?.get('themeColor'), 'color': stylesMap?.get('foregroundColor') }">