@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,110 @@
1
+ import { Container, Provider } from '@servicetitan/react-ioc';
2
+ import { ChatMessageModelText, mockChatMessageModelText } from '@servicetitan/titan-chat-ui-common';
3
+ import {
4
+ CHATBOT_UI_STORE_TOKEN,
5
+ ChatbotUiStore,
6
+ Models,
7
+ ModelsMocks,
8
+ } from '@servicetitan/titan-chatbot-api';
9
+ import { ChatUiSelectors } from '@servicetitan/titan-chatbot-ui-cypress';
10
+ import { mount } from 'cypress/react';
11
+ import { FC } from 'react';
12
+ import { ChatbotMessageAnswer } from '../chatbot-message-answer';
13
+
14
+ const initContainer = () => {
15
+ const container = new Container();
16
+ container.bind(CHATBOT_UI_STORE_TOKEN).to(ChatbotUiStore).inSingletonScope();
17
+ return container;
18
+ };
19
+
20
+ describe('<ChatbotMessageAnswer />', () => {
21
+ let container: Container;
22
+ let uiStore: ChatbotUiStore;
23
+
24
+ beforeEach(() => {
25
+ container = initContainer();
26
+ uiStore = container.get<ChatbotUiStore>(CHATBOT_UI_STORE_TOKEN);
27
+ cy.viewport(780, 800);
28
+ });
29
+
30
+ const renderComponent = (message: ChatMessageModelText) => {
31
+ const Wrapper: FC = () => (
32
+ <Provider
33
+ singletons={[
34
+ {
35
+ provide: CHATBOT_UI_STORE_TOKEN,
36
+ useValue: uiStore,
37
+ },
38
+ ]}
39
+ >
40
+ <ChatbotMessageAnswer message={message} />
41
+ </Provider>
42
+ );
43
+ mount(<Wrapper />);
44
+ };
45
+
46
+ it('should render MultilineText and ChatbotLinks when chatbotMessageData exists', () => {
47
+ cy.clock();
48
+ cy.spy(uiStore, 'triggerScroll').as('triggerScroll');
49
+ const botMessage: Models.IBotMessage = ModelsMocks.mockBotMessage({
50
+ scoredUrls: ModelsMocks.mockScoredUrls(),
51
+ });
52
+ const message = mockChatMessageModelText(true, {
53
+ message: 'Test message',
54
+ data: botMessage,
55
+ });
56
+ uiStore.setMessages([message]);
57
+
58
+ renderComponent(message);
59
+
60
+ ChatUiSelectors.chatbotMessageAnswer.should('contain.text', 'Test message');
61
+ ChatUiSelectors.chatbotLinksLink.should('not.be.visible');
62
+
63
+ // Expand links
64
+ ChatUiSelectors.chatbotLinksMore
65
+ .should('be.visible')
66
+ .should('have.text', 'Learn more')
67
+ .click();
68
+
69
+ cy.tick(100);
70
+ cy.get('@triggerScroll').should('be.called');
71
+ ChatUiSelectors.chatbotLinksLink.should('be.visible').should('have.length', 2);
72
+
73
+ // Collapse links
74
+ ChatUiSelectors.chatbotLinksMore
75
+ .should('be.visible')
76
+ .should('have.text', 'Learn less')
77
+ .click();
78
+
79
+ cy.tick(100);
80
+ cy.get('@triggerScroll').should('be.called');
81
+ ChatUiSelectors.chatbotLinksLink.should('not.be.visible');
82
+ });
83
+
84
+ it('should not render ChatbotLinks when no links exist', () => {
85
+ const botMessage: Models.IBotMessage = ModelsMocks.mockBotMessage({
86
+ scoredUrls: [],
87
+ });
88
+ const message = mockChatMessageModelText(true, {
89
+ message: 'Test message',
90
+ data: botMessage,
91
+ });
92
+ renderComponent(message);
93
+
94
+ ChatUiSelectors.chatbotMessageAnswer.should('contain.text', 'Test message');
95
+ ChatUiSelectors.chatbotLinksMore.should('not.exist');
96
+ ChatUiSelectors.chatbotLinksLink.should('not.exist');
97
+ });
98
+
99
+ it('should not render ChatbotLinks when chatbotMessageData is undefined', () => {
100
+ const message = mockChatMessageModelText(true, {
101
+ message: 'Test message',
102
+ data: undefined,
103
+ });
104
+ renderComponent(message);
105
+
106
+ ChatUiSelectors.chatbotMessageAnswer.should('contain.text', 'Test message');
107
+ ChatUiSelectors.chatbotLinksMore.should('not.exist');
108
+ ChatUiSelectors.chatbotLinksLink.should('not.exist');
109
+ });
110
+ });
@@ -0,0 +1,71 @@
1
+ import { IMessageAvatarProps } from '@servicetitan/titan-chat-ui';
2
+ import { ChatParticipantIcon } from '@servicetitan/titan-chat-ui-common';
3
+ import { ChatUiSelectors } from '@servicetitan/titan-chatbot-ui-cypress';
4
+ import { mount } from 'cypress/react';
5
+ import { ChatbotMessageTyping } from '../chatbot-message-typing';
6
+
7
+ describe('ChatbotMessageTyping', () => {
8
+ function render(icon: ChatParticipantIcon) {
9
+ const avatar: IMessageAvatarProps = {
10
+ name: 'Test User Name',
11
+ icon,
12
+ };
13
+ mount(<ChatbotMessageTyping avatar={avatar} />);
14
+ ChatUiSelectors.chatMessageTyping.should('be.visible');
15
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
16
+ }
17
+
18
+ it('should render message with bot icon', () => {
19
+ render(ChatParticipantIcon.Bot);
20
+ ChatUiSelectors.chatAvatarBot
21
+ .should('be.visible')
22
+ .invoke('css', 'background-image')
23
+ .then(bgColor => {
24
+ const isSvg = Boolean(
25
+ (bgColor as any as string).indexOf('data:image/svg+xml;base64') >= 0
26
+ );
27
+ expect(isSvg).to.eq(true);
28
+ });
29
+ });
30
+
31
+ it('should render message with empty icon', () => {
32
+ render(ChatParticipantIcon.Empty);
33
+ ChatUiSelectors.chatAvatarBot.should('not.exist');
34
+ ChatUiSelectors.chatAvatarInitials.should('not.exist');
35
+ });
36
+
37
+ it('should render message with initials', () => {
38
+ render(ChatParticipantIcon.Initials);
39
+ ChatUiSelectors.chatAvatarInitials.should('be.visible').should('contain.text', 'TU');
40
+ });
41
+
42
+ it('should show different messages during the time', () => {
43
+ cy.clock();
44
+ render(ChatParticipantIcon.Bot);
45
+ // Verify the rendered component + check the dots animation doesn't change over the time
46
+ ChatUiSelectors.chatMessageTypingDots.should('be.visible');
47
+ cy.tick(11000);
48
+ ChatUiSelectors.chatMessageContentAgent
49
+ .should('be.visible')
50
+ .should('contain.text', 'Looking for the final details');
51
+
52
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
53
+ cy.tick(10000);
54
+ ChatUiSelectors.chatMessageContentAgent
55
+ .should('be.visible')
56
+ .should(
57
+ 'contain.text',
58
+ 'This is taking longer than usual, please give me a little bit more time'
59
+ );
60
+
61
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
62
+ cy.tick(60000);
63
+ ChatUiSelectors.chatMessageContentAgent
64
+ .should('be.visible')
65
+ .should(
66
+ 'contain.text',
67
+ 'This is taking longer than usual, please give me a little bit more time'
68
+ );
69
+ ChatUiSelectors.chatMessageTypingDots.should('not.exist');
70
+ });
71
+ });
@@ -0,0 +1,22 @@
1
+ @import '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ ul.message-link-list {
4
+ margin-top: @spacing-1;
5
+ margin-bottom: @spacing-0;
6
+ padding-inline-start: @spacing-3;
7
+ margin-block-start: @spacing-half;
8
+ line-height: 1.5em;
9
+ }
10
+
11
+ ul.message-link-list > li {
12
+ padding-left: @spacing-half;
13
+ padding-top: @spacing-half;
14
+ }
15
+
16
+ .message-link {
17
+ -webkit-line-clamp: 2;
18
+ display: -webkit-box;
19
+ -webkit-box-orient: vertical;
20
+ overflow: hidden;
21
+ text-overflow: ellipsis;
22
+ }
@@ -0,0 +1,4 @@
1
+ export const __esModule: true;
2
+ export const messageLink: string;
3
+ export const messageLinkList: string;
4
+
@@ -0,0 +1,76 @@
1
+ import { BodyText, Collapsible, Icon, Link, Stack } from '@servicetitan/design-system';
2
+ import { Models } from '@servicetitan/titan-chatbot-api';
3
+ import { FC, Fragment, useCallback, useState } from 'react';
4
+ import * as Styles from './chatbot-links.module.less';
5
+
6
+ interface ChatLinksProps {
7
+ seeMoreLabel: string;
8
+ seeLessLabel: string;
9
+ links: Models.ScoredUrl[];
10
+ onToggle?: () => void;
11
+ linkProps?: {
12
+ primary?: boolean;
13
+ className?: string;
14
+ };
15
+ }
16
+
17
+ export const ChatbotLinks: FC<ChatLinksProps> = ({
18
+ linkProps,
19
+ links,
20
+ onToggle,
21
+ seeLessLabel,
22
+ seeMoreLabel,
23
+ }) => {
24
+ const [linksCollapsed, setLinksCollapsed] = useState(true);
25
+
26
+ const handleToggleCollapsible = useCallback(() => {
27
+ setLinksCollapsed(!linksCollapsed);
28
+ setTimeout(() => {
29
+ onToggle?.();
30
+ }, 200);
31
+ }, [onToggle, linksCollapsed]);
32
+
33
+ if (!links.length) {
34
+ return null;
35
+ }
36
+ return (
37
+ <Fragment>
38
+ <Link
39
+ className="m-t-2"
40
+ onClick={handleToggleCollapsible}
41
+ data-cy="titan-chatbot-links-more"
42
+ data-pendo="titan-chatbot-links-more"
43
+ >
44
+ <Stack direction="row" alignItems="center">
45
+ <BodyText el="div">{linksCollapsed ? seeMoreLabel : seeLessLabel}</BodyText>
46
+ <Icon
47
+ className="m-l-half"
48
+ name={linksCollapsed ? 'keyboard_arrow_down' : 'keyboard_arrow_up'}
49
+ />
50
+ </Stack>
51
+ </Link>
52
+ <Collapsible animate open={!linksCollapsed} data-cy="titan-chatbot-links-collapsible">
53
+ <ul className={Styles.messageLinkList}>
54
+ {links.map(u => (
55
+ <li key={u.url}>
56
+ <BodyText el="span">
57
+ <Link
58
+ href={u.url}
59
+ target="_blank"
60
+ className={Styles.messageLink}
61
+ primary
62
+ data-cy="titan-chatbot-links-link"
63
+ data-pendo="titan-chatbot-links-link-secondary"
64
+ title={u.title ?? u.url}
65
+ {...linkProps}
66
+ >
67
+ {u.title ?? u.url}
68
+ </Link>
69
+ </BodyText>
70
+ </li>
71
+ ))}
72
+ </ul>
73
+ </Collapsible>
74
+ </Fragment>
75
+ );
76
+ };
@@ -0,0 +1,35 @@
1
+ import { Eyebrow, Stack } from '@servicetitan/design-system';
2
+ import {
3
+ ChatMessageModelBase,
4
+ formatChatMessageDate,
5
+ getFirstName,
6
+ } from '@servicetitan/titan-chat-ui-common';
7
+ import { Models } from '@servicetitan/titan-chatbot-api';
8
+ import { observer } from 'mobx-react';
9
+ import { FC, useMemo } from 'react';
10
+ import { ChatbotMessageFeedback } from '../feedback/chatbot-message-feedback';
11
+
12
+ interface IChatbotMessageAgentFooterProps {
13
+ message: Pick<ChatMessageModelBase, 'participant' | 'timestamp' | 'data'>;
14
+ }
15
+
16
+ export const ChatbotMessageAgentFooter: FC<IChatbotMessageAgentFooterProps> = observer(
17
+ ({ message }) => {
18
+ const botMessage = useMemo(() => message.data as Models.IBotMessage, [message]);
19
+ const isBotMessage = Boolean(botMessage) && message.participant.isAgent;
20
+ const firstName = useMemo(
21
+ () => getFirstName(message.participant.name),
22
+ [message.participant.name]
23
+ );
24
+
25
+ return (
26
+ <Stack className="w-100" spacing="2" justifyContent="space-between" alignItems="center">
27
+ <Eyebrow>
28
+ {message.participant.isAgent && `${firstName} • `}
29
+ {formatChatMessageDate(message.timestamp)}
30
+ </Eyebrow>
31
+ {isBotMessage && <ChatbotMessageFeedback botMessage={botMessage} />}
32
+ </Stack>
33
+ );
34
+ }
35
+ );
@@ -0,0 +1,39 @@
1
+ import { MultilineText } from '@servicetitan/titan-chat-ui';
2
+ import { ChatMessageModelText } from '@servicetitan/titan-chat-ui-common';
3
+ import { Models } from '@servicetitan/titan-chatbot-api';
4
+ import classNames from 'classnames';
5
+ import { observer } from 'mobx-react';
6
+ import { FC, Fragment, useMemo } from 'react';
7
+ import { ChatbotLinks } from './chatbot-links';
8
+ import * as Styles from './chatbot-message-answer.module.less';
9
+
10
+ export const ChatbotMessageAnswerReadonly: FC<{ message: ChatMessageModelText }> = observer(
11
+ ({ message }) => {
12
+ const linkProps = useMemo(
13
+ () => ({
14
+ primary: false,
15
+ className: classNames(Styles.link, 'm-y-half-i'),
16
+ }),
17
+ []
18
+ );
19
+
20
+ const chatbotMessageData = useMemo(
21
+ () => message?.data as Models.IBotMessageWithFeedback,
22
+ [message]
23
+ );
24
+
25
+ return (
26
+ <Fragment>
27
+ <MultilineText text={message.message} className="m-y-half-i" />
28
+ {chatbotMessageData && (
29
+ <ChatbotLinks
30
+ links={chatbotMessageData.scoredUrls ?? []}
31
+ seeMoreLabel="Learn more"
32
+ seeLessLabel="Learn more"
33
+ linkProps={linkProps}
34
+ />
35
+ )}
36
+ </Fragment>
37
+ );
38
+ }
39
+ );
@@ -0,0 +1,3 @@
1
+ .link {
2
+ text-decoration: underline;
3
+ }
@@ -0,0 +1,3 @@
1
+ export const __esModule: true;
2
+ export const link: string;
3
+
@@ -0,0 +1,55 @@
1
+ import { useDependencies } from '@servicetitan/react-ioc';
2
+ import { MultilineText } from '@servicetitan/titan-chat-ui';
3
+ import { ChatMessageModelText } from '@servicetitan/titan-chat-ui-common';
4
+ import { CHATBOT_UI_STORE_TOKEN, Models } from '@servicetitan/titan-chatbot-api';
5
+ import classNames from 'classnames';
6
+ import { observer } from 'mobx-react';
7
+ import { FC, Fragment, useCallback, useMemo } from 'react';
8
+ import { ChatbotLinks } from './chatbot-links';
9
+ import * as Styles from './chatbot-message-answer.module.less';
10
+
11
+ export const ChatbotMessageAnswer: FC<{ message: ChatMessageModelText }> = observer(
12
+ ({ message }) => {
13
+ const [chatbotUiStore] = useDependencies(CHATBOT_UI_STORE_TOKEN);
14
+ const chatbotMessageData = useMemo(
15
+ () => message?.data as Models.IBotMessageWithFeedback,
16
+ [message]
17
+ );
18
+
19
+ const handleToggleLinks = useCallback(() => {
20
+ if (chatbotUiStore.messages.at(-1)?.id !== message.id) {
21
+ return;
22
+ }
23
+ setTimeout(() => {
24
+ chatbotUiStore.triggerScroll();
25
+ }, 100);
26
+ }, [message.id, chatbotUiStore]);
27
+
28
+ const linkProps = useMemo(
29
+ () => ({
30
+ primary: false,
31
+ className: classNames(Styles.link, 'm-y-half-i'),
32
+ }),
33
+ []
34
+ );
35
+
36
+ return (
37
+ <Fragment>
38
+ <MultilineText
39
+ text={message.message}
40
+ className="m-y-half-i"
41
+ data-cy="titan-chatbot-message-answer"
42
+ />
43
+ {chatbotMessageData && (
44
+ <ChatbotLinks
45
+ links={chatbotMessageData.scoredUrls ?? []}
46
+ onToggle={handleToggleLinks}
47
+ seeMoreLabel="Learn more"
48
+ seeLessLabel="Learn less"
49
+ linkProps={linkProps}
50
+ />
51
+ )}
52
+ </Fragment>
53
+ );
54
+ }
55
+ );
@@ -0,0 +1,20 @@
1
+ import { useDependencies } from '@servicetitan/react-ioc';
2
+ import { MessageTimeout } from '@servicetitan/titan-chat-ui';
3
+ import { ChatMessageModelTimeout } from '@servicetitan/titan-chat-ui-common';
4
+ import { CHATBOT_UI_STORE_TOKEN } from '@servicetitan/titan-chatbot-api';
5
+ import { observer } from 'mobx-react';
6
+ import { FC, useCallback } from 'react';
7
+
8
+ export const ChatbotMessageTimeout: FC<{ message: ChatMessageModelTimeout }> = observer(() => {
9
+ const [chatbotUiStore] = useDependencies(CHATBOT_UI_STORE_TOKEN);
10
+
11
+ const handleResume = useCallback(() => {
12
+ chatbotUiStore.restartTimers();
13
+ }, [chatbotUiStore]);
14
+
15
+ const handleReset = useCallback(() => {
16
+ chatbotUiStore.restart();
17
+ }, [chatbotUiStore]);
18
+
19
+ return <MessageTimeout onResume={handleResume} onReset={handleReset} />;
20
+ });
@@ -0,0 +1,39 @@
1
+ import { BodyText } from '@servicetitan/design-system';
2
+ import { IMessageTypingProps, MessageAgent, MessageTyping } from '@servicetitan/titan-chat-ui';
3
+ import { FC, useEffect, useMemo, useState } from 'react';
4
+
5
+ /**
6
+ * Typing message for chatbot. Shows three dots that are blinking, after 10 and 20 seconds shows a messages
7
+ * that the chatbot is still working instead of the dots.
8
+ */
9
+ export const ChatbotMessageTyping: FC<IMessageTypingProps> = ({ avatar }) => {
10
+ const [timer, setTimer] = useState(0);
11
+ const [dots, setDots] = useState('');
12
+
13
+ useEffect(() => {
14
+ const interval = setInterval(() => setTimer(prev => prev + 1), 1000);
15
+ return () => clearInterval(interval);
16
+ }, []);
17
+
18
+ useEffect(() => {
19
+ setDots(() => '.'.repeat((timer % 3) + 1));
20
+ }, [timer]);
21
+
22
+ const dotsStyle = useMemo(() => ({ width: '1rem' }), []);
23
+
24
+ if (timer < 10) {
25
+ return <MessageTyping avatar={avatar} />;
26
+ }
27
+ return (
28
+ <MessageAgent avatar={avatar}>
29
+ <BodyText>
30
+ {timer < 20
31
+ ? 'Looking for the final details'
32
+ : 'This is taking longer than usual, please give me a little bit more time'}
33
+ <span className="d-ib" style={dotsStyle}>
34
+ {dots}
35
+ </span>
36
+ </BodyText>
37
+ </MessageAgent>
38
+ );
39
+ };
@@ -0,0 +1,16 @@
1
+ import { BodyText } from '@servicetitan/design-system';
2
+ import { FC } from 'react';
3
+
4
+ export const ChatbotMessageWelcome: FC = () => (
5
+ <BodyText>
6
+ Hi there! I’m Titan, an AI chatbot.
7
+ <br />
8
+ <br />
9
+ <b>Have a question?</b> Ask me anything about how ServiceTitan works.
10
+ <br />
11
+ <b>Need help?</b> If I can’t solve your issue, I’ll guide you through submitting a support
12
+ case.
13
+ <br />
14
+ Let’s get started - how can I assist you today?
15
+ </BodyText>
16
+ );
@@ -0,0 +1,25 @@
1
+ import { MessageAgent, MessageFooter, useAvatarProps } from '@servicetitan/titan-chat-ui';
2
+ import { ChatMessageModelBase, getFirstName } from '@servicetitan/titan-chat-ui-common';
3
+ import { observer } from 'mobx-react';
4
+ import { FC, PropsWithChildren, useMemo } from 'react';
5
+
6
+ export const ChatbotMessageTemplateAgentReadonly: FC<
7
+ PropsWithChildren<{
8
+ message: ChatMessageModelBase;
9
+ }>
10
+ > = observer(({ children, message }) => {
11
+ const {
12
+ participant: { icon, name },
13
+ } = message;
14
+ const firstName = useMemo(() => getFirstName(name), [name]);
15
+ const avatarProps = useAvatarProps(name, icon);
16
+
17
+ return (
18
+ <MessageAgent
19
+ avatar={avatarProps}
20
+ messageFooter={<MessageFooter name={firstName} timestamp={message.timestamp} />}
21
+ >
22
+ {children}
23
+ </MessageAgent>
24
+ );
25
+ });
@@ -0,0 +1,25 @@
1
+ import { MessageAgent, useAvatarProps } from '@servicetitan/titan-chat-ui';
2
+ import { ChatMessageModelBase } from '@servicetitan/titan-chat-ui-common';
3
+ import { observer } from 'mobx-react';
4
+ import { FC, PropsWithChildren } from 'react';
5
+ import { ChatbotMessageAgentFooter } from '../messages/chatbot-message-agent-footer';
6
+
7
+ export const ChatbotMessageTemplateAgent: FC<
8
+ PropsWithChildren<{
9
+ message: ChatMessageModelBase;
10
+ }>
11
+ > = observer(({ children, message }) => {
12
+ const {
13
+ participant: { icon, name },
14
+ } = message;
15
+ const avatarProps = useAvatarProps(name, icon);
16
+
17
+ return (
18
+ <MessageAgent
19
+ avatar={avatarProps}
20
+ messageFooter={<ChatbotMessageAgentFooter message={message} />}
21
+ >
22
+ {children}
23
+ </MessageAgent>
24
+ );
25
+ });
@@ -0,0 +1,16 @@
1
+ import { MessageFooter, MessageUser } from '@servicetitan/titan-chat-ui';
2
+ import { ChatMessageModelBase } from '@servicetitan/titan-chat-ui-common';
3
+ import { observer } from 'mobx-react';
4
+ import { FC, PropsWithChildren } from 'react';
5
+
6
+ export const ChatbotMessageTemplateUserReadonly: FC<
7
+ PropsWithChildren<{
8
+ message: ChatMessageModelBase;
9
+ }>
10
+ > = observer(({ children, message }) => {
11
+ return (
12
+ <MessageUser messageFooter={<MessageFooter timestamp={message.timestamp} />}>
13
+ {children}
14
+ </MessageUser>
15
+ );
16
+ });
@@ -0,0 +1,10 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="cypress" />
3
+ declare namespace Cypress {
4
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
+ interface Chainable<Subject = any> {
6
+ getCy<E extends Node = HTMLElement>(value: string): Chainable<JQuery<E>>;
7
+ getCy2<E extends Node = HTMLElement>(value: string): Chainable<JQuery<E>>;
8
+ getAnvil<E extends Node = HTMLElement>(value: string, extra?: string): Chainable<JQuery<E>>;
9
+ }
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from '@servicetitan/titan-chat-ui';
2
+ export { Chatbot } from './components/chatbot/chatbot';
3
+ export { ChatbotLinks } from './components/chatbot/messages/chatbot-links';
4
+ export { ChatbotMessageAgentFooter } from './components/chatbot/messages/chatbot-message-agent-footer';
5
+ export { ChatbotSessionFeedbackLink } from './components/chatbot/feedback/chatbot-session-feedback-link';
@@ -0,0 +1,61 @@
1
+ import { expect } from '@jest/globals';
2
+ import { Models } from '@servicetitan/titan-chatbot-api';
3
+ import { initTestContainer } from '../../utils/test-utils';
4
+ import { MessageFeedbackGuardrailStore } from '../message-feedback-guardrail.store';
5
+
6
+ const initContainer = initTestContainer(MessageFeedbackGuardrailStore, () => {});
7
+
8
+ describe('[MessageFeedbackGuardrailStore]', () => {
9
+ let store: MessageFeedbackGuardrailStore;
10
+
11
+ beforeEach(() => {
12
+ const container = initContainer();
13
+ store = container.get(MessageFeedbackGuardrailStore);
14
+ });
15
+
16
+ afterEach(() => {
17
+ jest.clearAllMocks();
18
+ });
19
+
20
+ test('should have correct default values', () => {
21
+ expect(store.isValid).toBe(false);
22
+ expect(store.formState.$.linkUrl.value).toBe('');
23
+ expect(store.formState.$.comment.value).toBe('');
24
+ });
25
+
26
+ test('should export feedback with linkUrl and comment', async () => {
27
+ store.formState.$.linkUrl.onChange('https://example.com');
28
+ store.formState.$.comment.onChange('This is a comment');
29
+
30
+ await store.formState.validate();
31
+ const feedback = store.export();
32
+
33
+ expect(feedback.rating).toBe(Models.FeedbackRatings.GuardrailFeedback);
34
+ expect(feedback.linkUrl).toBe('https://example.com');
35
+ expect(feedback.description).toBe('This is a comment');
36
+ });
37
+
38
+ test('should validate form state', async () => {
39
+ store.formState.$.linkUrl.onChange('');
40
+ store.formState.$.comment.onChange('');
41
+
42
+ await store.formState.validate();
43
+ expect(store.isValid).toBe(false);
44
+ expect(store.formState.error).toBe('At least one input must be provided.');
45
+
46
+ store.formState.$.linkUrl.onChange('https://example.com');
47
+ store.formState.$.comment.onChange('');
48
+ await store.formState.validate();
49
+ expect(store.isValid).toBe(true);
50
+
51
+ store.formState.$.linkUrl.onChange('');
52
+ store.formState.$.comment.onChange('This is a comment');
53
+ await store.formState.validate();
54
+ expect(store.isValid).toBe(true);
55
+
56
+ store.formState.$.linkUrl.onChange('https://example.com');
57
+ store.formState.$.comment.onChange('This is a comment');
58
+ await store.formState.validate();
59
+ expect(store.isValid).toBe(true);
60
+ });
61
+ });