@servicetitan/titan-chatbot-ui 3.0.0

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 (226) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +15 -0
  3. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.d.ts +2 -0
  4. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.d.ts.map +1 -0
  5. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js +160 -0
  6. package/dist/components/chatbot/__tests-cy__/chatbot-help-center.test.js.map +1 -0
  7. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.d.ts +2 -0
  8. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.d.ts.map +1 -0
  9. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js +90 -0
  10. package/dist/components/chatbot/__tests-cy__/chatbot-live.test.js.map +1 -0
  11. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.d.ts +2 -0
  12. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.d.ts.map +1 -0
  13. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.js +155 -0
  14. package/dist/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.js.map +1 -0
  15. package/dist/components/chatbot/__tests-cy__/chatbot.test.d.ts +2 -0
  16. package/dist/components/chatbot/__tests-cy__/chatbot.test.d.ts.map +1 -0
  17. package/dist/components/chatbot/__tests-cy__/chatbot.test.js +139 -0
  18. package/dist/components/chatbot/__tests-cy__/chatbot.test.js.map +1 -0
  19. package/dist/components/chatbot/chatbot-to-chat-provider-adapter.d.ts +6 -0
  20. package/dist/components/chatbot/chatbot-to-chat-provider-adapter.d.ts.map +1 -0
  21. package/dist/components/chatbot/chatbot-to-chat-provider-adapter.js +21 -0
  22. package/dist/components/chatbot/chatbot-to-chat-provider-adapter.js.map +1 -0
  23. package/dist/components/chatbot/chatbot.d.ts +9 -0
  24. package/dist/components/chatbot/chatbot.d.ts.map +1 -0
  25. package/dist/components/chatbot/chatbot.js +68 -0
  26. package/dist/components/chatbot/chatbot.js.map +1 -0
  27. package/dist/components/chatbot/dialog/chatbot-restart-dialog.d.ts +6 -0
  28. package/dist/components/chatbot/dialog/chatbot-restart-dialog.d.ts.map +1 -0
  29. package/dist/components/chatbot/dialog/chatbot-restart-dialog.js +20 -0
  30. package/dist/components/chatbot/dialog/chatbot-restart-dialog.js.map +1 -0
  31. package/dist/components/chatbot/dialog/chatbot-restart-link.d.ts +4 -0
  32. package/dist/components/chatbot/dialog/chatbot-restart-link.d.ts.map +1 -0
  33. package/dist/components/chatbot/dialog/chatbot-restart-link.js +13 -0
  34. package/dist/components/chatbot/dialog/chatbot-restart-link.js.map +1 -0
  35. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.d.ts +2 -0
  36. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.d.ts.map +1 -0
  37. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.js +65 -0
  38. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.js.map +1 -0
  39. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.d.ts +2 -0
  40. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.d.ts.map +1 -0
  41. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.js +100 -0
  42. package/dist/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.js.map +1 -0
  43. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.d.ts +3 -0
  44. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.d.ts.map +1 -0
  45. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.js +13 -0
  46. package/dist/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.js.map +1 -0
  47. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.d.ts +3 -0
  48. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.d.ts.map +1 -0
  49. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.js +12 -0
  50. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.js.map +1 -0
  51. package/dist/components/chatbot/feedback/chatbot-message-feedback-form.module.less +15 -0
  52. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.d.ts +9 -0
  53. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.d.ts.map +1 -0
  54. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.js +60 -0
  55. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.js.map +1 -0
  56. package/dist/components/chatbot/feedback/chatbot-message-feedback-popover.module.less +10 -0
  57. package/dist/components/chatbot/feedback/chatbot-message-feedback.d.ts +9 -0
  58. package/dist/components/chatbot/feedback/chatbot-message-feedback.d.ts.map +1 -0
  59. package/dist/components/chatbot/feedback/chatbot-message-feedback.js +17 -0
  60. package/dist/components/chatbot/feedback/chatbot-message-feedback.js.map +1 -0
  61. package/dist/components/chatbot/feedback/chatbot-session-feedback-link.d.ts +4 -0
  62. package/dist/components/chatbot/feedback/chatbot-session-feedback-link.d.ts.map +1 -0
  63. package/dist/components/chatbot/feedback/chatbot-session-feedback-link.js +16 -0
  64. package/dist/components/chatbot/feedback/chatbot-session-feedback-link.js.map +1 -0
  65. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.d.ts +5 -0
  66. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.d.ts.map +1 -0
  67. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.js +24 -0
  68. package/dist/components/chatbot/feedback/chatbot-session-feedback-modal.js.map +1 -0
  69. package/dist/components/chatbot/filters/chatbot-filter.d.ts +8 -0
  70. package/dist/components/chatbot/filters/chatbot-filter.d.ts.map +1 -0
  71. package/dist/components/chatbot/filters/chatbot-filter.js +61 -0
  72. package/dist/components/chatbot/filters/chatbot-filter.js.map +1 -0
  73. package/dist/components/chatbot/filters/chatbot-filter.module.css +19 -0
  74. package/dist/components/chatbot/filters/chatbot-filters.d.ts +5 -0
  75. package/dist/components/chatbot/filters/chatbot-filters.d.ts.map +1 -0
  76. package/dist/components/chatbot/filters/chatbot-filters.js +11 -0
  77. package/dist/components/chatbot/filters/chatbot-filters.js.map +1 -0
  78. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.d.ts +2 -0
  79. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.d.ts.map +1 -0
  80. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.js +85 -0
  81. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.js.map +1 -0
  82. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.d.ts +2 -0
  83. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.d.ts.map +1 -0
  84. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.js +57 -0
  85. package/dist/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.js.map +1 -0
  86. package/dist/components/chatbot/messages/chatbot-links.d.ts +15 -0
  87. package/dist/components/chatbot/messages/chatbot-links.d.ts.map +1 -0
  88. package/dist/components/chatbot/messages/chatbot-links.js +21 -0
  89. package/dist/components/chatbot/messages/chatbot-links.js.map +1 -0
  90. package/dist/components/chatbot/messages/chatbot-links.module.less +22 -0
  91. package/dist/components/chatbot/messages/chatbot-message-agent-footer.d.ts +8 -0
  92. package/dist/components/chatbot/messages/chatbot-message-agent-footer.d.ts.map +1 -0
  93. package/dist/components/chatbot/messages/chatbot-message-agent-footer.js +13 -0
  94. package/dist/components/chatbot/messages/chatbot-message-agent-footer.js.map +1 -0
  95. package/dist/components/chatbot/messages/chatbot-message-answer-readonly.d.ts +6 -0
  96. package/dist/components/chatbot/messages/chatbot-message-answer-readonly.d.ts.map +1 -0
  97. package/dist/components/chatbot/messages/chatbot-message-answer-readonly.js +17 -0
  98. package/dist/components/chatbot/messages/chatbot-message-answer-readonly.js.map +1 -0
  99. package/dist/components/chatbot/messages/chatbot-message-answer.d.ts +6 -0
  100. package/dist/components/chatbot/messages/chatbot-message-answer.d.ts.map +1 -0
  101. package/dist/components/chatbot/messages/chatbot-message-answer.js +29 -0
  102. package/dist/components/chatbot/messages/chatbot-message-answer.js.map +1 -0
  103. package/dist/components/chatbot/messages/chatbot-message-answer.module.less +3 -0
  104. package/dist/components/chatbot/messages/chatbot-message-timeout.d.ts +6 -0
  105. package/dist/components/chatbot/messages/chatbot-message-timeout.d.ts.map +1 -0
  106. package/dist/components/chatbot/messages/chatbot-message-timeout.js +17 -0
  107. package/dist/components/chatbot/messages/chatbot-message-timeout.js.map +1 -0
  108. package/dist/components/chatbot/messages/chatbot-message-typing.d.ts +8 -0
  109. package/dist/components/chatbot/messages/chatbot-message-typing.d.ts.map +1 -0
  110. package/dist/components/chatbot/messages/chatbot-message-typing.js +27 -0
  111. package/dist/components/chatbot/messages/chatbot-message-typing.js.map +1 -0
  112. package/dist/components/chatbot/messages/chatbot-message-welcome.d.ts +3 -0
  113. package/dist/components/chatbot/messages/chatbot-message-welcome.d.ts.map +1 -0
  114. package/dist/components/chatbot/messages/chatbot-message-welcome.js +4 -0
  115. package/dist/components/chatbot/messages/chatbot-message-welcome.js.map +1 -0
  116. package/dist/components/chatbot/templates/chatbot-message-template-agent-readonly.d.ts +6 -0
  117. package/dist/components/chatbot/templates/chatbot-message-template-agent-readonly.d.ts.map +1 -0
  118. package/dist/components/chatbot/templates/chatbot-message-template-agent-readonly.js +12 -0
  119. package/dist/components/chatbot/templates/chatbot-message-template-agent-readonly.js.map +1 -0
  120. package/dist/components/chatbot/templates/chatbot-message-template-agent.d.ts +6 -0
  121. package/dist/components/chatbot/templates/chatbot-message-template-agent.d.ts.map +1 -0
  122. package/dist/components/chatbot/templates/chatbot-message-template-agent.js +10 -0
  123. package/dist/components/chatbot/templates/chatbot-message-template-agent.js.map +1 -0
  124. package/dist/components/chatbot/templates/chatbot-message-template-user-readonly.d.ts +6 -0
  125. package/dist/components/chatbot/templates/chatbot-message-template-user-readonly.d.ts.map +1 -0
  126. package/dist/components/chatbot/templates/chatbot-message-template-user-readonly.js +7 -0
  127. package/dist/components/chatbot/templates/chatbot-message-template-user-readonly.js.map +1 -0
  128. package/dist/index.d.ts +6 -0
  129. package/dist/index.d.ts.map +1 -0
  130. package/dist/index.js +6 -0
  131. package/dist/index.js.map +1 -0
  132. package/dist/stores/__tests__/message-feedback-guardrail.store.test.d.ts +2 -0
  133. package/dist/stores/__tests__/message-feedback-guardrail.store.test.d.ts.map +1 -0
  134. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js +49 -0
  135. package/dist/stores/__tests__/message-feedback-guardrail.store.test.js.map +1 -0
  136. package/dist/stores/__tests__/message-feedback.store.test.d.ts +2 -0
  137. package/dist/stores/__tests__/message-feedback.store.test.d.ts.map +1 -0
  138. package/dist/stores/__tests__/message-feedback.store.test.js +114 -0
  139. package/dist/stores/__tests__/message-feedback.store.test.js.map +1 -0
  140. package/dist/stores/__tests__/session-feedback.store.test.d.ts +2 -0
  141. package/dist/stores/__tests__/session-feedback.store.test.d.ts.map +1 -0
  142. package/dist/stores/__tests__/session-feedback.store.test.js +39 -0
  143. package/dist/stores/__tests__/session-feedback.store.test.js.map +1 -0
  144. package/dist/stores/message-feedback-base.store.d.ts +8 -0
  145. package/dist/stores/message-feedback-base.store.d.ts.map +1 -0
  146. package/dist/stores/message-feedback-base.store.js +2 -0
  147. package/dist/stores/message-feedback-base.store.js.map +1 -0
  148. package/dist/stores/message-feedback-guardrail.store.d.ts +16 -0
  149. package/dist/stores/message-feedback-guardrail.store.d.ts.map +1 -0
  150. package/dist/stores/message-feedback-guardrail.store.js +85 -0
  151. package/dist/stores/message-feedback-guardrail.store.js.map +1 -0
  152. package/dist/stores/message-feedback.store.d.ts +23 -0
  153. package/dist/stores/message-feedback.store.d.ts.map +1 -0
  154. package/dist/stores/message-feedback.store.js +145 -0
  155. package/dist/stores/message-feedback.store.js.map +1 -0
  156. package/dist/stores/session-feedback.store.d.ts +15 -0
  157. package/dist/stores/session-feedback.store.d.ts.map +1 -0
  158. package/dist/stores/session-feedback.store.js +75 -0
  159. package/dist/stores/session-feedback.store.js.map +1 -0
  160. package/dist/utils/__tests__/axios-utils.test.d.ts +2 -0
  161. package/dist/utils/__tests__/axios-utils.test.d.ts.map +1 -0
  162. package/dist/utils/__tests__/axios-utils.test.js +33 -0
  163. package/dist/utils/__tests__/axios-utils.test.js.map +1 -0
  164. package/dist/utils/axios-utils.d.ts +5 -0
  165. package/dist/utils/axios-utils.d.ts.map +1 -0
  166. package/dist/utils/axios-utils.js +23 -0
  167. package/dist/utils/axios-utils.js.map +1 -0
  168. package/dist/utils/test-utils.d.ts +5 -0
  169. package/dist/utils/test-utils.d.ts.map +1 -0
  170. package/dist/utils/test-utils.js +17 -0
  171. package/dist/utils/test-utils.js.map +1 -0
  172. package/package.json +59 -0
  173. package/src/components/chatbot/__tests-cy__/chatbot-help-center.test.tsx +210 -0
  174. package/src/components/chatbot/__tests-cy__/chatbot-live.test.tsx +120 -0
  175. package/src/components/chatbot/__tests-cy__/chatbot-titan-chatbot.test.tsx +206 -0
  176. package/src/components/chatbot/__tests-cy__/chatbot.test.tsx +181 -0
  177. package/src/components/chatbot/chatbot-to-chat-provider-adapter.tsx +36 -0
  178. package/src/components/chatbot/chatbot.tsx +95 -0
  179. package/src/components/chatbot/dialog/chatbot-restart-dialog.tsx +36 -0
  180. package/src/components/chatbot/dialog/chatbot-restart-link.tsx +26 -0
  181. package/src/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form-guardrail.test.tsx +89 -0
  182. package/src/components/chatbot/feedback/__tests-cy__/chatbot-message-feedback-form.test.tsx +131 -0
  183. package/src/components/chatbot/feedback/chatbot-message-feedback-form-guardrail.tsx +35 -0
  184. package/src/components/chatbot/feedback/chatbot-message-feedback-form.module.less +15 -0
  185. package/src/components/chatbot/feedback/chatbot-message-feedback-form.module.less.d.ts +4 -0
  186. package/src/components/chatbot/feedback/chatbot-message-feedback-form.tsx +65 -0
  187. package/src/components/chatbot/feedback/chatbot-message-feedback-popover.module.less +10 -0
  188. package/src/components/chatbot/feedback/chatbot-message-feedback-popover.module.less.d.ts +4 -0
  189. package/src/components/chatbot/feedback/chatbot-message-feedback-popover.tsx +200 -0
  190. package/src/components/chatbot/feedback/chatbot-message-feedback.tsx +27 -0
  191. package/src/components/chatbot/feedback/chatbot-session-feedback-link.tsx +29 -0
  192. package/src/components/chatbot/feedback/chatbot-session-feedback-modal.tsx +96 -0
  193. package/src/components/chatbot/filters/chatbot-filter.module.css +19 -0
  194. package/src/components/chatbot/filters/chatbot-filter.module.css.d.ts +5 -0
  195. package/src/components/chatbot/filters/chatbot-filter.tsx +160 -0
  196. package/src/components/chatbot/filters/chatbot-filters.tsx +17 -0
  197. package/src/components/chatbot/messages/__tests-cy__/chatbot-message-answer.test.tsx +110 -0
  198. package/src/components/chatbot/messages/__tests-cy__/chatbot-message-typing.test.tsx +71 -0
  199. package/src/components/chatbot/messages/chatbot-links.module.less +22 -0
  200. package/src/components/chatbot/messages/chatbot-links.module.less.d.ts +4 -0
  201. package/src/components/chatbot/messages/chatbot-links.tsx +76 -0
  202. package/src/components/chatbot/messages/chatbot-message-agent-footer.tsx +35 -0
  203. package/src/components/chatbot/messages/chatbot-message-answer-readonly.tsx +39 -0
  204. package/src/components/chatbot/messages/chatbot-message-answer.module.less +3 -0
  205. package/src/components/chatbot/messages/chatbot-message-answer.module.less.d.ts +3 -0
  206. package/src/components/chatbot/messages/chatbot-message-answer.tsx +55 -0
  207. package/src/components/chatbot/messages/chatbot-message-timeout.tsx +20 -0
  208. package/src/components/chatbot/messages/chatbot-message-typing.tsx +39 -0
  209. package/src/components/chatbot/messages/chatbot-message-welcome.tsx +16 -0
  210. package/src/components/chatbot/templates/chatbot-message-template-agent-readonly.tsx +25 -0
  211. package/src/components/chatbot/templates/chatbot-message-template-agent.tsx +25 -0
  212. package/src/components/chatbot/templates/chatbot-message-template-user-readonly.tsx +16 -0
  213. package/src/cypress.d.ts +10 -0
  214. package/src/index.ts +5 -0
  215. package/src/stores/__tests__/message-feedback-guardrail.store.test.ts +61 -0
  216. package/src/stores/__tests__/message-feedback.store.test.ts +121 -0
  217. package/src/stores/__tests__/session-feedback.store.test.ts +47 -0
  218. package/src/stores/message-feedback-base.store.ts +8 -0
  219. package/src/stores/message-feedback-guardrail.store.ts +60 -0
  220. package/src/stores/message-feedback.store.ts +113 -0
  221. package/src/stores/session-feedback.store.ts +44 -0
  222. package/src/utils/__tests__/axios-utils.test.ts +40 -0
  223. package/src/utils/axios-utils.ts +25 -0
  224. package/src/utils/test-utils.ts +22 -0
  225. package/tsconfig.json +25 -0
  226. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,181 @@
