@servicetitan/titan-chat-ui 2.0.0 → 2.0.2

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 +25 -0
  2. package/dist/components/chat/__tests-cy__/chat-messages.test.js +14 -13
  3. package/dist/components/chat/__tests-cy__/chat-messages.test.js.map +1 -1
  4. package/dist/components/chat/__tests-cy__/chat.test.js +21 -20
  5. package/dist/components/chat/__tests-cy__/chat.test.js.map +1 -1
  6. package/dist/components/chat/chat-error.js +1 -1
  7. package/dist/components/chat/chat-error.js.map +1 -1
  8. package/dist/components/chat/chat-message-template-user.js +1 -1
  9. package/dist/components/chat/chat-message-template-user.js.map +1 -1
  10. package/dist/components/chat/chat.js +1 -1
  11. package/dist/components/chat/chat.js.map +1 -1
  12. package/dist/components/messages/__tests-cy__/message-agent.test.js +13 -22
  13. package/dist/components/messages/__tests-cy__/message-agent.test.js.map +1 -1
  14. package/dist/components/messages/__tests-cy__/message-system.test.js +3 -2
  15. package/dist/components/messages/__tests-cy__/message-system.test.js.map +1 -1
  16. package/dist/components/messages/__tests-cy__/message-timeout.test.js +6 -11
  17. package/dist/components/messages/__tests-cy__/message-timeout.test.js.map +1 -1
  18. package/dist/components/messages/__tests-cy__/message-typing.test.js +10 -9
  19. package/dist/components/messages/__tests-cy__/message-typing.test.js.map +1 -1
  20. package/dist/components/messages/__tests-cy__/message-user.test.js +5 -9
  21. package/dist/components/messages/__tests-cy__/message-user.test.js.map +1 -1
  22. package/dist/components/messages/message-agent.d.ts +3 -2
  23. package/dist/components/messages/message-agent.d.ts.map +1 -1
  24. package/dist/components/messages/message-agent.js +4 -5
  25. package/dist/components/messages/message-agent.js.map +1 -1
  26. package/dist/components/messages/message-avatar.d.ts.map +1 -1
  27. package/dist/components/messages/message-avatar.js +5 -1
  28. package/dist/components/messages/message-avatar.js.map +1 -1
  29. package/dist/components/messages/message-system.d.ts +2 -2
  30. package/dist/components/messages/message-system.d.ts.map +1 -1
  31. package/dist/components/messages/message-system.js +2 -2
  32. package/dist/components/messages/message-system.js.map +1 -1
  33. package/dist/components/messages/message-timeout.d.ts +1 -2
  34. package/dist/components/messages/message-timeout.d.ts.map +1 -1
  35. package/dist/components/messages/message-timeout.js +2 -4
  36. package/dist/components/messages/message-timeout.js.map +1 -1
  37. package/dist/components/messages/message-typing.d.ts +1 -2
  38. package/dist/components/messages/message-typing.d.ts.map +1 -1
  39. package/dist/components/messages/message-typing.js +1 -5
  40. package/dist/components/messages/message-typing.js.map +1 -1
  41. package/dist/components/messages/message-user.d.ts +1 -2
  42. package/dist/components/messages/message-user.d.ts.map +1 -1
  43. package/dist/components/messages/message-user.js +3 -6
  44. package/dist/components/messages/message-user.js.map +1 -1
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/stores/__tests__/chat-input.store.test.js +5 -1
  50. package/dist/stores/__tests__/chat-input.store.test.js.map +1 -1
  51. package/dist/stores/__tests__/chat-ui.store.test.d.ts +2 -0
  52. package/dist/stores/__tests__/chat-ui.store.test.d.ts.map +1 -0
  53. package/dist/stores/__tests__/chat-ui.store.test.js +424 -0
  54. package/dist/stores/__tests__/chat-ui.store.test.js.map +1 -0
  55. package/dist/stores/chat-ui.store.d.ts.map +1 -1
  56. package/dist/stores/chat-ui.store.js +9 -10
  57. package/dist/stores/chat-ui.store.js.map +1 -1
  58. package/dist/utils/__tests__/text-utils.test.d.ts +2 -0
  59. package/dist/utils/__tests__/text-utils.test.d.ts.map +1 -0
  60. package/dist/utils/__tests__/text-utils.test.js +59 -0
  61. package/dist/utils/__tests__/text-utils.test.js.map +1 -0
  62. package/dist/utils/text-utils.d.ts +0 -5
  63. package/dist/utils/text-utils.d.ts.map +1 -1
  64. package/dist/utils/text-utils.js +2 -51
  65. package/dist/utils/text-utils.js.map +1 -1
  66. package/package.json +9 -3
  67. package/src/components/chat/__tests-cy__/chat-messages.test.tsx +17 -17
  68. package/src/components/chat/__tests-cy__/chat.test.tsx +21 -20
  69. package/src/components/chat/chat-error.tsx +1 -1
  70. package/src/components/chat/chat-message-template-user.tsx +2 -2
  71. package/src/components/chat/chat.tsx +1 -1
  72. package/src/components/messages/__tests-cy__/message-agent.test.tsx +13 -31
  73. package/src/components/messages/__tests-cy__/message-system.test.tsx +3 -2
  74. package/src/components/messages/__tests-cy__/message-timeout.test.tsx +6 -13
  75. package/src/components/messages/__tests-cy__/message-typing.test.tsx +10 -9
  76. package/src/components/messages/__tests-cy__/message-user.test.tsx +5 -14
  77. package/src/components/messages/message-agent.tsx +18 -11
  78. package/src/components/messages/message-avatar.tsx +7 -3
  79. package/src/components/messages/message-system.tsx +5 -4
  80. package/src/components/messages/message-timeout.tsx +9 -8
  81. package/src/components/messages/message-typing.tsx +12 -16
  82. package/src/components/messages/message-user.tsx +7 -10
  83. package/src/index.ts +1 -0
  84. package/src/stores/__tests__/chat-input.store.test.ts +5 -1
  85. package/src/stores/__tests__/chat-ui.store.test.ts +531 -0
  86. package/src/stores/chat-ui.store.ts +9 -10
  87. package/src/utils/__tests__/text-utils.test.ts +70 -0
  88. package/src/utils/text-utils.ts +2 -59
  89. package/tsconfig.json +5 -0
  90. package/tsconfig.tsbuildinfo +1 -1
  91. package/dist/models/component.d.ts +0 -4
  92. package/dist/models/component.d.ts.map +0 -1
  93. package/dist/models/component.js +0 -2
  94. package/dist/models/component.js.map +0 -1
  95. package/dist/utils/test-utils.d.ts +0 -5
  96. package/dist/utils/test-utils.d.ts.map +0 -1
  97. package/dist/utils/test-utils.js +0 -17
  98. package/dist/utils/test-utils.js.map +0 -1
  99. package/src/models/component.ts +0 -3
  100. package/src/utils/test-utils.ts +0 -22
