@servicetitan/titan-chat-ui 1.0.1

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 (244) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/assets/floating-chat-avatar.svg +16 -0
  3. package/dist/components/chat/__tests-cy__/chat-messages.test.d.ts +2 -0
  4. package/dist/components/chat/__tests-cy__/chat-messages.test.d.ts.map +1 -0
  5. package/dist/components/chat/__tests-cy__/chat-messages.test.js +28 -0
  6. package/dist/components/chat/__tests-cy__/chat-messages.test.js.map +1 -0
  7. package/dist/components/chat/__tests-cy__/chat.test.d.ts +2 -0
  8. package/dist/components/chat/__tests-cy__/chat.test.d.ts.map +1 -0
  9. package/dist/components/chat/__tests-cy__/chat.test.js +122 -0
  10. package/dist/components/chat/__tests-cy__/chat.test.js.map +1 -0
  11. package/dist/components/chat/chat-connecting.d.ts +7 -0
  12. package/dist/components/chat/chat-connecting.d.ts.map +1 -0
  13. package/dist/components/chat/chat-connecting.js +6 -0
  14. package/dist/components/chat/chat-connecting.js.map +1 -0
  15. package/dist/components/chat/chat-error.d.ts +3 -0
  16. package/dist/components/chat/chat-error.d.ts.map +1 -0
  17. package/dist/components/chat/chat-error.js +18 -0
  18. package/dist/components/chat/chat-error.js.map +1 -0
  19. package/dist/components/chat/chat-error.module.less +6 -0
  20. package/dist/components/chat/chat-input-file.d.ts +5 -0
  21. package/dist/components/chat/chat-input-file.d.ts.map +1 -0
  22. package/dist/components/chat/chat-input-file.js +43 -0
  23. package/dist/components/chat/chat-input-file.js.map +1 -0
  24. package/dist/components/chat/chat-input.d.ts +5 -0
  25. package/dist/components/chat/chat-input.d.ts.map +1 -0
  26. package/dist/components/chat/chat-input.js +94 -0
  27. package/dist/components/chat/chat-input.js.map +1 -0
  28. package/dist/components/chat/chat-input.module.less +11 -0
  29. package/dist/components/chat/chat-message-template-agent.d.ts +4 -0
  30. package/dist/components/chat/chat-message-template-agent.d.ts.map +1 -0
  31. package/dist/components/chat/chat-message-template-agent.js +14 -0
  32. package/dist/components/chat/chat-message-template-agent.js.map +1 -0
  33. package/dist/components/chat/chat-message-template-user.d.ts +4 -0
  34. package/dist/components/chat/chat-message-template-user.d.ts.map +1 -0
  35. package/dist/components/chat/chat-message-template-user.js +16 -0
  36. package/dist/components/chat/chat-message-template-user.js.map +1 -0
  37. package/dist/components/chat/chat-message-typing.d.ts +3 -0
  38. package/dist/components/chat/chat-message-typing.d.ts.map +1 -0
  39. package/dist/components/chat/chat-message-typing.js +15 -0
  40. package/dist/components/chat/chat-message-typing.js.map +1 -0
  41. package/dist/components/chat/chat-message.d.ts +12 -0
  42. package/dist/components/chat/chat-message.d.ts.map +1 -0
  43. package/dist/components/chat/chat-message.js +60 -0
  44. package/dist/components/chat/chat-message.js.map +1 -0
  45. package/dist/components/chat/chat-messages.d.ts +7 -0
  46. package/dist/components/chat/chat-messages.d.ts.map +1 -0
  47. package/dist/components/chat/chat-messages.js +12 -0
  48. package/dist/components/chat/chat-messages.js.map +1 -0
  49. package/dist/components/chat/chat-notifications.d.ts +5 -0
  50. package/dist/components/chat/chat-notifications.d.ts.map +1 -0
  51. package/dist/components/chat/chat-notifications.js +12 -0
  52. package/dist/components/chat/chat-notifications.js.map +1 -0
  53. package/dist/components/chat/chat-timer.d.ts +3 -0
  54. package/dist/components/chat/chat-timer.d.ts.map +1 -0
  55. package/dist/components/chat/chat-timer.js +18 -0
  56. package/dist/components/chat/chat-timer.js.map +1 -0
  57. package/dist/components/chat/chat-timer.module.less +5 -0
  58. package/dist/components/chat/chat.d.ts +8 -0
  59. package/dist/components/chat/chat.d.ts.map +1 -0
  60. package/dist/components/chat/chat.js +28 -0
  61. package/dist/components/chat/chat.js.map +1 -0
  62. package/dist/components/common/multiline-text.d.ts +8 -0
  63. package/dist/components/common/multiline-text.d.ts.map +1 -0
  64. package/dist/components/common/multiline-text.js +12 -0
  65. package/dist/components/common/multiline-text.js.map +1 -0
  66. package/dist/components/common/multiline-text.module.less +9 -0
  67. package/dist/components/message-content/message-content-file.d.ts +8 -0
  68. package/dist/components/message-content/message-content-file.d.ts.map +1 -0
  69. package/dist/components/message-content/message-content-file.js +11 -0
  70. package/dist/components/message-content/message-content-file.js.map +1 -0
  71. package/dist/components/message-content/message-content-text.d.ts +8 -0
  72. package/dist/components/message-content/message-content-text.d.ts.map +1 -0
  73. package/dist/components/message-content/message-content-text.js +7 -0
  74. package/dist/components/message-content/message-content-text.js.map +1 -0
  75. package/dist/components/messages/__tests-cy__/message-agent.test.d.ts +2 -0
  76. package/dist/components/messages/__tests-cy__/message-agent.test.d.ts.map +1 -0
  77. package/dist/components/messages/__tests-cy__/message-agent.test.js +89 -0
  78. package/dist/components/messages/__tests-cy__/message-agent.test.js.map +1 -0
  79. package/dist/components/messages/__tests-cy__/message-system.test.d.ts +2 -0
  80. package/dist/components/messages/__tests-cy__/message-system.test.d.ts.map +1 -0
  81. package/dist/components/messages/__tests-cy__/message-system.test.js +20 -0
  82. package/dist/components/messages/__tests-cy__/message-system.test.js.map +1 -0
  83. package/dist/components/messages/__tests-cy__/message-timeout.test.d.ts +2 -0
  84. package/dist/components/messages/__tests-cy__/message-timeout.test.d.ts.map +1 -0
  85. package/dist/components/messages/__tests-cy__/message-timeout.test.js +32 -0
  86. package/dist/components/messages/__tests-cy__/message-timeout.test.js.map +1 -0
  87. package/dist/components/messages/__tests-cy__/message-typing.test.d.ts +2 -0
  88. package/dist/components/messages/__tests-cy__/message-typing.test.d.ts.map +1 -0
  89. package/dist/components/messages/__tests-cy__/message-typing.test.js +49 -0
  90. package/dist/components/messages/__tests-cy__/message-typing.test.js.map +1 -0
  91. package/dist/components/messages/__tests-cy__/message-user.test.d.ts +2 -0
  92. package/dist/components/messages/__tests-cy__/message-user.test.d.ts.map +1 -0
  93. package/dist/components/messages/__tests-cy__/message-user.test.js +33 -0
  94. package/dist/components/messages/__tests-cy__/message-user.test.js.map +1 -0
  95. package/dist/components/messages/message-agent.d.ts +12 -0
  96. package/dist/components/messages/message-agent.d.ts.map +1 -0
  97. package/dist/components/messages/message-agent.js +18 -0
  98. package/dist/components/messages/message-agent.js.map +1 -0
  99. package/dist/components/messages/message-agent.module.less +59 -0
  100. package/dist/components/messages/message-avatar.d.ts +9 -0
  101. package/dist/components/messages/message-avatar.d.ts.map +1 -0
  102. package/dist/components/messages/message-avatar.js +14 -0
  103. package/dist/components/messages/message-avatar.js.map +1 -0
  104. package/dist/components/messages/message-avatar.module.less +26 -0
  105. package/dist/components/messages/message-footer.d.ts +7 -0
  106. package/dist/components/messages/message-footer.d.ts.map +1 -0
  107. package/dist/components/messages/message-footer.js +7 -0
  108. package/dist/components/messages/message-footer.js.map +1 -0
  109. package/dist/components/messages/message-system.d.ts +8 -0
  110. package/dist/components/messages/message-system.d.ts.map +1 -0
  111. package/dist/components/messages/message-system.js +12 -0
  112. package/dist/components/messages/message-system.js.map +1 -0
  113. package/dist/components/messages/message-system.module.less +26 -0
  114. package/dist/components/messages/message-timeout.d.ts +8 -0
  115. package/dist/components/messages/message-timeout.d.ts.map +1 -0
  116. package/dist/components/messages/message-timeout.js +16 -0
  117. package/dist/components/messages/message-timeout.js.map +1 -0
  118. package/dist/components/messages/message-typing.d.ts +8 -0
  119. package/dist/components/messages/message-typing.d.ts.map +1 -0
  120. package/dist/components/messages/message-typing.js +10 -0
  121. package/dist/components/messages/message-typing.js.map +1 -0
  122. package/dist/components/messages/message-typing.module.less +40 -0
  123. package/dist/components/messages/message-user.d.ts +9 -0
  124. package/dist/components/messages/message-user.d.ts.map +1 -0
  125. package/dist/components/messages/message-user.js +13 -0
  126. package/dist/components/messages/message-user.js.map +1 -0
  127. package/dist/components/messages/message-user.module.less +35 -0
  128. package/dist/components/messages/use-avatar-props.d.ts +4 -0
  129. package/dist/components/messages/use-avatar-props.d.ts.map +1 -0
  130. package/dist/components/messages/use-avatar-props.js +12 -0
  131. package/dist/components/messages/use-avatar-props.js.map +1 -0
  132. package/dist/index.d.ts +12 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +12 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/models/chat-customizations.d.ts +29 -0
  137. package/dist/models/chat-customizations.d.ts.map +1 -0
  138. package/dist/models/chat-customizations.js +2 -0
  139. package/dist/models/chat-customizations.js.map +1 -0
  140. package/dist/models/component.d.ts +4 -0
  141. package/dist/models/component.d.ts.map +1 -0
  142. package/dist/models/component.js +2 -0
  143. package/dist/models/component.js.map +1 -0
  144. package/dist/models/index.d.ts +3 -0
  145. package/dist/models/index.d.ts.map +1 -0
  146. package/dist/models/index.js +3 -0
  147. package/dist/models/index.js.map +1 -0
  148. package/dist/models/support-chat.d.ts +66 -0
  149. package/dist/models/support-chat.d.ts.map +1 -0
  150. package/dist/models/support-chat.js +28 -0
  151. package/dist/models/support-chat.js.map +1 -0
  152. package/dist/stores/__mocks-cy__/chat-ui.store.mock.d.ts +65 -0
  153. package/dist/stores/__mocks-cy__/chat-ui.store.mock.d.ts.map +1 -0
  154. package/dist/stores/__mocks-cy__/chat-ui.store.mock.js +268 -0
  155. package/dist/stores/__mocks-cy__/chat-ui.store.mock.js.map +1 -0
  156. package/dist/stores/chat-input.store.d.ts +10 -0
  157. package/dist/stores/chat-input.store.d.ts.map +1 -0
  158. package/dist/stores/chat-input.store.js +46 -0
  159. package/dist/stores/chat-input.store.js.map +1 -0
  160. package/dist/stores/chat-ui-backend-echo.store.d.ts +13 -0
  161. package/dist/stores/chat-ui-backend-echo.store.d.ts.map +1 -0
  162. package/dist/stores/chat-ui-backend-echo.store.js +111 -0
  163. package/dist/stores/chat-ui-backend-echo.store.js.map +1 -0
  164. package/dist/stores/chat-ui-backend.store.d.ts +6 -0
  165. package/dist/stores/chat-ui-backend.store.d.ts.map +1 -0
  166. package/dist/stores/chat-ui-backend.store.js +3 -0
  167. package/dist/stores/chat-ui-backend.store.js.map +1 -0
  168. package/dist/stores/chat-ui.store.d.ts +142 -0
  169. package/dist/stores/chat-ui.store.d.ts.map +1 -0
  170. package/dist/stores/chat-ui.store.js +679 -0
  171. package/dist/stores/chat-ui.store.js.map +1 -0
  172. package/dist/stores/index.d.ts +3 -0
  173. package/dist/stores/index.d.ts.map +1 -0
  174. package/dist/stores/index.js +3 -0
  175. package/dist/stores/index.js.map +1 -0
  176. package/dist/utils/text-utils.d.ts +11 -0
  177. package/dist/utils/text-utils.d.ts.map +1 -0
  178. package/dist/utils/text-utils.js +82 -0
  179. package/dist/utils/text-utils.js.map +1 -0
  180. package/package.json +52 -0
  181. package/src/assets/floating-chat-avatar.svg +16 -0
  182. package/src/components/chat/__tests-cy__/chat-messages.test.tsx +36 -0
  183. package/src/components/chat/__tests-cy__/chat.test.tsx +156 -0
  184. package/src/components/chat/chat-connecting.tsx +23 -0
  185. package/src/components/chat/chat-error.module.less +6 -0
  186. package/src/components/chat/chat-error.module.less.d.ts +3 -0
  187. package/src/components/chat/chat-error.tsx +39 -0
  188. package/src/components/chat/chat-input-file.tsx +68 -0
  189. package/src/components/chat/chat-input.module.less +11 -0
  190. package/src/components/chat/chat-input.module.less.d.ts +3 -0
  191. package/src/components/chat/chat-input.tsx +143 -0
  192. package/src/components/chat/chat-message-template-agent.tsx +26 -0
  193. package/src/components/chat/chat-message-template-user.tsx +46 -0
  194. package/src/components/chat/chat-message-typing.tsx +19 -0
  195. package/src/components/chat/chat-message.tsx +78 -0
  196. package/src/components/chat/chat-messages.tsx +23 -0
  197. package/src/components/chat/chat-notifications.tsx +19 -0
  198. package/src/components/chat/chat-timer.module.less +5 -0
  199. package/src/components/chat/chat-timer.module.less.d.ts +3 -0
  200. package/src/components/chat/chat-timer.tsx +35 -0
  201. package/src/components/chat/chat.tsx +55 -0
  202. package/src/components/common/multiline-text.module.less +9 -0
  203. package/src/components/common/multiline-text.module.less.d.ts +3 -0
  204. package/src/components/common/multiline-text.tsx +30 -0
  205. package/src/components/message-content/message-content-file.tsx +27 -0
  206. package/src/components/message-content/message-content-text.tsx +12 -0
  207. package/src/components/messages/__tests-cy__/message-agent.test.tsx +155 -0
  208. package/src/components/messages/__tests-cy__/message-system.test.tsx +33 -0
  209. package/src/components/messages/__tests-cy__/message-timeout.test.tsx +38 -0
  210. package/src/components/messages/__tests-cy__/message-typing.test.tsx +58 -0
  211. package/src/components/messages/__tests-cy__/message-user.test.tsx +52 -0
  212. package/src/components/messages/message-agent.module.less +59 -0
  213. package/src/components/messages/message-agent.module.less.d.ts +9 -0
  214. package/src/components/messages/message-agent.tsx +62 -0
  215. package/src/components/messages/message-avatar.module.less +26 -0
  216. package/src/components/messages/message-avatar.module.less.d.ts +5 -0
  217. package/src/components/messages/message-avatar.tsx +33 -0
  218. package/src/components/messages/message-footer.tsx +17 -0
  219. package/src/components/messages/message-system.module.less +26 -0
  220. package/src/components/messages/message-system.module.less.d.ts +5 -0
  221. package/src/components/messages/message-system.tsx +35 -0
  222. package/src/components/messages/message-timeout.tsx +42 -0
  223. package/src/components/messages/message-typing.module.less +40 -0
  224. package/src/components/messages/message-typing.module.less.d.ts +5 -0
  225. package/src/components/messages/message-typing.tsx +25 -0
  226. package/src/components/messages/message-user.module.less +35 -0
  227. package/src/components/messages/message-user.module.less.d.ts +7 -0
  228. package/src/components/messages/message-user.tsx +49 -0
  229. package/src/components/messages/use-avatar-props.tsx +17 -0
  230. package/src/cypress.d.ts +10 -0
  231. package/src/index.ts +11 -0
  232. package/src/models/chat-customizations.ts +34 -0
  233. package/src/models/component.ts +3 -0
  234. package/src/models/index.ts +2 -0
  235. package/src/models/support-chat.ts +84 -0
  236. package/src/stores/__mocks-cy__/chat-ui.store.mock.ts +105 -0
  237. package/src/stores/chat-input.store.ts +25 -0
  238. package/src/stores/chat-ui-backend-echo.store.ts +94 -0
  239. package/src/stores/chat-ui-backend.store.ts +10 -0
  240. package/src/stores/chat-ui.store.ts +537 -0
  241. package/src/stores/index.ts +10 -0
  242. package/src/utils/text-utils.ts +93 -0
  243. package/tsconfig.json +15 -0
  244. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,155 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="../../../cypress" />