1
+ import { ILog, Log } from '@servicetitan/log-service';
2
+ import { Container, Provider } from '@servicetitan/react-ioc';
3
+ import {
4
+ CHATBOT_API_CLIENT,
5
+ CHATBOT_UI_BACKEND_STORE_TOKEN,
6
+ CHATBOT_UI_STORE_TOKEN,
7
+ ChatbotCustomizations,
8
+ ChatbotUiBackendStore,
9
+ ChatbotUiStore,
10
+ IChatbotApiClient,
11
+ IChatbotUiBackendStore,
12
+ IChatbotUiStore,
13
+ ModelsMocks,
14
+ } from '@servicetitan/titan-chatbot-api';
15
+ import { ChatUiSelectors, CypressMocks } from '@servicetitan/titan-chatbot-ui-cypress';
16
+ import { mount } from 'cypress/react';
17
+ import { FC, useMemo } from 'react';
18
+ import { Chatbot } from '../chatbot';
19
+
20
+ const initContainer = () => {
21
+ const rootContainer = new Container();
22
+ const container = new Container();
23
+ container.parent = rootContainer;
24
+ container.bind<ILog>(Log).to(CypressMocks.LogMock).inSingletonScope();
25
+ container
26
+ .bind<IChatbotApiClient>(CHATBOT_API_CLIENT)
27
+ .to(CypressMocks.ChatbotApiClientMock)
28
+ .inSingletonScope();
29
+ container.bind<IChatbotUiStore>(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
30
+ container
31
+ .bind<IChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN)
32
+ .to(ChatbotUiBackendStore)
33
+ .inSingletonScope();
34
+ return container;
35
+ };
36
+
37
+ describe('[Chatbot]', () => {
38
+ let container: Container;
39
+ let log: CypressMocks.LogMock;
40
+ let api: CypressMocks.ChatbotApiClientMock;
41
+ let uiStore: ChatbotUiStore;
42
+ let uiBackendStore: ChatbotUiBackendStore;
43
+
44
+ const mockApi = () => {
45
+ api.postSession = cy.stub().resolves(ModelsMocks.mockSession());
46
+ api.getOptions = cy.stub().resolves(ModelsMocks.mockFrontendModel());
47
+ };
48
+
49
+ beforeEach(() => {
50
+ container = initContainer();
51
+ log = container.get<CypressMocks.LogMock>(Log);
52
+ api = container.get<CypressMocks.ChatbotApiClientMock>(CHATBOT_API_CLIENT);
53
+ uiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
54
+ uiBackendStore = container.get<ChatbotUiBackendStore>(CHATBOT_UI_BACKEND_STORE_TOKEN);
55
+ mockApi();
56
+ cy.viewport(550, 800);
57
+ cy.clock(Date.parse('2023-10-01T00:00:00Z'));
58
+ });
59
+
60
+ const mockAskBotApi = (answer: string) => {
61
+ api.postMessage = cy.stub().as('askBot').resolves(
62
+ ModelsMocks.mockBotMessage({
63
+ answer,
64
+ })
65
+ );
66
+ return '@askBot';
67
+ };
68
+
69
+ const render = () => {
70
+ const ChatbotWrapper: FC = () => {
71
+ const customizationContext = useMemo<ChatbotCustomizations>(
72
+ () => ({
73
+ filters: { enabled: true },
74
+ feedback: { title: 'TITLE' },
75
+ }),
76
+ []
77
+ );
78
+ return (
79
+ <Provider
80
+ singletons={[
81
+ {
82
+ provide: Log,
83
+ useValue: log,
84
+ },
85
+ { provide: CHATBOT_API_CLIENT, useValue: api },
86
+ {
87
+ provide: CHATBOT_UI_STORE_TOKEN,
88
+ useValue: uiStore,
89
+ },
90
+ {
91
+ provide: CHATBOT_UI_BACKEND_STORE_TOKEN,
92
+ useValue: uiBackendStore,
93
+ },
94
+ ]}
95
+ >
96
+ <Chatbot
97
+ className="h-100vh max-h-100vh of-x-hidden"
98
+ customizations={customizationContext}
99
+ />
100
+ </Provider>
101
+ );
102
+ };
103
+ cy.spy(uiStore, 'run').as('runSpy');
104
+ mount(<ChatbotWrapper />);
105
+ return cy.wrap(
106
+ new Promise(resolve => {
107
+ cy.get('@runSpy')
108
+ .should('have.been.calledOnce')
109
+ .then((invocation: any) => {
110
+ const initPromise = invocation.firstCall.returnValue as ReturnType<
111
+ IChatbotUiStore['run']
112
+ >;
113
+ initPromise.then(resolve);
114
+ });
115
+ })
116
+ );
117
+ };
118
+
119
+ const ask = (message: string) => {
120
+ ChatUiSelectors.chatInput.type(`${message}{enter}`, { delay: 0 });
121
+ };
122
+
123
+ const triggerFile = () => {
124
+ uiStore.setFilePickerEnabled(true);
125
+ ChatUiSelectors.chatUploadFile.should('be.visible');
126
+ };
127
+
128
+ it('should render the chatbot component', () => {
129
+ const apiAnswerAlias = mockAskBotApi("Hello! I'm chatbot mocked response!!!");
130
+ render().then(() => {
131
+ // Pick filter
132
+ ChatUiSelectors.chatbotFilterButton.first().click();
133
+ ChatUiSelectors.chatbotFilterOptions.find('label').first().click();
134
+ ChatUiSelectors.chatbotFilterButton.first().click();
135
+
136
+ // Ask chatbot
137
+ ask('Hello, Chatbot!');
138
+ ask('Hello, Chatbot! Hello, Chatbot! Hello, Chatbot! Hello, Chatbot! Hello, Chatbot!');
139
+ triggerFile();
140
+
141
+ // Check if the message is sent
142
+ cy.get(apiAnswerAlias).should('have.been.calledTwice');
143
+
144
+ cy.tick(1000);
145
+ });
146
+ });
147
+
148
+ describe('with error handling', () => {
149
+ const mockAskBotApiError = (errorMessage: string) => {
150
+ api.postMessage = cy.stub().as('askBot').rejects(new Error(errorMessage));
151
+ return '@askBot';
152
+ };
153
+
154
+ it('should react on send message API error', () => {
155
+ mockAskBotApiError('Chatbot API error');
156
+ render()
157
+ .then(() => {
158
+ ask('Hello, Chatbot!');
159
+
160
+ ChatUiSelectors.chatMessageFooter
161
+ .last()
162
+ .should('be.visible')
163
+ .should('contain.text', 'Message not delivered. Retry');
164
+ const errorBanner = ChatUiSelectors.chatError.should('be.visible');
165
+ errorBanner
166
+ .find('.Banner__text > .Banner__title')
167
+ .should('contain.text', 'Chat Error');
168
+ ChatUiSelectors.chatErrorText
169
+ .should('be.visible')
170
+ .should('contain.text', 'Failed to send message');
171
+ })
172
+ .then(() => {
173
+ mockAskBotApi("Hello! I'm chatbot mocked response!!!");
174
+ ChatUiSelectors.chatMessageErrorRetry.click();
175
+ ChatUiSelectors.chatMessageContentAgent
176
+ .should('be.visible')
177
+ .should('contain.text', "Hello! I'm chatbot mocked response!!!");
178
+ });
179
+ });
180
+ });
181
+ });
@@ -0,0 +1,36 @@
1
+ import { Provider, useDependencies } from '@servicetitan/react-ioc';
2
+ import {
3
+ CHAT_UI_BACKEND_STORE_TOKEN,
4
+ CHAT_UI_STORE_TOKEN,
5
+ } from '@servicetitan/titan-chat-ui-common';
6
+ import {
7
+ CHATBOT_UI_BACKEND_STORE_TOKEN,
8
+ CHATBOT_UI_STORE_TOKEN,
9
+ } from '@servicetitan/titan-chatbot-api';
10
+ import { FC, PropsWithChildren } from 'react';
11
+
12
+ /**
13
+ * Passes the Chatbot UI store and backend store to the Chat UI providers.
14
+ */
15
+ export const ChatbotToChatProviderAdapter: FC<PropsWithChildren> = ({ children }) => {
16
+ const [chatbotUiStore, chatbotUiBackendStore] = useDependencies(
17
+ CHATBOT_UI_STORE_TOKEN,
18
+ CHATBOT_UI_BACKEND_STORE_TOKEN
19
+ );
20
+ return (
21
+ <Provider
22
+ singletons={[
23
+ {
24
+ provide: CHAT_UI_STORE_TOKEN,
25
+ useValue: chatbotUiStore,
26
+ },
27
+ {
28
+ provide: CHAT_UI_BACKEND_STORE_TOKEN,
29
+ useValue: chatbotUiBackendStore,
30
+ },
31
+ ]}
32
+ >
33
+ {children}
34
+ </Provider>
35
+ );
36
+ };
@@ -0,0 +1,95 @@
1
+ import { Stack } from '@servicetitan/design-system';
2
+ import { useDependencies } from '@servicetitan/react-ioc';
3
+ import { Chat } from '@servicetitan/titan-chat-ui';
4
+ import {
5
+ ChatMessageModelText,
6
+ ChatMessageModelTimeout,
7
+ ChatMessageModelWelcome,
8
+ ChatParticipantIcon,
9
+ } from '@servicetitan/titan-chat-ui-common';
10
+ import {
11
+ CHATBOT_UI_BACKEND_STORE_TOKEN,
12
+ CHATBOT_UI_STORE_TOKEN,
13
+ ChatbotCustomizations,
14
+ } from '@servicetitan/titan-chatbot-api';
15
+ import { observer } from 'mobx-react';
16
+ import { FC, useEffect, useMemo } from 'react';
17
+ import { ChatbotToChatProviderAdapter } from './chatbot-to-chat-provider-adapter';
18
+ import { ChatFilters } from './filters/chatbot-filters';
19
+ import { ChatbotMessageAnswer } from './messages/chatbot-message-answer';
20
+ import { ChatbotMessageTimeout } from './messages/chatbot-message-timeout';
21
+ import { ChatbotMessageTyping } from './messages/chatbot-message-typing';
22
+ import { ChatbotMessageWelcome } from './messages/chatbot-message-welcome';
23
+ import { ChatbotMessageTemplateAgent } from './templates/chatbot-message-template-agent';
24
+
25
+ export interface IChatbotProps {
26
+ className?: string;
27
+ customizations?: ChatbotCustomizations;
28
+ onReady?: () => void;
29
+ }
30
+
31
+ export const Chatbot: FC<IChatbotProps> = observer(({ className, customizations, onReady }) => {
32
+ const [chatUiStore, chatUiBackendStore] = useDependencies(
33
+ CHATBOT_UI_STORE_TOKEN,
34
+ CHATBOT_UI_BACKEND_STORE_TOKEN
35
+ );
36
+ useEffect(() => {
37
+ const init = async () => {
38
+ chatUiBackendStore.subscribe();
39
+ await chatUiStore.run({
40
+ agentName: 'Titan',
41
+ agentIcon: ChatParticipantIcon.Bot,
42
+ });
43
+ onReady?.();
44
+ };
45
+ init().then(() => {});
46
+ return () => chatUiBackendStore.unsubscribe();
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, []);
49
+
50
+ const customizationsMerged = useMemo<ChatbotCustomizations>(() => {
51
+ const templateCustomizations = customizations?.messageTemplates ?? {};
52
+ templateCustomizations.agent = templateCustomizations.agent ?? {
53
+ predicate: message => message.participant.isAgent,
54
+ component: ChatbotMessageTemplateAgent,
55
+ };
56
+ return {
57
+ error: customizations?.error,
58
+ filters: customizations?.filters,
59
+ feedback: customizations?.feedback,
60
+ footerComponent: customizations?.footerComponent,
61
+ messageTyping: customizations?.messageTyping ?? {
62
+ component: ChatbotMessageTyping,
63
+ },
64
+ messages: [
65
+ ...(customizations?.messages ?? []),
66
+ {
67
+ predicate: (message: ChatMessageModelWelcome) => message.type === 'welcome',
68
+ component: ChatbotMessageWelcome,
69
+ },
70
+ {
71
+ predicate: (message: ChatMessageModelText) =>
72
+ message.participant.isAgent && message.type === 'message',
73
+ component: ChatbotMessageAnswer,
74
+ },
75
+ {
76
+ predicate: (message: ChatMessageModelTimeout) => message.type === 'timeout',
77
+ component: ChatbotMessageTimeout,
78
+ isSystem: true,
79
+ },
80
+ ],
81
+ messageTemplates: templateCustomizations,
82
+ };
83
+ }, [customizations]);
84
+
85
+ return (
86
+ <ChatbotToChatProviderAdapter>
87
+ <Stack direction="column" className={className}>
88
+ <Chat className="flex-grow-1 h-0" customizations={customizationsMerged} />
89
+ {Boolean(customizationsMerged.filters?.enabled) && (
90
+ <ChatFilters className="p-x-3 p-b-2" />
91
+ )}
92
+ </Stack>
93
+ </ChatbotToChatProviderAdapter>
94
+ );
95
+ });
@@ -0,0 +1,36 @@
1
+ import { Dialog } from '@servicetitan/design-system';
2
+ import { useDependencies } from '@servicetitan/react-ioc';
3
+ import { CHATBOT_UI_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';
4
+ import { FC, useCallback, useState } from 'react';
5
+
6
+ export const ChatbotRestartDialog: FC<{ confirmText: string; onClose: () => void }> = ({
7
+ confirmText,
8
+ onClose,
9
+ }) => {
10
+ const [isRestartDialogOpen, setIsRestartDialogOpen] = useState(true);
11
+ const [chatbotUiStore] = useDependencies(CHATBOT_UI_STORE_TOKEN);
12
+
13
+ const handleCloseDialog = useCallback(() => {
14
+ setIsRestartDialogOpen(false);
15
+ onClose();
16
+ }, [onClose]);
17
+
18
+ const handleRestart = useCallback(() => {
19
+ setIsRestartDialogOpen(false);
20
+ onClose();
21
+ chatbotUiStore.restart();
22
+ }, [onClose, chatbotUiStore]);
23
+
24
+ return (
25
+ <Dialog
26
+ open={isRestartDialogOpen}
27
+ portal
28
+ onPrimaryActionClick={handleRestart}
29
+ primaryActionName="Restart"
30
+ onSecondaryActionClick={handleCloseDialog}
31
+ secondaryActionName="Cancel"
32
+ >
33
+ {confirmText}
34
+ </Dialog>
35
+ );
36
+ };
@@ -0,0 +1,26 @@
1
+ import { Link, LinkProps } from '@servicetitan/design-system';
2
+ import { FC, Fragment, useCallback, useState } from 'react';
3
+ import { ChatbotRestartDialog } from './chatbot-restart-dialog';
4
+
5
+ export const ChatbotRestartLink: FC<LinkProps> = ({ children, onClick, ...rest }) => {
6
+ const [isRestartDialogOpen, setIsRestartDialogOpen] = useState(false);
7
+
8
+ const handleRestartOpen = useCallback(() => {
9
+ setIsRestartDialogOpen(true);
10
+ onClick?.();
11
+ }, [onClick]);
12
+
13
+ return (
14
+ <Fragment>
15
+ <Link {...rest} onClick={handleRestartOpen} data-cy="titan-chatbot-restart-session">
16
+ {children}
17
+ </Link>
18
+ {isRestartDialogOpen && (
19
+ <ChatbotRestartDialog
20
+ confirmText="Restart Titan session?"
21
+ onClose={() => setIsRestartDialogOpen(false)}
22
+ />
23
+ )}
24
+ </Fragment>
25
+ );
26
+ };
@@ -0,0 +1,89 @@
1
+ import { Button } from '@servicetitan/design-system';
2
+ import { Container, Provider, useDependencies } from '@servicetitan/react-ioc';
3
+ import { ChatUiSelectors } from '@servicetitan/titan-chatbot-ui-cypress';
4
+ import { mount } from 'cypress/react';
5
+ import { FC } from 'react';
6
+ import { MessageFeedbackGuardrailStore } from '../../../../stores/message-feedback-guardrail.store';
7
+ import { ChatbotMessageFeedbackFormGuardrail } from '../chatbot-message-feedback-form-guardrail';
8
+
9
+ const initContainer = () => {
10
+ const rootContainer = new Container();
11
+ const container = new Container();
12
+ container.parent = rootContainer;
13
+ container.bind(MessageFeedbackGuardrailStore).toSelf().inSingletonScope();
14
+ return container;
15
+ };
16
+
17
+ describe('[ChatbotMessageFeedbackFormGuardrail]', () => {
18
+ let container: Container;
19
+ let messageFeedbackGuardrailStore: MessageFeedbackGuardrailStore;
20
+
21
+ beforeEach(() => {
22
+ container = initContainer();
23
+ messageFeedbackGuardrailStore = container.get(MessageFeedbackGuardrailStore);
24
+ cy.viewport(780, 800);
25
+ });
26
+
27
+ const getFormError = () => cy.get('[data-anvil-component="Banner"]');
28
+ const checkValid = () => {
29
+ cy.getCy('validator').click();
30
+ getFormError().should('not.exist');
31
+ };
32
+ const checkInvalid = () => {
33
+ cy.getCy('validator').click();
34
+ getFormError().should('be.visible');
35
+ };
36
+
37
+ const render = () => {
38
+ const Wrapper: FC = () => {
39
+ const Validator: FC = () => {
40
+ const [formStore] = useDependencies(MessageFeedbackGuardrailStore);
41
+ return (
42
+ <Button
43
+ className="m-t-3"
44
+ data-cy="validator"
45
+ onClick={() => formStore.formState.validate()}
46
+ >
47
+ Validate
48
+ </Button>
49
+ );
50
+ };
51
+ return (
52
+ <Provider
53
+ singletons={[
54
+ {
55
+ provide: MessageFeedbackGuardrailStore,
56
+ useValue: messageFeedbackGuardrailStore,
57
+ },
58
+ ]}
59
+ >
60
+ <ChatbotMessageFeedbackFormGuardrail />
61
+ <Validator />
62
+ </Provider>
63
+ );
64
+ };
65
+ mount(<Wrapper />);
66
+ };
67
+
68
+ it('should render form', () => {
69
+ render();
70
+
71
+ ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
72
+ ChatUiSelectors.chatbotMessageFeedbackLinkUrl.should('be.visible');
73
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.should('be.visible');
74
+ checkInvalid();
75
+ getFormError().find('.Banner__title').should('contain.text', 'Form Incomplete');
76
+ getFormError()
77
+ .find('ul > li')
78
+ .should('contain.text', 'At least one input must be provided.');
79
+
80
+ ChatUiSelectors.chatbotMessageFeedbackLinkUrl.type('https://example.com');
81
+ checkValid();
82
+
83
+ ChatUiSelectors.chatbotMessageFeedbackLinkUrl.clear();
84
+ checkInvalid();
85
+
86
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.type('Test comment');
87
+ checkValid();
88
+ });
89
+ });
@@ -0,0 +1,131 @@
1
+ import { Button } from '@servicetitan/design-system';
2
+ import { Container, Provider, useDependencies } from '@servicetitan/react-ioc';
3
+ import {
4
+ CHATBOT_UI_STORE_TOKEN,
5
+ ChatbotUiStore,
6
+ IChatbotUiStore,
7
+ } from '@servicetitan/titan-chatbot-api';
8
+ import { ChatUiSelectors } from '@servicetitan/titan-chatbot-ui-cypress';
9
+ import { mount } from 'cypress/react';
10
+ import { FC } from 'react';
11
+ import { MessageFeedbackStore } from '../../../../stores/message-feedback.store';
12
+ import { ChatbotMessageFeedbackForm } from '../chatbot-message-feedback-form';
13
+
14
+ const initContainer = () => {
15
+ const rootContainer = new Container();
16
+ const container = new Container();
17
+ container.parent = rootContainer;
18
+ container.bind<IChatbotUiStore>(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
19
+ container.bind<MessageFeedbackStore>(MessageFeedbackStore).toSelf().inSingletonScope();
20
+ return container;
21
+ };
22
+
23
+ describe('[ChatbotMessageFeedbackForm]', () => {
24
+ let container: Container;
25
+ let uiStore: ChatbotUiStore;
26
+ let messageFeedbackStore: MessageFeedbackStore;
27
+
28
+ beforeEach(() => {
29
+ container = initContainer();
30
+ uiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
31
+ messageFeedbackStore = container.get<MessageFeedbackStore>(MessageFeedbackStore);
32
+ cy.viewport(780, 800);
33
+ cy.clock(Date.parse('2023-10-01T00:00:00Z'));
34
+ });
35
+
36
+ const getFormError = () => ChatUiSelectors.chatbotMessageFeedback.find('.FormField__errorText');
37
+ const checkValid = () => {
38
+ cy.getCy('validator').click();
39
+ getFormError().should('not.exist');
40
+ };
41
+ const checkInvalid = () => {
42
+ cy.getCy('validator').click();
43
+ getFormError().should('be.visible');
44
+ };
45
+
46
+ const render = () => {
47
+ const Wrapper: FC = () => {
48
+ const Validator: FC = () => {
49
+ const [formStore] = useDependencies(MessageFeedbackStore);
50
+ return (
51
+ <Button
52
+ className="m-t-3"
53
+ data-cy="validator"
54
+ onClick={() => formStore.formState.validate()}
55
+ >
56
+ Validate
57
+ </Button>
58
+ );
59
+ };
60
+ return (
61
+ <Provider
62
+ singletons={[
63
+ {
64
+ provide: CHATBOT_UI_STORE_TOKEN,
65
+ useValue: uiStore,
66
+ },
67
+ {
68
+ provide: MessageFeedbackStore,
69
+ useValue: messageFeedbackStore,
70
+ },
71
+ ]}
72
+ >
73
+ <ChatbotMessageFeedbackForm />
74
+ <Validator />
75
+ </Provider>
76
+ );
77
+ };
78
+ mount(<Wrapper />);
79
+ };
80
+
81
+ it('should render default state external', () => {
82
+ render();
83
+
84
+ // Default render
85
+ ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
86
+ ChatUiSelectors.chatbotMessageFeedbackUnrelated.should('be.visible');
87
+ ChatUiSelectors.chatbotMessageFeedbackUnclear.should('be.visible');
88
+ ChatUiSelectors.chatbotMessageFeedbackNotFull.should('be.visible');
89
+ ChatUiSelectors.chatbotMessageFeedbackIncorrect.should('be.visible');
90
+ ChatUiSelectors.chatbotMessageFeedbackOther.should('be.visible');
91
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.should('not.exist');
92
+ checkValid();
93
+
94
+ // Click 'other' and check comment textarea
95
+ ChatUiSelectors.chatbotMessageFeedbackOther.click();
96
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment
97
+ .should('be.visible')
98
+ .type('Test comment');
99
+ getFormError().should('not.exist');
100
+ checkValid();
101
+
102
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.clear().blur();
103
+ getFormError().should('be.visible').should('contain.text', 'Enter details.');
104
+ checkInvalid();
105
+ });
106
+
107
+ it('should render default state internal', () => {
108
+ uiStore.customizations.feedback = {
109
+ isCommentAlwaysRequired: true,
110
+ };
111
+ render();
112
+
113
+ // Default render
114
+ ChatUiSelectors.chatbotMessageFeedback.should('be.visible');
115
+ ChatUiSelectors.chatbotMessageFeedbackUnrelated.should('be.visible');
116
+ ChatUiSelectors.chatbotMessageFeedbackUnclear.should('be.visible');
117
+ ChatUiSelectors.chatbotMessageFeedbackNotFull.should('be.visible');
118
+ ChatUiSelectors.chatbotMessageFeedbackIncorrect.should('be.visible');
119
+ ChatUiSelectors.chatbotMessageFeedbackOther.should('be.visible');
120
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.should('be.visible');
121
+ checkInvalid();
122
+
123
+ // Don't click 'other' and check comment textarea: should be mandatory
124
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.focus().blur();
125
+ getFormError().should('be.visible').should('contain.text', 'Enter details.');
126
+ checkInvalid();
127
+
128
+ ChatUiSelectors.chatbotMessageFeedbackOtherComment.type('Test comment');
129
+ checkValid();
130
+ });
131
+ });
@@ -0,0 +1,35 @@
1
+ import { Form, Stack } from '@servicetitan/design-system';
2
+ import { FormStateErrorBanner } from '@servicetitan/form';
3
+ import { useDependencies } from '@servicetitan/react-ioc';
4
+ import classNames from 'classnames';
5
+ import { observer } from 'mobx-react';
6
+ import { FC } from 'react';
7
+ import { MessageFeedbackGuardrailStore } from '../../../stores/message-feedback-guardrail.store';
8
+ import * as Styles from './chatbot-message-feedback-form.module.less';
9
+
10
+ export const ChatbotMessageFeedbackFormGuardrail: FC = observer(() => {
11
+ const [store] = useDependencies(MessageFeedbackGuardrailStore);
12
+ return (
13
+ <Stack direction="column" spacing="2">
14
+ {store.formState.error && <FormStateErrorBanner form={store.formState} />}
15
+ <Form data-cy="titan-chatbot-message-feedback-form">
16
+ <Form.Input
17
+ label="Link to correct article"
18
+ className={classNames(Styles.formItem)}
19
+ error={store.formState.$.linkUrl.error}
20
+ value={store.formState.$.linkUrl.value}
21
+ onChange={store.formState.$.linkUrl.onChangeHandler}
22
+ data-cy="titan-chatbot-message-feedback-form-link-url"
23
+ />
24
+ <Form.TextArea
25
+ label="Notes"
26
+ className={classNames(Styles.formItem, Styles.textArea)}
27
+ value={store.formState.$.comment.value}
28
+ onChange={store.formState.$.comment.onChangeHandler}
29
+ onBlur={store.formState.$.comment.enableAutoValidationAndValidate}
30
+ data-cy="titan-chatbot-message-feedback-form-other-comment"
31
+ />
32
+ </Form>
33
+ </Stack>
34
+ );
35
+ });
@@ -0,0 +1,15 @@
1
+ @import '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ .formItem {
4
+ margin-bottom: @spacing-2;
5
+ white-space: nowrap;
6
+ }
7
+
8
+ .formItem.textArea {
9
+ margin-bottom: @spacing-1;
10
+ }
11
+
12
+ .formItem.textArea textarea {
13
+ resize: none;
14
+ height: 4lh;
15
+ }
@@ -0,0 +1,4 @@
1
+ export const __esModule: true;
2
+ export const formItem: string;
3
+ export const textArea: string;
4
+