@servicetitan/titan-chat-ui 1.1.1 → 2.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 (100) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/components/chat/__tests-cy__/chat-messages.test.js +69 -1
  3. package/dist/components/chat/__tests-cy__/chat-messages.test.js.map +1 -1
  4. package/dist/components/chat/chat-input.js +2 -2
  5. package/dist/components/chat/chat-input.js.map +1 -1
  6. package/dist/components/chat/chat-log.d.ts +8 -0
  7. package/dist/components/chat/chat-log.d.ts.map +1 -0
  8. package/dist/components/chat/chat-log.js +15 -0
  9. package/dist/components/chat/chat-log.js.map +1 -0
  10. package/dist/components/chat/chat-message-template-agent.d.ts.map +1 -1
  11. package/dist/components/chat/chat-message-template-agent.js +2 -2
  12. package/dist/components/chat/chat-message-template-agent.js.map +1 -1
  13. package/dist/components/chat/chat-message-template-user.js +2 -2
  14. package/dist/components/chat/chat-message-template-user.js.map +1 -1
  15. package/dist/components/chat/chat-message.d.ts +1 -5
  16. package/dist/components/chat/chat-message.d.ts.map +1 -1
  17. package/dist/components/chat/chat-message.js +4 -4
  18. package/dist/components/chat/chat-message.js.map +1 -1
  19. package/dist/components/chat/chat-messages.d.ts +3 -0
  20. package/dist/components/chat/chat-messages.d.ts.map +1 -1
  21. package/dist/components/chat/chat-messages.js +35 -7
  22. package/dist/components/chat/chat-messages.js.map +1 -1
  23. package/dist/components/chat/chat.d.ts.map +1 -1
  24. package/dist/components/chat/chat.js +3 -1
  25. package/dist/components/chat/chat.js.map +1 -1
  26. package/dist/components/messages/__tests-cy__/message-agent.test.js +1 -1
  27. package/dist/components/messages/__tests-cy__/message-agent.test.js.map +1 -1
  28. package/dist/components/messages/message-agent.d.ts +1 -0
  29. package/dist/components/messages/message-agent.d.ts.map +1 -1
  30. package/dist/components/messages/message-agent.js +2 -4
  31. package/dist/components/messages/message-agent.js.map +1 -1
  32. package/dist/components/messages/message-agent.module.less +29 -37
  33. package/dist/components/messages/message-footer.d.ts +1 -1
  34. package/dist/components/messages/message-footer.d.ts.map +1 -1
  35. package/dist/components/messages/message-footer.js +1 -1
  36. package/dist/components/messages/message-footer.js.map +1 -1
  37. package/dist/components/messages/message-user.js +2 -2
  38. package/dist/components/messages/message-user.js.map +1 -1
  39. package/dist/components/messages/message-user.module.less +3 -6
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +3 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/models/__mocks__/support-chat.mock.d.ts +9 -0
  45. package/dist/models/__mocks__/support-chat.mock.d.ts.map +1 -0
  46. package/dist/models/__mocks__/support-chat.mock.js +82 -0
  47. package/dist/models/__mocks__/support-chat.mock.js.map +1 -0
  48. package/dist/models/chat-customizations.d.ts +14 -6
  49. package/dist/models/chat-customizations.d.ts.map +1 -1
  50. package/dist/models/index.d.ts +1 -0
  51. package/dist/models/index.d.ts.map +1 -1
  52. package/dist/models/index.js +1 -0
  53. package/dist/models/index.js.map +1 -1
  54. package/dist/models/support-chat.d.ts +4 -4
  55. package/dist/models/support-chat.d.ts.map +1 -1
  56. package/dist/models/support-chat.js +18 -0
  57. package/dist/models/support-chat.js.map +1 -1
  58. package/dist/stores/__mocks-cy__/chat-ui.store.mock.d.ts +6 -1
  59. package/dist/stores/__mocks-cy__/chat-ui.store.mock.d.ts.map +1 -1
  60. package/dist/stores/__mocks-cy__/chat-ui.store.mock.js +30 -0
  61. package/dist/stores/__mocks-cy__/chat-ui.store.mock.js.map +1 -1
  62. package/dist/stores/__tests__/chat-input.store.test.d.ts +2 -0
  63. package/dist/stores/__tests__/chat-input.store.test.d.ts.map +1 -0
  64. package/dist/stores/__tests__/chat-input.store.test.js +32 -0
  65. package/dist/stores/__tests__/chat-input.store.test.js.map +1 -0
  66. package/dist/stores/chat-ui.store.d.ts +19 -10
  67. package/dist/stores/chat-ui.store.d.ts.map +1 -1
  68. package/dist/stores/chat-ui.store.js +47 -48
  69. package/dist/stores/chat-ui.store.js.map +1 -1
  70. package/dist/utils/test-utils.d.ts +5 -0
  71. package/dist/utils/test-utils.d.ts.map +1 -0
  72. package/dist/utils/test-utils.js +17 -0
  73. package/dist/utils/test-utils.js.map +1 -0
  74. package/package.json +2 -2
  75. package/src/components/chat/__tests-cy__/chat-messages.test.tsx +78 -1
  76. package/src/components/chat/chat-input.tsx +4 -4
  77. package/src/components/chat/chat-log.tsx +29 -0
  78. package/src/components/chat/chat-message-template-agent.tsx +7 -3
  79. package/src/components/chat/chat-message-template-user.tsx +3 -3
  80. package/src/components/chat/chat-message.tsx +58 -52
  81. package/src/components/chat/chat-messages.tsx +55 -11
  82. package/src/components/chat/chat.tsx +14 -8
  83. package/src/components/messages/__tests-cy__/message-agent.test.tsx +1 -1
  84. package/src/components/messages/message-agent.module.less +29 -37
  85. package/src/components/messages/message-agent.module.less.d.ts +0 -1
  86. package/src/components/messages/message-agent.tsx +4 -4
  87. package/src/components/messages/message-footer.tsx +4 -3
  88. package/src/components/messages/message-user.module.less +3 -6
  89. package/src/components/messages/message-user.tsx +5 -5
  90. package/src/index.ts +3 -0
  91. package/src/models/__mocks__/support-chat.mock.ts +105 -0
  92. package/src/models/chat-customizations.ts +17 -6
  93. package/src/models/index.ts +1 -0
  94. package/src/models/support-chat.ts +22 -10
  95. package/src/stores/__mocks-cy__/chat-ui.store.mock.ts +6 -0
  96. package/src/stores/__tests__/chat-input.store.test.ts +39 -0
  97. package/src/stores/chat-ui.store.ts +54 -55
  98. package/src/utils/test-utils.ts +22 -0
  99. package/tsconfig.json +1 -2
  100. package/tsconfig.tsbuildinfo +1 -1