@@ -1,5 +1,6 @@
1
1
  // eslint-disable-next-line spaced-comment
2
2
  /// <reference types="../../../cypress" />
3
+ import { ChatUiSelectors } from '@local/utils';
3
4
  import { BodyText } from '@servicetitan/design-system';
4
5
  import { mount } from 'cypress/react';
5
6
  import { MessageUser } from '../message-user';
@@ -15,9 +16,8 @@ describe('[MessageUser]', () => {
15
16
  <BodyText data-cy="content">message user content</BodyText>
16
17
  </MessageUser>
17
18
  );
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')
19
+ ChatUiSelectors.chatMessageUser.should('be.visible');
20
+ ChatUiSelectors.chatMessageContent
21
21
  .should('be.visible')
22
22
  .should('contain.text', 'message user content');
23
23
  });
@@ -29,7 +29,7 @@ describe('[MessageUser]', () => {
29
29
  <BodyText data-cy="content">{longText}</BodyText>
30
30
  </MessageUser>
31
31
  );
32
- cy.getCy('chat-message-user-content').should('be.visible').should('contain.text', longText);
32
+ ChatUiSelectors.chatMessageContent.should('be.visible').should('contain.text', longText);
33
33
  });
34
34
 
35
35
  it('should be rendered with footer', () => {
@@ -38,15 +38,6 @@ describe('[MessageUser]', () => {
38
38
  <BodyText data-cy="content">message user content</BodyText>
39
39
  </MessageUser>
40
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');
41
+ ChatUiSelectors.chatMessageFooter.should('be.visible');
51
42
  });
52
43
  });