3
+ import { BodyText, Button, Stack } from '@servicetitan/design-system';
4
+ import { mount } from 'cypress/react';
5
+ import { ChatParticipantIcon } from '../../../models/support-chat';
6
+ import { MessageAgent } from '../message-agent';
7
+ import { MessageFooter } from '../message-footer';
8
+
9
+ describe('[MessageAgent]', () => {
10
+ beforeEach(() => {
11
+ cy.viewport('macbook-13');
12
+ cy.clock(new Date('2023-10-10T10:10:00Z').getTime());
13
+ });
14
+
15
+ it('should be properly rendered', () => {
16
+ mount(
17
+ <MessageAgent
18
+ avatar={{
19
+ name: 'Test User Name',
20
+ icon: ChatParticipantIcon.Bot,
21
+ className: 'custom-class',
22
+ }}
23
+ >
24
+ <BodyText data-cy="content">message agent content</BodyText>
25
+ </MessageAgent>
26
+ );
27
+ cy.getCy('chat-message-agent').should('be.visible').children().should('have.length', 2);
28
+ cy.getCy('chat-avatar-bot').should('be.visible').should('have.class', 'custom-class');
29
+ cy.getCy('chat-message-agent-content')
30
+
31
+ .should('contain.text', 'message agent content');
32
+ cy.getCy('content').should('contain.text', 'message agent content');
33
+ });
34
+
35
+ it('should be full width', () => {
36
+ mount(
37
+ <MessageAgent
38
+ fullWidth
39
+ avatar={{
40
+ name: 'Test User Name',
41
+ icon: ChatParticipantIcon.Bot,
42
+ }}
43
+ >
44
+ <BodyText>message agent content</BodyText>
45
+ </MessageAgent>
46
+ );
47
+ cy.getCy('chat-message-agent-center').should('have.class', 'w-100');
48
+ });
49
+
50
+ it('should render with custom data-cy', () => {
51
+ mount(
52
+ <MessageAgent
53
+ data-cy="custom-data-cy"
54
+ avatar={{
55
+ name: 'Test User Name',
56
+ icon: ChatParticipantIcon.Bot,
57
+ }}
58
+ >
59
+ <BodyText>message agent content</BodyText>
60
+ </MessageAgent>
61
+ );
62
+ cy.getCy('custom-data-cy').should('be.visible');
63
+ cy.getCy('custom-data-cy-content').should('be.visible');
64
+ });
65
+
66
+ it('should render custom message footer', () => {
67
+ mount(
68
+ <MessageAgent
69
+ messageFooter={
70
+ <Stack direction="row" justifyContent="space-between" alignItems="center">
71
+ <MessageFooter name="name" timestamp={new Date()} />
72
+ <Stack direction="row" spacing="1">
73
+ <Button iconName="thumb_up" xsmall fill="subtle" />
74
+ <Button iconName="thumb_down" xsmall fill="subtle" />
75
+ </Stack>
76
+ </Stack>
77
+ }
78
+ avatar={{
79
+ name: 'Test User Name',
80
+ icon: ChatParticipantIcon.Bot,
81
+ }}
82
+ >
83
+ <BodyText>message agent content</BodyText>
84
+ </MessageAgent>
85
+ );
86
+ cy.getCy('chat-message-agent').should('be.visible').children().should('have.length', 2);
87
+ cy.getCy('chat-message-agent-footer')
88
+ .should('be.visible')
89
+ .should('contain.text', 'name • 10:10 AM');
90
+ });
91
+
92
+ it('should render error message', () => {
93
+ mount(
94
+ <MessageAgent
95
+ isError
96
+ avatar={{
97
+ name: 'Test User Name',
98
+ icon: ChatParticipantIcon.Bot,
99
+ }}
100
+ >
101
+ <BodyText>message agent content</BodyText>
102
+ </MessageAgent>
103
+ );
104
+ cy.getCy2('chat-message-error').should('be.visible');
105
+ });
106
+
107
+ it('should render subtle message', () => {
108
+ mount(
109
+ <MessageAgent
110
+ subtle
111
+ avatar={{
112
+ name: 'Test User Name',
113
+ icon: ChatParticipantIcon.Bot,
114
+ }}
115
+ >
116
+ <Stack className="border h-100" alignItems="center" data-cy="custom-content">
117
+ <BodyText>message agent content</BodyText>
118
+ </Stack>
119
+ </MessageAgent>
120
+ );
121
+ cy.getCy('chat-avatar-bot').should('be.visible');
122
+ cy.getCy('custom-content')
123
+ .should('be.visible')
124
+ .should('contain.text', 'message agent content');
125
+ });
126
+
127
+ describe('with different icon types', () => {
128
+ const render = (icon: ChatParticipantIcon) => {
129
+ return mount(
130
+ <MessageAgent
131
+ avatar={{
132
+ name: 'Test User Name',
133
+ icon,
134
+ }}
135
+ >
136
+ <BodyText>message agent content</BodyText>
137
+ </MessageAgent>
138
+ );
139
+ };
140
+ it('icon bot', () => {
141
+ render(ChatParticipantIcon.Bot);
142
+ cy.getCy('chat-avatar-bot').should('be.visible');
143
+ });
144
+
145
+ it('icon initials', () => {
146
+ render(ChatParticipantIcon.Initials);
147
+ cy.getCy('chat-avatar-initials').should('be.visible').should('contain.text', 'T');
148
+ });
149
+
150
+ it('icon empty', () => {
151
+ render(ChatParticipantIcon.Empty);
152
+ cy.getCy('chat-avatar-empty').should('exist').should('not.be.visible');
153
+ });
154
+ });
155
+ });
@@ -0,0 +1,33 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="../../../cypress" />
3
+ import { BodyText, Stack } from '@servicetitan/design-system';
4
+ import { mount } from 'cypress/react';
5
+ import { MessageSystem } from '../message-system';
6
+
7
+ describe('[MessageSystem]', () => {
8
+ beforeEach(() => {
9
+ cy.viewport('macbook-13');
10
+ });
11
+
12
+ it('should be properly rendered', () => {
13
+ mount(
14
+ <MessageSystem className="custom-class">
15
+ <Stack className="border">
16
+ <BodyText data-cy="content">message system content</BodyText>
17
+ </Stack>
18
+ </MessageSystem>
19
+ );
20
+ cy.getCy('chat-message-system').should('have.class', 'custom-class').should('be.visible');
21
+ });
22
+
23
+ it('should be properly rendered fullwidth', () => {
24
+ mount(
25
+ <MessageSystem className="custom-class" fullWidth>
26
+ <Stack className="border">
27
+ <BodyText data-cy="content">message system content</BodyText>
28
+ </Stack>
29
+ </MessageSystem>
30
+ );
31
+ cy.getCy('chat-message-system').should('have.class', 'custom-class').should('be.visible');
32
+ });
33
+ });
@@ -0,0 +1,38 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="../../../cypress" />
3
+ import { mount } from 'cypress/react';
4
+ import { MessageTimeout } from '../message-timeout';
5
+
6
+ describe('MessageTimeout', () => {
7
+ it('should render message', () => {
8
+ const onResume = cy.spy().as('onResume');
9
+ const onReset = cy.spy().as('onReset');
10
+ mount(<MessageTimeout onResume={onResume} onReset={onReset} />);
11
+
12
+ cy.getCy('chat-message-timeout')
13
+ .should('be.visible')
14
+ .should(
15
+ 'contain.text',
16
+ [
17
+ 'Your session has timed out.',
18
+ 'Would you like to resume it or start a new one?',
19
+ 'Continue session or Start new session',
20
+ ].join('')
21
+ );
22
+ cy.getCy('chat-message-timeout-resume').should('be.visible');
23
+ cy.getCy('chat-message-timeout-reset').should('be.visible');
24
+
25
+ cy.getCy('chat-message-timeout-resume').click();
26
+ cy.get('@onResume').should('have.been.calledOnce');
27
+ cy.getCy('chat-message-timeout-reset').click();
28
+ cy.get('@onReset').should('have.been.calledOnce');
29
+ });
30
+
31
+ it('should render with custom data-cy', () => {
32
+ mount(<MessageTimeout onResume={cy.stub()} onReset={cy.stub()} data-cy="custom-data-cy" />);
33
+
34
+ cy.getCy('custom-data-cy').should('be.visible');
35
+ cy.getCy('custom-data-cy-resume').should('be.visible');
36
+ cy.getCy('custom-data-cy-reset').should('be.visible');
37
+ });
38
+ });
@@ -0,0 +1,58 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="../../../cypress" />
3
+ import { mount } from 'cypress/react';
4
+ import { ChatParticipantIcon } from '../../../models/support-chat';
5
+ import { MessageTyping } from '../message-typing';
6
+
7
+ describe('ChatMessageTyping', () => {
8
+ function render(icon: ChatParticipantIcon) {
9
+ mount(
10
+ <MessageTyping
11
+ avatar={{
12
+ name: 'Test User Name',
13
+ icon,
14
+ className: 'custom-class',
15
+ }}
16
+ />
17
+ );
18
+ cy.getCy('chat-message-typing').should('be.visible').children().should('have.length', 2);
19
+ cy.getCy('chat-message-typing-dots').should('be.visible');
20
+ }
21
+
22
+ it('should render message with bot icon', () => {
23
+ render(ChatParticipantIcon.Bot);
24
+ cy.getCy('chat-avatar-bot')
25
+ .should('be.visible')
26
+ .should('have.class', 'custom-class')
27
+ .invoke('css', 'background-image')
28
+ .then(bgColor => {
29
+ const isSvg = Boolean(
30
+ (bgColor as any as string).indexOf('data:image/svg+xml;base64') >= 0
31
+ );
32
+ expect(isSvg).to.eq(true);
33
+ });
34
+ });
35
+
36
+ it('should render message with empty icon', () => {
37
+ render(ChatParticipantIcon.Empty);
38
+ cy.getCy('chat-avatar-empty').should('exist').should('not.be.visible');
39
+ });
40
+
41
+ it('should render message with initials', () => {
42
+ render(ChatParticipantIcon.Initials);
43
+ cy.getCy('chat-avatar-initials').should('be.visible').should('contain.text', 'T');
44
+ });
45
+
46
+ it('should not cancel the dots animation', () => {
47
+ cy.clock();
48
+ render(ChatParticipantIcon.Initials);
49
+ // Verify the rendered component + check the dots animation doesn't change over the time
50
+ cy.getCy('chat-message-typing-dots').should('be.visible');
51
+ cy.tick(10000);
52
+ cy.getCy('chat-message-typing-dots').should('be.visible');
53
+ cy.tick(20000);
54
+ cy.getCy('chat-message-typing-dots').should('be.visible');
55
+ cy.tick(30000);
56
+ cy.getCy('chat-message-typing-dots').should('be.visible');
57
+ });
58
+ });
@@ -0,0 +1,52 @@
1
+ // eslint-disable-next-line spaced-comment
2
+ /// <reference types="../../../cypress" />
3
+ import { BodyText } from '@servicetitan/design-system';
4
+ import { mount } from 'cypress/react';
5
+ import { MessageUser } from '../message-user';
6
+
7
+ describe('[MessageUser]', () => {
8
+ beforeEach(() => {
9
+ cy.viewport('macbook-13');
10
+ });
11
+
12
+ it('should be properly rendered', () => {
13
+ mount(
14
+ <MessageUser>
15
+ <BodyText data-cy="content">message user content</BodyText>
16
+ </MessageUser>
17
+ );
18
+ cy.getCy('chat-message-user').should('be.visible');
19
+ cy.getCy2('chat-message-normal').should('be.visible');
20
+ cy.getCy('chat-message-user-content')
21
+ .should('be.visible')
22
+ .should('contain.text', 'message user content');
23
+ });
24
+
25
+ it('should be properly rendered with long text', () => {
26
+ const longText = 'message user content '.repeat(100).trim();
27
+ mount(
28
+ <MessageUser>
29
+ <BodyText data-cy="content">{longText}</BodyText>
30
+ </MessageUser>
31
+ );
32
+ cy.getCy('chat-message-user-content').should('be.visible').should('contain.text', longText);
33
+ });
34
+
35
+ it('should be rendered with footer', () => {
36
+ mount(
37
+ <MessageUser messageFooter={<BodyText>message user footer</BodyText>}>
38
+ <BodyText data-cy="content">message user content</BodyText>
39
+ </MessageUser>
40
+ );
41
+ cy.getCy('chat-message-user-footer').should('be.visible');
42
+ });
43
+
44
+ it('should be rendered with error', () => {
45
+ mount(
46
+ <MessageUser isError>
47
+ <BodyText data-cy="content">message user content</BodyText>
48
+ </MessageUser>
49
+ );
50
+ cy.getCy2('chat-message-error').should('be.visible');
51
+ });
52
+ });
@@ -0,0 +1,59 @@
1
+ @import '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ @color-gradient-bot-start: #2270ee;
4
+ @color-gradient-bot-end: #8772e5;
5
+
6
+ /* stylelint-disable declaration-property-value-no-unknown */
7
+ .messageRoot {
8
+ display: grid;
9
+ max-width: 100%;
10
+ column-gap: @spacing-2;
11
+ row-gap: @spacing-1;
12
+ grid-template-areas:
13
+ 'avatar content .'
14
+ '. footer .';
15
+ grid-template-rows: auto auto;
16
+ grid-template-columns: minmax(@spacing-5, auto) 1fr minmax(@spacing-5, auto);
17
+
18
+ &.fullWidth {
19
+ width: 100%;
20
+ grid-template-areas:
21
+ 'avatar content'
22
+ '. footer ';
23
+ grid-template-rows: auto auto;
24
+ grid-template-columns: minmax(@spacing-5, auto) 1fr;
25
+ }
26
+ }
27
+
28
+ .messageFooter {
29
+ grid-area: footer;
30
+ }
31
+
32
+ .messageAvatar {
33
+ grid-area: avatar;
34
+ }
35
+
36
+ .messageContent {
37
+ max-width: 100%;
38
+ grid-area: content;
39
+ overflow-wrap: anywhere;
40
+ justify-self: flex-start;
41
+ }
42
+
43
+ .messageBubble {
44
+ color: @color-neutral-200;
45
+ padding: @spacing-2 @spacing-3;
46
+ background-color: @color-blue-100;
47
+ position: relative;
48
+ border-radius: @spacing-0 @spacing-3 @spacing-3 @spacing-3;
49
+ box-sizing: border-box;
50
+
51
+ &.error {
52
+ color: @color-neutral-400;
53
+ background-color: @color-red-100;
54
+
55
+ &::after {
56
+ background: @color-red-200;
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,9 @@
1
+ export const __esModule: true;
2
+ export const error: string;
3
+ export const fullWidth: string;
4
+ export const messageAvatar: string;
5
+ export const messageBubble: string;
6
+ export const messageContent: string;
7
+ export const messageFooter: string;
8
+ export const messageRoot: string;
9
+
@@ -0,0 +1,62 @@
1
+ import { Stack } from '@servicetitan/design-system';
2
+ import classNames from 'classnames';
3
+ import { FC, PropsWithChildren, ReactNode } from 'react';
4
+ import { IDataCyProps } from '../../models/component';
5
+ import * as Styles from './message-agent.module.less';
6
+ import { IMessageAvatarProps, MessageAvatar } from './message-avatar';
7
+
8
+ export interface IMessageAgentProps extends IDataCyProps {
9
+ avatar?: IMessageAvatarProps;
10
+ messageFooter?: ReactNode;
11
+ isError?: boolean;
12
+ fullWidth?: boolean;
13
+ subtle?: boolean;
14
+ }
15
+
16
+ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
17
+ avatar,
18
+ children,
19
+ fullWidth,
20
+ isError,
21
+ messageFooter,
22
+ subtle,
23
+ ...rest
24
+ }) => {
25
+ const dataCy = rest['data-cy'] ?? 'chat-message-agent';
26
+ const dataCy2 = isError ? 'chat-message-error' : 'chat-message-normal';
27
+ return (
28
+ <div
29
+ className={classNames(Styles.messageRoot, {
30
+ [Styles.fullWidth]: Boolean(fullWidth),
31
+ })}
32
+ data-cy={dataCy}
33
+ data-cy2={dataCy2}
34
+ >
35
+ <div className={Styles.messageAvatar}>
36
+ {avatar ? <MessageAvatar {...avatar} /> : <div />}
37
+ </div>
38
+ <Stack
39
+ direction="column"
40
+ spacing="1"
41
+ className={classNames(Styles.messageContent, {
42
+ 'w-100': Boolean(fullWidth),
43
+ })}
44
+ data-cy={`${dataCy}-center`}
45
+ >
46
+ {subtle ? (
47
+ children
48
+ ) : (
49
+ <div
50
+ className={classNames(Styles.messageBubble, {
51
+ [Styles.error]: Boolean(isError),
52
+ })}
53
+ data-cy={`${dataCy}-content`}
54
+ >
55
+ {children}
56
+ </div>
57
+ )}
58
+ {Boolean(messageFooter) && <div data-cy={`${dataCy}-footer`}>{messageFooter}</div>}
59
+ </Stack>
60
+ </div>
61
+ );
62
+ };
@@ -0,0 +1,26 @@
1
+ @import '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ .userIcon {
4
+ width: @spacing-5;
5
+ height: @spacing-5;
6
+ min-width: @spacing-5;
7
+ min-height: @spacing-5;
8
+ border-radius: @spacing-3;
9
+ background-color: @color-neutral-60;
10
+ font-size: @typescale-4;
11
+ color: @color-neutral-400;
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ position: relative;
16
+
17
+ &.bot {
18
+ background-image: url('../../assets/floating-chat-avatar.svg');
19
+ }
20
+
21
+ &.system {
22
+ min-height: @spacing-0;
23
+ height: @spacing-0;
24
+ background-color: transparent;
25
+ }
26
+ }
@@ -0,0 +1,5 @@
1
+ export const __esModule: true;
2
+ export const bot: string;
3
+ export const system: string;
4
+ export const userIcon: string;
5
+
@@ -0,0 +1,33 @@
1
+ import classNames from 'classnames';
2
+ import { FC } from 'react';
3
+ import { ChatParticipantIcon } from '../../models/support-chat';
4
+ import * as Styles from './message-avatar.module.less';
5
+
6
+ export interface IMessageAvatarProps {
7
+ className?: string;
8
+ name: string;
9
+ icon: ChatParticipantIcon;
10
+ }
11
+
12
+ export const MessageAvatar: FC<IMessageAvatarProps> = ({ className, icon, name }) => {
13
+ const isEmpty = icon === ChatParticipantIcon.Empty;
14
+ const isInitials = icon === ChatParticipantIcon.Initials;
15
+ const isBot = icon === ChatParticipantIcon.Bot;
16
+
17
+ return (
18
+ <div
19
+ className={classNames(
20
+ 'align-self-end',
21
+ Styles.userIcon,
22
+ {
23
+ [Styles.bot]: isBot,
24
+ [Styles.system]: isEmpty,
25
+ },
26
+ className
27
+ )}
28
+ data-cy={`chat-avatar-${icon}`}
29
+ >
30
+ {isInitials ? name : null}
31
+ </div>
32
+ );
33
+ };
@@ -0,0 +1,17 @@
1
+ import { Eyebrow } from '@servicetitan/design-system';
2
+ import { FC } from 'react';
3
+ import { formatChatMessageDate } from '../../utils/text-utils';
4
+
5
+ export interface IMessageFooterProps {
6
+ timestamp: Date;
7
+ name?: string;
8
+ }
9
+
10
+ export const MessageFooter: FC<IMessageFooterProps> = ({ name, timestamp }) => {
11
+ return (
12
+ <Eyebrow>
13
+ {name && `${name} • `}
14
+ {formatChatMessageDate(timestamp)}
15
+ </Eyebrow>
16
+ );
17
+ };
@@ -0,0 +1,26 @@
1
+ @import '@servicetitan/tokens/dist/tokens.less';
2
+
3
+ /* stylelint-disable declaration-property-value-no-unknown */
4
+ .messageRoot {
5
+ display: grid;
6
+ grid-template-areas: '. content .';
7
+ grid-template-rows: auto;
8
+ grid-template-columns: minmax(@spacing-5, auto) 1fr minmax(@spacing-5, auto);
9
+ column-gap: @spacing-2;
10
+ row-gap: @spacing-1;
11
+ width: 100%;
12
+
13
+ &.fullWidth {
14
+ grid-template:
15
+ 'content' auto
16
+ /
17
+ 1fr;
18
+ }
19
+ }
20
+
21
+ .messageContent {
22
+ grid-area: content;
23
+ overflow-wrap: anywhere;
24
+ justify-self: flex-start;
25
+ width: 100%;
26
+ }
@@ -0,0 +1,5 @@
1
+ export const __esModule: true;
2
+ export const fullWidth: string;
3
+ export const messageContent: string;
4
+ export const messageRoot: string;
5
+
@@ -0,0 +1,35 @@
1
+ import { Stack } from '@servicetitan/design-system';
2
+ import classNames from 'classnames';
3
+ import { FC, PropsWithChildren } from 'react';
4
+ import { IDataCyProps } from '../../models/component';
5
+ import * as Styles from './message-system.module.less';
6
+
7
+ export interface IMessageSystemProps extends IDataCyProps {
8
+ fullWidth?: boolean;
9
+ className?: string;
10
+ }
11
+
12
+ export const MessageSystem: FC<PropsWithChildren<IMessageSystemProps>> = ({
13
+ children,
14
+ className,
15
+ fullWidth,
16
+ ...rest
17
+ }) => {
18
+ const dataCy = rest['data-cy'] ?? 'chat-message-system';
19
+ return (
20
+ <div
21
+ className={classNames(
22
+ Styles.messageRoot,
23
+ {
24
+ [Styles.fullWidth]: Boolean(fullWidth),
25
+ },
26
+ className
27
+ )}
28
+ data-cy={dataCy}
29
+ >
30
+ <Stack direction="column" spacing="1" className={Styles.messageContent}>
31
+ {children}
32
+ </Stack>
33
+ </div>
34
+ );
35
+ };
@@ -0,0 +1,42 @@
1
+ import { BodyText, Divider, Link, Stack } from '@servicetitan/design-system';
2
+ import { FC, useCallback } from 'react';
3
+ import { IDataCyProps } from '../../models/component';
4
+ import { MessageSystem } from './message-system';
5
+
6
+ export interface IMessageTimeoutProps extends IDataCyProps {
7
+ onResume: () => void;
8
+ onReset: () => void;
9
+ }
10
+
11
+ export const MessageTimeout: FC<IMessageTimeoutProps> = ({ onReset, onResume, ...rest }) => {
12
+ const dataCy = rest['data-cy'] ?? 'chat-message-timeout';
13
+
14
+ const handleResume = useCallback(() => {
15
+ onResume();
16
+ }, [onResume]);
17
+
18
+ const handleReset = useCallback(() => {
19
+ onReset();
20
+ }, [onReset]);
21
+
22
+ return (
23
+ <MessageSystem data-cy={dataCy} fullWidth>
24
+ <Stack direction="column" spacing="2">
25
+ <Divider />
26
+ <BodyText el="div" className="ta-center">
27
+ Your session has timed out.
28
+ <br />
29
+ Would you like to resume it or start a new one?
30
+ <br />
31
+ <Link onClick={handleResume} primary data-cy={`${dataCy}-resume`}>
32
+ Continue session
33
+ </Link>{' '}
34
+ or{' '}
35
+ <Link onClick={handleReset} primary data-cy={`${dataCy}-reset`}>
36
+ Start new session
37
+ </Link>
38
+ </BodyText>
39
+ </Stack>
40
+ </MessageSystem>
41
+ );
42
+ };