@@ -2,8 +2,8 @@ import { BodyText } from '@servicetitan/design-system';
2
2
  import { useDependencies } from '@servicetitan/react-ioc';
3
3
  import { observer } from 'mobx-react';
4
4
  import { FC, PropsWithChildren, ReactNode } from 'react';
5
+ import { IChatMessageProps } from '../../models';
5
6
  import {
6
- ChatMessageModelBase,
7
7
  ChatMessageModelFile,
8
8
  ChatMessageModelText,
9
9
  ChatMessageModelWelcome,
@@ -14,65 +14,71 @@ import { MessageContentText } from '../message-content/message-content-text';
14
14
  import { ChatMessageTemplateAgent } from './chat-message-template-agent';
15
15
  import { ChatMessageTemplateUser } from './chat-message-template-user';
16
16
 
17
- interface IChatMessageProps {
18
- message: ChatMessageModelBase;
19
- }
20
-
21
17
  /**
22
18
  * ChatMessage component provides a default way to render chat messages in the chat UI and contains only most generic
23
19
  * components and templates/customizations rendering
24
20
  */
25
- export const ChatMessage: FC<IChatMessageProps> = observer(({ message }) => {
26
- const [chatUiStore] = useDependencies(CHAT_UI_STORE_TOKEN);
27
- const isAgent = message.participant.isAgent;
28
- const { messageTemplates, messages } = chatUiStore.customizations;
29
- const messageCustomization = messages?.find(c => c.predicate(message));
30
- const isSystem = Boolean(messageCustomization?.isSystem);
21
+ export const ChatMessage: FC<IChatMessageProps> = observer(
22
+ ({ message, omitAvatar, omitTimestamp }) => {
23
+ const [chatUiStore] = useDependencies(CHAT_UI_STORE_TOKEN);
24
+ const isAgent = message.participant.isAgent;
25
+ const { messageTemplates, messages } = chatUiStore.customizations;
26
+ const messageCustomization = messages?.find(c => c.predicate(message));
27
+ const isSystem = Boolean(messageCustomization?.isSystem);
31
28
 
32
- let messageContentNode: ReactNode | undefined;
33
- if (messageCustomization) {
34
- messageContentNode = <messageCustomization.component message={message} />;
35
- } else {
36
- // Default message content rendering
37
- switch (message.type) {
38
- case 'message':
39
- case 'welcome':
40
- messageContentNode = (
41
- <MessageContentText
42
- message={message as ChatMessageModelText | ChatMessageModelWelcome}
43
- />
44
- );
45
- break;
46
- case 'file':
47
- messageContentNode = (
48
- <MessageContentFile message={message as ChatMessageModelFile} />
49
- );
50
- break;
51
- default: {
52
- messageContentNode = <BodyText>Unknown message type: {message.type}</BodyText>;
29
+ let messageContentNode: ReactNode | undefined;
30
+ if (messageCustomization?.component) {
31
+ messageContentNode = <messageCustomization.component message={message} />;
32
+ } else {
33
+ // Default message content rendering
34
+ switch (message.type) {
35
+ case 'message':
36
+ case 'welcome':
37
+ messageContentNode = (
38
+ <MessageContentText
39
+ message={message as ChatMessageModelText | ChatMessageModelWelcome}
40
+ />
41
+ );
42
+ break;
43
+ case 'file':
44
+ messageContentNode = (
45
+ <MessageContentFile message={message as ChatMessageModelFile} />
46
+ );
47
+ break;
48
+ default: {
49
+ messageContentNode = <BodyText>Unknown message type: {message.type}</BodyText>;
50
+ }
53
51
  }
54
52
  }
55
- }
56
53
 
57
- if (!messageContentNode) {
58
- return null;
59
- }
54
+ if (!messageContentNode || messageCustomization?.isIgnored) {
55
+ return null;
56
+ }
60
57
 
61
- if (isSystem) {
62
- return messageContentNode;
63
- }
58
+ if (isSystem) {
59
+ return messageContentNode;
60
+ }
64
61
 
65
- let ComponentTemplate: FC<PropsWithChildren<IChatMessageProps>>;
66
- if (isAgent) {
67
- const template = messageTemplates?.agent;
68
- ComponentTemplate = template?.predicate(message)
69
- ? template.component
70
- : ChatMessageTemplateAgent;
71
- } else {
72
- const template = messageTemplates?.user;
73
- ComponentTemplate = template?.predicate(message)
74
- ? template.component
75
- : ChatMessageTemplateUser;
62
+ let ComponentTemplate: FC<PropsWithChildren<IChatMessageProps>>;
63
+ if (isAgent) {
64
+ const template = messageTemplates?.agent;
65
+ ComponentTemplate = template?.predicate(message)
66
+ ? template.component
67
+ : ChatMessageTemplateAgent;
68
+ } else {
69
+ const template = messageTemplates?.user;
70
+ ComponentTemplate = template?.predicate(message)
71
+ ? template.component
72
+ : ChatMessageTemplateUser;
73
+ }
74
+ return (
75
+ <ComponentTemplate
76
+ message={message}
77
+ omitAvatar={omitAvatar}
78
+ omitTimestamp={omitTimestamp}
79
+ >
80
+ {messageContentNode}
81
+ </ComponentTemplate>
82
+ );
76
83
  }
77
- return <ComponentTemplate message={message}>{messageContentNode}</ComponentTemplate>;
78
- });
84
+ );
@@ -1,23 +1,67 @@
1
1
  import { Stack } from '@servicetitan/design-system';
2
- import { useDependencies } from '@servicetitan/react-ioc';
3
- import { observer } from 'mobx-react';
4
- import { FC } from 'react';
5
- import { CHAT_UI_STORE_TOKEN } from '../../stores';
2
+ import { FC, useMemo } from 'react';
3
+ import { ChatMessageModelBase } from '../../models';
4
+ import { formatChatMessageDate } from '../../utils/text-utils';
6
5
  import { ChatMessage } from './chat-message';
7
6
  import { ChatMessageTyping } from './chat-message-typing';
8
7
 
9
8
  interface IChatMessagesProps {
10
9
  className?: string;
10
+ messages: ChatMessageModelBase[];
11
+ showTypingIndicator?: boolean;
11
12
  }
12
13
 
13
- export const ChatMessages: FC<IChatMessagesProps> = observer(({ className }) => {
14
- const [chatUiStore] = useDependencies(CHAT_UI_STORE_TOKEN);
14
+ export const ChatMessages: FC<IChatMessagesProps> = ({
15
+ className,
16
+ messages,
17
+ showTypingIndicator,
18
+ }) => {
19
+ // Split messages into chunks divided by participant name and message time (with 1 minute accuracy)
20
+ const messagesChunks = useMemo(
21
+ () =>
22
+ messages.reduce((acc: ChatMessageModelBase[][][], message) => {
23
+ if (!acc.length) {
24
+ acc.push([[message]]);
25
+ return acc;
26
+ }
27
+ const lastMessage = acc.at(-1)!.at(-1)!.at(-1)!;
28
+ if (lastMessage.participant.name !== message.participant.name) {
29
+ acc.push([[message]]);
30
+ } else if (
31
+ formatChatMessageDate(lastMessage.timestamp) !==
32
+ formatChatMessageDate(message.timestamp)
33
+ ) {
34
+ acc.at(-1)!.push([message]);
35
+ } else {
36
+ acc.at(-1)!.at(-1)!.push(message);
37
+ }
38
+ return acc;
39
+ }, []),
40
+ [messages]
41
+ );
42
+
15
43
  return (
16
44
  <Stack direction="column" spacing="2" className={className} data-cy="titan-chat-messages">
17
- {chatUiStore.messages.map(message => (
18
- <ChatMessage key={message.id} message={message} />
19
- ))}
20
- {chatUiStore.isAgentTyping && <ChatMessageTyping />}
45
+ {messagesChunks
46
+ .map(sameParticipantMessages => {
47
+ // Set omit avatar for all messages except the first one
48
+ const firstParticipantMessage = sameParticipantMessages[0][0];
49
+ return sameParticipantMessages.map(sameDateMessages => {
50
+ const lastTimestampMessage = sameDateMessages.at(-1)!;
51
+ return sameDateMessages.map(message => {
52
+ return (
53
+ <ChatMessage
54
+ key={message.id}
55
+ message={message}
56
+ omitAvatar={message !== firstParticipantMessage}
57
+ omitTimestamp={message !== lastTimestampMessage}
58
+ />
59
+ );
60
+ });
61
+ });
62
+ })
63
+ .flat(2)}
64
+ {showTypingIndicator && <ChatMessageTyping />}
21
65
  </Stack>
22
66
  );
23
- });
67
+ };
@@ -20,6 +20,7 @@ export const Chat: FC<IChatProps> = observer(({ className, customizations }) =>
20
20
  const [chatUiStore] = useDependencies(CHAT_UI_STORE_TOKEN);
21
21
 
22
22
  const footerComponent = customizations?.footerComponent;
23
+ const loadingComponent = customizations?.loadingComponent;
23
24
 
24
25
  useEffect(() => {
25
26
  chatUiStore.setCustomizationContext(customizations);
@@ -36,18 +37,23 @@ export const Chat: FC<IChatProps> = observer(({ className, customizations }) =>
36
37
  return (
37
38
  <Stack direction="column" className={className} data-cy="titan-chat">
38
39
  {chatUiStore.isStarting ? (
39
- <ChatConnecting className="p-3" />
40
+ (loadingComponent ?? <ChatConnecting className="p-x-3 p-y-2" />)
40
41
  ) : (
41
42
  <Fragment>
42
- <div ref={scrollRef} className="flex-grow-1 of-y-auto">
43
- <ChatMessages className="p-3" />
43
+ <div ref={scrollRef} className="p-x-3 p-y-2 flex-grow-1 of-y-auto">
44
+ <ChatMessages
45
+ messages={chatUiStore.messages}
46
+ showTypingIndicator={chatUiStore.isAgentTyping}
47
+ />
44
48
  </div>
45
49
  <ChatNotifications />
46
- <div>
47
- <ChatInputFile className="m-3 m-b-0 p-3 border-radius-1 border border-style-dashed" />
48
- <ChatInput className="p-x-3 p-y-2" />
49
- {Boolean(footerComponent) && footerComponent}
50
- </div>
50
+ {!chatUiStore.customizations?.input?.isDisabled && (
51
+ <Stack className="p-x-3 p-y-2" direction="column" spacing="2">
52
+ <ChatInputFile className="box-sizing-border-box border-radius-1 border border-style-dashed" />
53
+ <ChatInput />
54
+ {Boolean(footerComponent) && footerComponent}
55
+ </Stack>
56
+ )}
51
57
  </Fragment>
52
58
  )}
53
59
  </Stack>
@@ -44,7 +44,7 @@ describe('[MessageAgent]', () => {
44
44
  <BodyText>message agent content</BodyText>
45
45
  </MessageAgent>
46
46
  );
47
- cy.getCy('chat-message-agent-center').should('have.class', 'w-100');
47
+ cy.getCy('chat-message-agent-center').should('contain.text', 'message agent content');
48
48
  });
49
49
 
50
50
  it('should render with custom data-cy', () => {
@@ -9,51 +9,43 @@
9
9
  max-width: 100%;
10
10
  column-gap: @spacing-2;
11
11
  row-gap: @spacing-1;
12
- grid-template-areas:
13
- 'avatar content .'
14
- '. footer .';
15
- grid-template-rows: auto auto;
12
+ grid-template-areas: 'avatar content .';
13
+ grid-template-rows: auto;
16
14
  grid-template-columns: minmax(@spacing-5, auto) 1fr minmax(@spacing-5, auto);
17
15
 
18
16
  &.fullWidth {
19
- width: 100%;
20
- grid-template-areas:
21
- 'avatar content'
22
- '. footer ';
23
- grid-template-rows: auto auto;
17
+ //width: 100%;
18
+ grid-template-areas: 'avatar content';
19
+ grid-template-rows: auto;
24
20
  grid-template-columns: minmax(@spacing-5, auto) 1fr;
25
21
  }
26
- }
27
-
28
- .messageFooter {
29
- grid-area: footer;
30
- }
31
-
32
- .messageAvatar {
33
- grid-area: avatar;
34
- }
35
22
 
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;
23
+ .messageAvatar {
24
+ grid-area: avatar;
25
+ }
50
26
 
51
- &.error {
52
- color: @color-neutral-400;
53
- background-color: @color-red-100;
27
+ .messageContent {
28
+ max-width: 100%;
29
+ grid-area: content;
30
+ overflow-wrap: anywhere;
31
+ justify-self: flex-start;
32
+ }
54
33
 
55
- &::after {
56
- background: @color-red-200;
34
+ .messageBubble {
35
+ color: @color-neutral-200;
36
+ padding: @spacing-2 @spacing-3;
37
+ background-color: @color-blue-100;
38
+ position: relative;
39
+ border-radius: @spacing-0 @spacing-3 @spacing-3 @spacing-3;
40
+ box-sizing: border-box;
41
+
42
+ &.error {
43
+ color: @color-neutral-400;
44
+ background-color: @color-red-100;
45
+
46
+ &::after {
47
+ background: @color-red-200;
48
+ }
57
49
  }
58
50
  }
59
51
  }
@@ -4,6 +4,5 @@ export const fullWidth: string;
4
4
  export const messageAvatar: string;
5
5
  export const messageBubble: string;
6
6
  export const messageContent: string;
7
- export const messageFooter: string;
8
7
  export const messageRoot: string;
9
8
 
@@ -11,6 +11,7 @@ export interface IMessageAgentProps extends IDataCyProps {
11
11
  isError?: boolean;
12
12
  fullWidth?: boolean;
13
13
  subtle?: boolean;
14
+ omitAvatar?: boolean;
14
15
  }
15
16
 
16
17
  export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
@@ -19,6 +20,7 @@ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
19
20
  fullWidth,
20
21
  isError,
21
22
  messageFooter,
23
+ omitAvatar,
22
24
  subtle,
23
25
  ...rest
24
26
  }) => {
@@ -33,14 +35,12 @@ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
33
35
  data-cy2={dataCy2}
34
36
  >
35
37
  <div className={Styles.messageAvatar}>
36
- {avatar ? <MessageAvatar {...avatar} /> : <div />}
38
+ {!omitAvatar && avatar ? <MessageAvatar {...avatar} /> : <div />}
37
39
  </div>
38
40
  <Stack
39
41
  direction="column"
40
42
  spacing="1"
41
- className={classNames(Styles.messageContent, {
42
- 'w-100': Boolean(fullWidth),
43
- })}
43
+ className={classNames(Styles.messageContent)}
44
44
  data-cy={`${dataCy}-center`}
45
45
  >
46
46
  {subtle ? (
@@ -3,15 +3,16 @@ import { FC } from 'react';
3
3
  import { formatChatMessageDate } from '../../utils/text-utils';
4
4
 
5
5
  export interface IMessageFooterProps {
6
- timestamp: Date;
6
+ timestamp?: Date;
7
7
  name?: string;
8
8
  }
9
9
 
10
10
  export const MessageFooter: FC<IMessageFooterProps> = ({ name, timestamp }) => {
11
11
  return (
12
12
  <Eyebrow>
13
- {name && `${name} • `}
14
- {formatChatMessageDate(timestamp)}
13
+ {Boolean(name) && name}
14
+ {name && timestamp && ' • '}
15
+ {timestamp && formatChatMessageDate(timestamp)}
15
16
  </Eyebrow>
16
17
  );
17
18
  };
@@ -3,18 +3,15 @@
3
3
  /* stylelint-disable declaration-property-value-no-unknown */
4
4
  .messageRoot {
5
5
  display: grid;
6
- grid-template-areas:
7
- '. content'
8
- '. footer';
9
- grid-template-rows: auto auto;
6
+ grid-template-areas: '. content';
7
+ grid-template-rows: auto;
10
8
  grid-template-columns: minmax(@spacing-5, auto) 1fr;
11
9
  column-gap: @spacing-2;
12
10
  row-gap: @spacing-1;
13
11
  }
14
12
 
15
13
  .messageFooter {
16
- grid-area: footer;
17
- justify-self: end;
14
+ text-align: right;
18
15
  }
19
16
 
20
17
  .messageContent {
@@ -38,12 +38,12 @@ export const MessageUser: FC<PropsWithChildren<IMessageUserProps>> = ({
38
38
  >
39
39
  {children}
40
40
  </div>
41
+ {Boolean(messageFooter) && (
42
+ <div className={Styles.messageFooter} data-cy={`${dataCy}-footer`}>
43
+ {messageFooter}
44
+ </div>
45
+ )}
41
46
  </Stack>
42
- {Boolean(messageFooter) && (
43
- <div className={Styles.messageFooter} data-cy={`${dataCy}-footer`}>
44
- {messageFooter}
45
- </div>
46
- )}
47
47
  </div>
48
48
  );
49
49
  };
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ export { ChatMessages } from './components/chat/chat-messages';
2
+ export { Chat, IChatProps } from './components/chat/chat';
3
+ export { ChatLog } from './components/chat/chat-log';
1
4
  export { MessageAgent, IMessageAgentProps } from './components/messages/message-agent';
2
5
  export { MessageUser, IMessageUserProps } from './components/messages/message-user';
3
6
  export { MessageSystem, IMessageSystemProps } from './components/messages/message-system';
@@ -0,0 +1,105 @@
1
+ import {
2
+ ChatMessageModelFile,
3
+ ChatMessageModelText,
4
+ ChatMessageModelTimeout,
5
+ ChatMessageModelWelcome,
6
+ ChatMessageState,
7
+ ChatParticipantIcon,
8
+ ChatParticipantModel,
9
+ } from '../support-chat';
10
+
11
+ export function mockChatAgent(overrides?: Partial<ChatParticipantModel>): ChatParticipantModel {
12
+ return {
13
+ isAgent: true,
14
+ name: 'agent name',
15
+ icon: ChatParticipantIcon.Bot,
16
+ ...overrides,
17
+ };
18
+ }
19
+
20
+ export function mockChatUser(overrides?: Partial<ChatParticipantModel>): ChatParticipantModel {
21
+ return {
22
+ isAgent: false,
23
+ name: 'user name',
24
+ icon: ChatParticipantIcon.Initials,
25
+ ...overrides,
26
+ };
27
+ }
28
+
29
+ export function mockChatMessageModelText(
30
+ isAgent: boolean,
31
+ overrides?: Partial<ChatMessageModelText>
32
+ ): ChatMessageModelText {
33
+ return {
34
+ id: 'id',
35
+ type: 'message',
36
+ message: 'message',
37
+ timestamp: new Date('2000-01-01T00:00:00Z'),
38
+ state: ChatMessageState.Delivered,
39
+ data: {},
40
+ participant: isAgent ? mockChatAgent() : mockChatUser(),
41
+ ...overrides,
42
+ };
43
+ }
44
+
45
+ export function mockChatMessageModelWelcome(
46
+ overrides?: Partial<ChatMessageModelWelcome>
47
+ ): ChatMessageModelWelcome {
48
+ return {
49
+ id: 'id',
50
+ type: 'welcome',
51
+ message: 'message welcome',
52
+ timestamp: new Date('2000-01-01T00:00:00Z'),
53
+ state: ChatMessageState.Delivered,
54
+ data: {},
55
+ participant: mockChatAgent(),
56
+ ...overrides,
57
+ };
58
+ }
59
+
60
+ export function mockChatMessageModelFile(
61
+ overrides?: Partial<ChatMessageModelFile>
62
+ ): ChatMessageModelFile {
63
+ return {
64
+ id: 'id',
65
+ type: 'file',
66
+ timestamp: new Date('2000-01-01T00:00:00Z'),
67
+ state: ChatMessageState.Delivered,
68
+ fileName: 'file name',
69
+ data: {},
70
+ participant: mockChatUser(),
71
+ ...overrides,
72
+ };
73
+ }
74
+
75
+ export function mockChatMessageModelTimeout(
76
+ overrides?: Partial<ChatMessageModelTimeout>
77
+ ): ChatMessageModelTimeout {
78
+ return {
79
+ id: 'id',
80
+ type: 'timeout',
81
+ timestamp: new Date('2000-01-01T00:00:00Z'),
82
+ state: ChatMessageState.Delivered,
83
+ data: {},
84
+ participant: mockChatAgent(),
85
+ ...overrides,
86
+ };
87
+ }
88
+
89
+ export function mockChatConversation() {
90
+ return [
91
+ mockChatMessageModelWelcome(),
92
+ mockChatMessageModelText(false, {
93
+ message: 'question 1',
94
+ }),
95
+ mockChatMessageModelText(true, {
96
+ message: 'answer 1',
97
+ }),
98
+ mockChatMessageModelText(false, {
99
+ message: 'question 2',
100
+ }),
101
+ mockChatMessageModelText(true, {
102
+ message: 'answer 2',
103
+ }),
104
+ ];
105
+ }
@@ -4,11 +4,15 @@ import { ChatMessageModelBase } from './support-chat';
4
4
 
5
5
  export interface IChatMessageProps<T extends ChatMessageModelBase = ChatMessageModelBase> {
6
6
  message: T;
7
+ omitAvatar?: boolean;
8
+ omitTimestamp?: boolean;
7
9
  }
8
10
 
9
- export interface ChatMessageTemplateCustomization {
10
- component: FC<PropsWithChildren<IChatMessageProps>>;
11
- predicate: (message: ChatMessageModelBase) => boolean;
11
+ export interface ChatMessageTemplateCustomization<
12
+ T extends ChatMessageModelBase = ChatMessageModelBase,
13
+ > {
14
+ component: FC<PropsWithChildren<IChatMessageProps<T>>>;
15
+ predicate: (message: T) => boolean;
12
16
  }
13
17
 
14
18
  export type ChatMessageTemplateCustomizations = Partial<{
@@ -16,19 +20,26 @@ export type ChatMessageTemplateCustomizations = Partial<{
16
20
  user: ChatMessageTemplateCustomization;
17
21
  }>;
18
22
 
19
- export interface ChatMessageCustomization {
20
- component: FC<IChatMessageProps>;
21
- predicate: (message: ChatMessageModelBase) => boolean;
23
+ export interface ChatMessageCustomization<T extends ChatMessageModelBase = ChatMessageModelBase> {
24
+ predicate: (message: T) => boolean;
25
+ component?: FC<IChatMessageProps<T>>;
22
26
  isSystem?: boolean; // System messages are messages that are not sent by the user or the agent and rendered without chat bubbles
27
+ isIgnored?: boolean;
23
28
  }
24
29
 
25
30
  export interface ChatMessageTypingCustomization {
26
31
  component: FC<IMessageTypingProps>;
27
32
  }
28
33
 
34
+ export interface ChatInputCustomization {
35
+ isDisabled?: boolean;
36
+ }
37
+
29
38
  export type ChatCustomizations = Partial<{
30
39
  messageTemplates: ChatMessageTemplateCustomizations;
31
40
  messages: ChatMessageCustomization[];
32
41
  messageTyping: ChatMessageTypingCustomization;
42
+ input: ChatInputCustomization;
33
43
  footerComponent: ReactNode;
44
+ loadingComponent: ReactNode;
34
45
  }>;
@@ -1,2 +1,3 @@
1
1
  export * from './support-chat';
2
+ export * from './__mocks__/support-chat.mock';
2
3
  export * from './chat-customizations';