@@ -1,22 +1,24 @@
1
1
  import { Stack } from '@servicetitan/design-system';
2
2
  import classNames from 'classnames';
3
3
  import { FC, PropsWithChildren, ReactNode } from 'react';
4
- import { IDataCyProps } from '../../models/component';
5
4
  import * as Styles from './message-agent.module.less';
6
5
  import { IMessageAvatarProps, MessageAvatar } from './message-avatar';
7
6
 
8
- export interface IMessageAgentProps extends IDataCyProps {
7
+ export interface IMessageAgentProps {
9
8
  avatar?: IMessageAvatarProps;
10
9
  messageFooter?: ReactNode;
11
10
  isError?: boolean;
12
11
  fullWidth?: boolean;
13
12
  subtle?: boolean;
14
13
  omitAvatar?: boolean;
14
+ className?: string;
15
+ ['data-cy2']?: string;
15
16
  }
16
17
 
17
18
  export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
18
19
  avatar,
19
20
  children,
21
+ className,
20
22
  fullWidth,
21
23
  isError,
22
24
  messageFooter,
@@ -24,14 +26,17 @@ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
24
26
  subtle,
25
27
  ...rest
26
28
  }) => {
27
- const dataCy = rest['data-cy'] ?? 'chat-message-agent';
28
- const dataCy2 = isError ? 'chat-message-error' : 'chat-message-normal';
29
+ const dataCy2 = rest['data-cy2'] ?? 'titan-chat-message-agent';
29
30
  return (
30
31
  <div
31
- className={classNames(Styles.messageRoot, {
32
- [Styles.fullWidth]: Boolean(fullWidth),
33
- })}
34
- data-cy={dataCy}
32
+ className={classNames(
33
+ Styles.messageRoot,
34
+ {
35
+ [Styles.fullWidth]: Boolean(fullWidth),
36
+ },
37
+ className
38
+ )}
39
+ data-cy="titan-chat-message"
35
40
  data-cy2={dataCy2}
36
41
  >
37
42
  <div className={Styles.messageAvatar}>
@@ -41,7 +46,8 @@ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
41
46
  direction="column"
42
47
  spacing="1"
43
48
  className={classNames(Styles.messageContent)}
44
- data-cy={`${dataCy}-center`}
49
+ data-cy="titan-chat-message-content"
50
+ data-cy2="titan-chat-message-content-agent"
45
51
  >
46
52
  {subtle ? (
47
53
  children
@@ -50,12 +56,13 @@ export const MessageAgent: FC<PropsWithChildren<IMessageAgentProps>> = ({
50
56
  className={classNames(Styles.messageBubble, {
51
57
  [Styles.error]: Boolean(isError),
52
58
  })}
53
- data-cy={`${dataCy}-content`}
54
59
  >
55
60
  {children}
56
61
  </div>
57
62
  )}
58
- {Boolean(messageFooter) && <div data-cy={`${dataCy}-footer`}>{messageFooter}</div>}
63
+ {Boolean(messageFooter) && (
64
+ <div data-cy="titan-chat-message-footer">{messageFooter}</div>
65
+ )}
59
66
  </Stack>
60
67
  </div>
61
68
  );
@@ -1,6 +1,8 @@
1
+ import { BodyText } from '@servicetitan/design-system';
1
2
  import classNames from 'classnames';
2
- import { FC } from 'react';
3
+ import { FC, useMemo } from 'react';
3
4
  import { ChatParticipantIcon } from '../../models/support-chat';
5
+ import { getNameInitials } from '../../utils/text-utils';
4
6
  import * as Styles from './message-avatar.module.less';
5
7
 
6
8
  export interface IMessageAvatarProps {
@@ -13,6 +15,7 @@ export const MessageAvatar: FC<IMessageAvatarProps> = ({ className, icon, name }
13
15
  const isEmpty = icon === ChatParticipantIcon.Empty;
14
16
  const isInitials = icon === ChatParticipantIcon.Initials;
15
17
  const isBot = icon === ChatParticipantIcon.Bot;
18
+ const nameInitials = useMemo(() => getNameInitials(name), [name]);
16
19
 
17
20
  return (
18
21
  <div
@@ -25,9 +28,10 @@ export const MessageAvatar: FC<IMessageAvatarProps> = ({ className, icon, name }
25
28
  },
26
29
  className
27
30
  )}
28
- data-cy={`chat-avatar-${icon}`}
31
+ data-cy="titan-chat-avatar"
32
+ data-cy2={`titan-chat-avatar-${icon}`}
29
33
  >
30
- {isInitials ? name : null}
34
+ <BodyText el="span">{isInitials ? nameInitials : null}</BodyText>
31
35
  </div>
32
36
  );
33
37
  };
@@ -1,12 +1,12 @@
1
1
  import { Stack } from '@servicetitan/design-system';
2
2
  import classNames from 'classnames';
3
3
  import { FC, PropsWithChildren } from 'react';
4
- import { IDataCyProps } from '../../models/component';
5
4
  import * as Styles from './message-system.module.less';
6
5
 
7
- export interface IMessageSystemProps extends IDataCyProps {
6
+ export interface IMessageSystemProps {
8
7
  fullWidth?: boolean;
9
8
  className?: string;
9
+ ['data-cy2']?: string;
10
10
  }
11
11
 
12
12
  export const MessageSystem: FC<PropsWithChildren<IMessageSystemProps>> = ({
@@ -15,7 +15,7 @@ export const MessageSystem: FC<PropsWithChildren<IMessageSystemProps>> = ({
15
15
  fullWidth,
16
16
  ...rest
17
17
  }) => {
18
- const dataCy = rest['data-cy'] ?? 'chat-message-system';
18
+ const dataCy2 = rest['data-cy2'] ?? 'titan-chat-message-system';
19
19
  return (
20
20
  <div
21
21
  className={classNames(
@@ -25,7 +25,8 @@ export const MessageSystem: FC<PropsWithChildren<IMessageSystemProps>> = ({
25
25
  },
26
26
  className
27
27
  )}
28
- data-cy={dataCy}
28
+ data-cy="titan-chat-message"
29
+ data-cy2={dataCy2}
29
30
  >
30
31
  <Stack direction="column" spacing="1" className={Styles.messageContent}>
31
32
  {children}
@@ -1,16 +1,13 @@
1
1
  import { BodyText, Divider, Link, Stack } from '@servicetitan/design-system';
2
2
  import { FC, useCallback } from 'react';
3
- import { IDataCyProps } from '../../models/component';
4
3
  import { MessageSystem } from './message-system';
5
4
 
6
- export interface IMessageTimeoutProps extends IDataCyProps {
5
+ export interface IMessageTimeoutProps {
7
6
  onResume: () => void;
8
7
  onReset: () => void;
9
8
  }
10
9
 
11
- export const MessageTimeout: FC<IMessageTimeoutProps> = ({ onReset, onResume, ...rest }) => {
12
- const dataCy = rest['data-cy'] ?? 'chat-message-timeout';
13
-
10
+ export const MessageTimeout: FC<IMessageTimeoutProps> = ({ onReset, onResume }) => {
14
11
  const handleResume = useCallback(() => {
15
12
  onResume();
16
13
  }, [onResume]);
@@ -20,7 +17,7 @@ export const MessageTimeout: FC<IMessageTimeoutProps> = ({ onReset, onResume, ..
20
17
  }, [onReset]);
21
18
 
22
19
  return (
23
- <MessageSystem data-cy={dataCy} fullWidth>
20
+ <MessageSystem data-cy2="titan-chat-message-timeout" fullWidth>
24
21
  <Stack direction="column" spacing="2">
25
22
  <Divider />
26
23
  <BodyText el="div" className="ta-center">
@@ -28,11 +25,15 @@ export const MessageTimeout: FC<IMessageTimeoutProps> = ({ onReset, onResume, ..
28
25
  <br />
29
26
  Would you like to resume it or start a new one?
30
27
  <br />
31
- <Link onClick={handleResume} primary data-cy={`${dataCy}-resume`}>
28
+ <Link
29
+ onClick={handleResume}
30
+ primary
31
+ data-cy="titan-chat-message-timeout-resume"
32
+ >
32
33
  Continue session
33
34
  </Link>{' '}
34
35
  or{' '}
35
- <Link onClick={handleReset} primary data-cy={`${dataCy}-reset`}>
36
+ <Link onClick={handleReset} primary data-cy="titan-chat-message-timeout-reset">
36
37
  Start new session
37
38
  </Link>
38
39
  </BodyText>
@@ -1,25 +1,21 @@
1
1
  import { Stack } from '@servicetitan/design-system';
2
2
  import { FC } from 'react';
3
- import { IDataCyProps } from '../../models/component';
4
3
  import { MessageAgent } from './message-agent';
5
4
  import { IMessageAvatarProps } from './message-avatar';
6
5
  import * as Styles from './message-typing.module.less';
7
6
 
8
- export interface IMessageTypingProps extends IDataCyProps {
7
+ export interface IMessageTypingProps {
9
8
  avatar: IMessageAvatarProps;
10
9
  }
11
10
 
12
- export const MessageTyping: FC<IMessageTypingProps> = ({ avatar, ...rest }) => {
13
- const dataCy = rest['data-cy'] ?? 'chat-message-typing';
14
- return (
15
- <MessageAgent avatar={avatar} data-cy={dataCy} subtle>
16
- <Stack className="h-100" alignItems="center">
17
- <div className={Styles.dotsContainer} data-cy={`${dataCy}-dots`}>
18
- <span className={Styles.dot} />
19
- <span className={Styles.dot} />
20
- <span className={Styles.dot} />
21
- </div>
22
- </Stack>
23
- </MessageAgent>
24
- );
25
- };
11
+ export const MessageTyping: FC<IMessageTypingProps> = ({ avatar }) => (
12
+ <MessageAgent avatar={avatar} data-cy2="titan-chat-message-typing" subtle>
13
+ <Stack className="h-100" alignItems="center">
14
+ <div className={Styles.dotsContainer} data-cy="titan-chat-message-typing-dots">
15
+ <span className={Styles.dot} />
16
+ <span className={Styles.dot} />
17
+ <span className={Styles.dot} />
18
+ </div>
19
+ </Stack>
20
+ </MessageAgent>
21
+ );
@@ -1,10 +1,9 @@
1
1
  import { Stack } from '@servicetitan/design-system';
2
2
  import classNames from 'classnames';
3
3
  import { FC, PropsWithChildren, ReactNode } from 'react';
4
- import { IDataCyProps } from '../../models/component';
5
4
  import * as Styles from './message-user.module.less';
6
5
 
7
- export interface IMessageUserProps extends IDataCyProps {
6
+ export interface IMessageUserProps {
8
7
  messageFooter?: ReactNode;
9
8
  isError?: boolean;
10
9
  className?: string;
@@ -15,15 +14,12 @@ export const MessageUser: FC<PropsWithChildren<IMessageUserProps>> = ({
15
14
  className,
16
15
  isError,
17
16
  messageFooter,
18
- ...rest
19
17
  }) => {
20
- const dataCy = rest['data-cy'] ?? 'chat-message-user';
21
- const dataCy2 = isError ? 'chat-message-error' : 'chat-message-normal';
22
18
  return (
23
19
  <div
24
- className={classNames(Styles.messageRoot, 'max-w-100')}
25
- data-cy={dataCy}
26
- data-cy2={dataCy2}
20
+ className={classNames(Styles.messageRoot, 'max-w-100', className)}
21
+ data-cy="titan-chat-message"
22
+ data-cy2="titan-chat-message-user"
27
23
  >
28
24
  <Stack
29
25
  direction="column"
@@ -34,12 +30,13 @@ export const MessageUser: FC<PropsWithChildren<IMessageUserProps>> = ({
34
30
  className={classNames(Styles.messageBubble, {
35
31
  [Styles.error]: isError,
36
32
  })}
37
- data-cy={`${dataCy}-content`}
33
+ data-cy="titan-chat-message-content"
34
+ data-cy2="titan-chat-message-content-user"
38
35
  >
39
36
  {children}
40
37
  </div>
41
38
  {Boolean(messageFooter) && (
42
- <div className={Styles.messageFooter} data-cy={`${dataCy}-footer`}>
39
+ <div className={Styles.messageFooter} data-cy="titan-chat-message-footer">
43
40
  {messageFooter}
44
41
  </div>
45
42
  )}
package/src/index.ts CHANGED
@@ -12,3 +12,4 @@ export { useAvatarProps } from './components/messages/use-avatar-props';
12
12
  export { MultilineText } from './components/common/multiline-text';
13
13
  export * from './stores';
14
14
  export * from './models';
15
+ export * from './utils/text-utils';
@@ -1,6 +1,6 @@
1
1
  import { expect } from '@jest/globals';
2
+ import { initTestContainer } from '@local/utils';
2
3
  import { Container } from '@servicetitan/react-ioc';
3
- import { initTestContainer } from '../../utils/test-utils';
4
4
  import { ChatInputStore } from '../chat-input.store';
5
5
 
6
6
  const initContainer = initTestContainer(ChatInputStore, () => {});
@@ -19,21 +19,25 @@ describe('[ChatInputStore]', () => {
19
19
  expect(store.formState.$.message.value).toBe('');
20
20
  const validateResult = await store.formState.validate();
21
21
  expect(validateResult.hasError).toBe(false);
22
+ expect(store.isEmpty).toBe(true);
22
23
 
23
24
  store.formState.$.message.onChange('test');
24
25
  expect(store.formState.$.message.value).toBe('test');
25
26
  const validateResult2 = await store.formState.validate();
26
27
  expect(validateResult2.hasError).toBe(false);
28
+ expect(store.isEmpty).toBe(false);
27
29
 
28
30
  store.formState.$.message.onChange('a'.repeat(6000));
29
31
  expect(store.formState.$.message.value).toBe('a'.repeat(6000));
30
32
  const validateResult3 = await store.formState.validate();
31
33
  expect(validateResult3.hasError).toBe(false);
34
+ expect(store.isEmpty).toBe(false);
32
35
 
33
36
  store.formState.$.message.onChange('a'.repeat(6001));
34
37
  expect(store.formState.$.message.value).toBe('a'.repeat(6001));
35
38
  const validateResult4 = await store.formState.validate();
36
39
  expect(validateResult4.hasError).toBe(true);
37
40
  expect(store.formState.$.message.error).toBe('Message character max is 6000.');
41
+ expect(store.isEmpty).toBe(false);
38
42
  });
39
43
  });