@elliemae/ds-chat 2.0.0-next.0 → 2.0.0-next.12

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 (91) hide show
  1. package/cjs/parts/bubble/ChatBubble.js +15 -12
  2. package/cjs/parts/bubble/ChatBubbleArrow.js +64 -61
  3. package/cjs/parts/bubble/config/useValidateProps.js +5 -6
  4. package/cjs/parts/bubble/props.js +1 -1
  5. package/cjs/parts/bubble/styled.js +2 -31
  6. package/cjs/parts/chat-card/props.js +2 -2
  7. package/cjs/parts/chat-composer/ChatContainerComposer.js +16 -8
  8. package/cjs/parts/chat-composer/props.js +5 -11
  9. package/cjs/parts/chat-composer/styled.js +4 -4
  10. package/cjs/parts/chat-container/ChatContainer.js +50 -9
  11. package/cjs/parts/chat-container/props.js +2 -12
  12. package/cjs/parts/chat-container/styled.js +1 -1
  13. package/cjs/parts/chat-container-header/ChatContainerHeader.js +3 -3
  14. package/cjs/parts/chat-container-header/helpers.js +31 -0
  15. package/cjs/parts/chat-container-header/styled.js +6 -6
  16. package/cjs/parts/chat-container-messages-list/ChatContainerMessagesList.js +139 -34
  17. package/cjs/parts/chat-container-messages-list/props.js +5 -13
  18. package/cjs/parts/chat-container-messages-list/styled.js +2 -6
  19. package/cjs/parts/chat-empty-state/ChatEmptyState.js +2 -3
  20. package/cjs/parts/chat-sidebar/SideBarChat.js +57 -29
  21. package/cjs/parts/chat-sidebar/props.js +11 -1
  22. package/cjs/parts/chat-sidebar/styled.js +2 -2
  23. package/cjs/parts/floating-button/FloatingButton.js +12 -10
  24. package/cjs/parts/grid-influencer/styled.js +2 -6
  25. package/cjs/parts/message-delimiter/styled.js +1 -1
  26. package/cjs/parts/sidebar-button/SidebarButton.js +6 -1
  27. package/cjs/parts/sidebar-button/props.js +2 -3
  28. package/cjs/parts/system-message/style.js +1 -1
  29. package/cjs/parts/tile-button/TileButton.js +31 -28
  30. package/cjs/parts/tile-button/props.js +12 -8
  31. package/cjs/parts/tile-button/styled.js +7 -3
  32. package/cjs/styled.js +1 -1
  33. package/esm/parts/bubble/ChatBubble.js +14 -11
  34. package/esm/parts/bubble/ChatBubbleArrow.js +64 -61
  35. package/esm/parts/bubble/config/useValidateProps.js +5 -6
  36. package/esm/parts/bubble/props.js +1 -1
  37. package/esm/parts/bubble/styled.js +2 -31
  38. package/esm/parts/chat-card/props.js +2 -2
  39. package/esm/parts/chat-composer/ChatContainerComposer.js +15 -6
  40. package/esm/parts/chat-composer/props.js +5 -11
  41. package/esm/parts/chat-composer/styled.js +2 -2
  42. package/esm/parts/chat-container/ChatContainer.js +50 -9
  43. package/esm/parts/chat-container/props.js +2 -12
  44. package/esm/parts/chat-container/styled.js +1 -1
  45. package/esm/parts/chat-container-header/ChatContainerHeader.js +3 -2
  46. package/esm/parts/chat-container-header/helpers.js +23 -0
  47. package/esm/parts/chat-container-header/styled.js +6 -6
  48. package/esm/parts/chat-container-messages-list/ChatContainerMessagesList.js +139 -34
  49. package/esm/parts/chat-container-messages-list/props.js +5 -13
  50. package/esm/parts/chat-container-messages-list/styled.js +2 -6
  51. package/esm/parts/chat-empty-state/ChatEmptyState.js +1 -1
  52. package/esm/parts/chat-sidebar/SideBarChat.js +57 -28
  53. package/esm/parts/chat-sidebar/props.js +11 -1
  54. package/esm/parts/chat-sidebar/styled.js +2 -2
  55. package/esm/parts/floating-button/FloatingButton.js +12 -9
  56. package/esm/parts/grid-influencer/styled.js +2 -6
  57. package/esm/parts/message-delimiter/styled.js +1 -1
  58. package/esm/parts/sidebar-button/SidebarButton.js +6 -1
  59. package/esm/parts/sidebar-button/props.js +1 -1
  60. package/esm/parts/system-message/style.js +1 -1
  61. package/esm/parts/tile-button/TileButton.js +32 -27
  62. package/esm/parts/tile-button/props.js +11 -3
  63. package/esm/parts/tile-button/styled.js +7 -4
  64. package/esm/styled.js +1 -1
  65. package/package.json +16 -6
  66. package/types/parts/bubble/styled.d.ts +3 -3
  67. package/types/parts/bubble/tests/ChatBubble.test.d.ts +1 -0
  68. package/types/parts/chat-card/styled.d.ts +1 -1
  69. package/types/parts/chat-composer/ChatContainerComposer.d.ts +2 -0
  70. package/types/parts/chat-composer/props.d.ts +10 -6
  71. package/types/parts/chat-composer/styled.d.ts +2 -2
  72. package/types/parts/chat-composer/tests/ChatContainerComposer.test.d.ts +1 -0
  73. package/types/parts/chat-container/props.d.ts +2 -0
  74. package/types/parts/chat-container/styled.d.ts +2 -2
  75. package/types/parts/chat-container-header/helpers.d.ts +6 -0
  76. package/types/parts/chat-container-header/styled.d.ts +3 -3
  77. package/types/parts/chat-container-messages-list/props.d.ts +6 -0
  78. package/types/parts/chat-empty-state/styled.d.ts +1 -1
  79. package/types/parts/chat-sidebar/SideBarChat.d.ts +3 -0
  80. package/types/parts/chat-sidebar/props.d.ts +8 -1
  81. package/types/parts/chat-sidebar/styled.d.ts +2 -2
  82. package/types/parts/floating-container/styled.d.ts +1 -1
  83. package/types/parts/grid-influencer/styled.d.ts +2 -2
  84. package/types/parts/message-delimiter/styled.d.ts +1 -1
  85. package/types/parts/sidebar-button/styled.d.ts +1 -1
  86. package/types/parts/system-message/style.d.ts +2 -2
  87. package/types/parts/tile-button/TileButton.d.ts +3 -0
  88. package/types/parts/tile-button/props.d.ts +6 -0
  89. package/types/parts/tile-button/styled.d.ts +5 -4
  90. package/cjs/package.json +0 -4
  91. package/esm/package.json +0 -4
@@ -1,10 +1,16 @@
1
1
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
2
  import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
3
3
  import 'core-js/modules/web.url.to-json.js';
4
+ import 'core-js/modules/web.dom-collections.iterator.js';
5
+ import 'core-js/modules/esnext.async-iterator.filter.js';
6
+ import 'core-js/modules/esnext.iterator.constructor.js';
7
+ import 'core-js/modules/esnext.iterator.filter.js';
8
+ import 'core-js/modules/esnext.async-iterator.for-each.js';
9
+ import 'core-js/modules/esnext.iterator.for-each.js';
4
10
  import React from 'react';
5
11
  import { Grid } from '@elliemae/ds-grid';
6
12
  import { describe } from 'react-desc';
7
- import PaperAirplane from '@elliemae/ds-icons/PaperAirplane';
13
+ import { PaperAirplane } from '@elliemae/ds-icons';
8
14
  import { StyledChatContainerContentComposer, StyledComposerInput, StyledComposerButton, StyledCounterArea, StyledCounterErrorA11y } from './styled.js';
9
15
  import { useValidateProps } from './config/useValidateProps.js';
10
16
  import { composerProps, defaultComposerPros } from './props.js';
@@ -27,9 +33,11 @@ const ChatComposer = props => {
27
33
  onSend,
28
34
  onFocus,
29
35
  onBlur,
36
+ onKeyDown,
30
37
  maxHeight = 300,
31
38
  placeholder,
32
39
  inputMaxLength,
40
+ maxLengthMessage,
33
41
  buttonDisabled,
34
42
  dataTestid,
35
43
  actionRef
@@ -47,7 +55,7 @@ const ChatComposer = props => {
47
55
  onResize(newHeight);
48
56
  setHeight(newHeight);
49
57
  setShowScroll(((_inputSize$current3 = inputSize.current) === null || _inputSize$current3 === void 0 ? void 0 : _inputSize$current3.scrollHeight) - ((_inputSize$current4 = inputSize.current) === null || _inputSize$current4 === void 0 ? void 0 : _inputSize$current4.clientHeight) > 5);
50
- }, [inputValue, internalMaxHeight]);
58
+ }, [inputValue, internalMaxHeight, onResize]);
51
59
  React.useEffect(() => {
52
60
  if (actionRef && actionRef.current) {
53
61
  actionRef.current.focusToComposer = () => {
@@ -58,7 +66,7 @@ const ChatComposer = props => {
58
66
  inputSend.current.focus();
59
67
  };
60
68
  }
61
- }, [actionRef.current]);
69
+ }, [actionRef]);
62
70
  const handleChange = React.useCallback(e => {
63
71
  onChange(e.target.value);
64
72
  }, [onChange]);
@@ -70,7 +78,7 @@ const ChatComposer = props => {
70
78
  const hasError = React.useMemo(() => inputLength > inputMaxLength, [inputLength, inputMaxLength]);
71
79
  return /*#__PURE__*/_jsx(StyledChatContainerContentComposer, {
72
80
  rows: ['auto', '15px'],
73
- maxHeight: `${maxHeight}px`,
81
+ maxHeight: "".concat(maxHeight, "px"),
74
82
  "data-testid": dataTestid
75
83
  }, void 0, /*#__PURE__*/_jsx(Grid, {
76
84
  cols: ['1fr', '24px'],
@@ -84,6 +92,7 @@ const ChatComposer = props => {
84
92
  value: inputValue,
85
93
  onFocus: onFocus,
86
94
  onBlur: onBlur,
95
+ onKeyDown: onKeyDown,
87
96
  placeholder: placeholder,
88
97
  "aria-label": "Message to send",
89
98
  "data-testid": "chat-composer-textarea"
@@ -127,8 +136,8 @@ const ChatComposer = props => {
127
136
  }, void 0, inputMaxLength && hasError ? /*#__PURE__*/jsxs(Fragment, {
128
137
  children: [/*#__PURE__*/_jsx(StyledCounterErrorA11y, {
129
138
  role: "alert"
130
- }, void 0, "You have exceeded the character limit by ", inputLength - inputMaxLength), "Please limit to less than ", inputMaxLength, " characters.", ' ']
131
- }) : '', inputMaxLength ? `${inputLength} / ${inputMaxLength}` : ''), _Grid || (_Grid = /*#__PURE__*/_jsx(Grid, {}))));
139
+ }, void 0, maxLengthMessage || "You have exceeded the character limit by ".concat(inputLength - inputMaxLength)), maxLengthMessage || "Please limit to less than ".concat(inputMaxLength, " characters. ")]
140
+ }) : '', inputMaxLength ? " ".concat(inputLength, " / ").concat(inputMaxLength) : ''), _Grid || (_Grid = /*#__PURE__*/_jsx(Grid, {}))));
132
141
  };
133
142
 
134
143
  ChatComposer.defaultProps = defaultComposerPros;
@@ -9,8 +9,10 @@ const defaultComposerPros = {
9
9
  onSend: noop,
10
10
  onFocus: noop,
11
11
  onBlur: noop,
12
+ onKeyDown: noop,
12
13
  maxHeight: 0,
13
14
  inputMaxLength: undefined,
15
+ maxLengthMessage: undefined,
14
16
  buttonDisabled: false,
15
17
  dataTestid: 'ds-chat-composer',
16
18
  actionRef: noop
@@ -18,26 +20,18 @@ const defaultComposerPros = {
18
20
  const composerProps = {
19
21
  placeholder: PropTypes.string.description('Input Placeholder').defaultValue(defaultComposerPros.placeholder),
20
22
  inputValue: PropTypes.string.description('Input value').defaultValue(defaultComposerPros.inputValue),
23
+ maxLengthMessage: PropTypes.string.description('Max length message').defaultValue(defaultComposerPros.maxLengthMessage),
21
24
  onChange: PropTypes.func.description('Called when the user types').defaultValue(defaultComposerPros.onChange),
22
25
  onResize: PropTypes.func.description('Called the input changes his height').defaultValue(defaultComposerPros.onResize),
23
26
  onSend: PropTypes.func.description('Called when the click send').defaultValue(defaultComposerPros.onSend),
24
27
  onFocus: PropTypes.func.description('Input focus').defaultValue(defaultComposerPros.onFocus),
25
28
  onBlur: PropTypes.func.description('Input blur').defaultValue(defaultComposerPros.onBlur),
29
+ onKeyDown: PropTypes.func.description('Input on key down').defaultValue(defaultComposerPros.onKeyDown),
26
30
  maxHeight: PropTypes.number.description('Max height of the composer. Use 0 for no limit').defaultValue(defaultComposerPros.onBlur),
27
31
  inputMaxLength: PropTypes.number.description('Max length of the composer. `undefined` for no limit').defaultValue(defaultComposerPros.inputMaxLength),
28
32
  buttonDisabled: PropTypes.bool.description('Control the disabled for the send button').defaultValue(defaultComposerPros.buttonDisabled),
29
33
  dataTestid: PropTypes.string.description('Data testid').defaultValue(defaultComposerPros.placeholder),
30
- actionRef: PropTypes.object.description(`Reference:
31
- {
32
- scrollToIndex: (n: number) => void,
33
- getMessagesInView: () => number[],
34
- focusToIndex: (n: number) => void,
35
- focusToList: () => void,
36
- focusToComposer: () => void,
37
- focusToSend: () => void,
38
- }
39
-
40
- `).defaultValue(defaultComposerPros.actionRef)
34
+ actionRef: PropTypes.object.description("Reference: \n { \n scrollToIndex: (n: number) => void,\n getMessagesInView: () => number[],\n focusToIndex: (n: number) => void,\n focusToList: () => void,\n focusToComposer: () => void,\n focusToSend: () => void,\n }\n \n ").defaultValue(defaultComposerPros.actionRef)
41
35
  };
42
36
 
43
37
  export { composerProps, defaultComposerPros };
@@ -8,13 +8,13 @@ const StyledChatContainerContentComposer = /*#__PURE__*/styled(Grid).withConfig(
8
8
  })(["width:100%;min-height:56px;background:", ";padding:", ";padding-bottom:3px;padding-top:6px;padding-left:", ";max-height:", ";border-top:1px solid #cbcfd7;"], props => props.theme.colors.neutral['050'], props => props.theme.space.xxs, props => props.theme.space.xs, props => props.maxHeight);
9
9
  const StyledComposerInput = /*#__PURE__*/styled.textarea.withConfig({
10
10
  componentId: "sc-1ahwpce-1"
11
- })(["padding:", ";resize:none;min-height:32px;border-radius:2px;border:solid 1px #cbcfd7;color:#25292f;height:", ";min-width:244px;overflow-y:auto;font-size:13px;font-weight:400;line-height:1;transition:height 0.3s;&:focus{outline:none;border:1px solid ", ";box-shadow:inset 0 0 0 1px #8cc2ed;border-radius:2px;}"], props => props.theme.space.xxs, props => typeof props.height === 'number' ? `${props.height}px` : props.height, props => props.theme.colors.brand[600]);
11
+ })(["padding:", ";resize:none;min-height:32px;border-radius:2px;border:solid 1px #cbcfd7;color:#25292f;height:", ";min-width:244px;overflow-y:auto;font-size:13px;font-weight:400;line-height:1;transition:height 0.3s;&:focus{outline:none;border:1px solid ", ";box-shadow:inset 0 0 0 1px #8cc2ed;border-radius:2px;}"], props => props.theme.space.xxs, props => typeof props.height === 'number' ? "".concat(props.height, "px") : props.height, props => props.theme.colors.brand[600]);
12
12
  const StyledComposerButton = /*#__PURE__*/styled(DSButton).withConfig({
13
13
  componentId: "sc-1ahwpce-2"
14
14
  })(["&:disabled{background:none;svg{fill:#5d6775;}}svg{fill:", ";}&:focus{background:", ";}"], props => props.theme.colors.brand[600], props => props.theme.colors.brand[200]);
15
15
  const StyledCounterArea = /*#__PURE__*/styled(Grid).withConfig({
16
16
  componentId: "sc-1ahwpce-3"
17
- })(["text-align:right;font-size:11px;height:15px;align-items:center;font-style:italic;", " position:relative;"], props => props.hasError ? `color:${props.theme.colors.danger[900]};` : '');
17
+ })(["text-align:right;font-size:11px;height:15px;align-items:center;font-style:italic;", " position:relative;"], props => props.hasError ? "color:".concat(props.theme.colors.danger[900], ";") : '');
18
18
  const StyledCounterErrorA11y = /*#__PURE__*/styled.span.withConfig({
19
19
  componentId: "sc-1ahwpce-4"
20
20
  })(["width:1px;height:1px;position:absolute;clip:rect(1px,1px,1px,1px);"]);
@@ -1,9 +1,14 @@
1
+ import 'core-js/modules/esnext.async-iterator.filter.js';
2
+ import 'core-js/modules/esnext.iterator.constructor.js';
3
+ import 'core-js/modules/esnext.iterator.filter.js';
4
+ import 'core-js/modules/esnext.async-iterator.for-each.js';
5
+ import 'core-js/modules/esnext.iterator.for-each.js';
1
6
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
7
  import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
3
8
  import React from 'react';
4
9
  import { noop } from 'lodash';
5
10
  import { describe } from 'react-desc';
6
- import DSIndeterminateProgressIndicator from '@elliemae/ds-basic/IndeterminateProgressIndicator';
11
+ import DSIndeterminateProgressIndicator from '@elliemae/ds-indeterminate-progress-indicator';
7
12
  import DSBanner, { BANNER_TYPES } from '@elliemae/ds-banner';
8
13
  import { ChatComposer } from '../chat-composer/ChatContainerComposer.js';
9
14
  import { ChatContainerMessagesList } from '../chat-container-messages-list/ChatContainerMessagesList.js';
@@ -30,7 +35,8 @@ const ChatContainer = props => {
30
35
  messagesListProps,
31
36
  composerProps,
32
37
  composerProps: {
33
- onResize: onComposerResize
38
+ onResize: onComposerResize,
39
+ onKeyDown: onComposerKeyDown
34
40
  },
35
41
  actionRef,
36
42
  autoScroll,
@@ -39,6 +45,10 @@ const ChatContainer = props => {
39
45
  hasMoreItems,
40
46
  getMoreMessages
41
47
  } = props;
48
+ const bannerRef = React.useRef({
49
+ focusOnWrapper: noop,
50
+ focusOnLink: noop
51
+ });
42
52
  const containerRef = React.useRef();
43
53
  const listRef = React.useRef({
44
54
  scrollToIndex: noop
@@ -53,6 +63,29 @@ const ChatContainer = props => {
53
63
  }
54
64
  }, [onComposerResize, messagesListProps.messages, autoScroll]);
55
65
  const Container = device === 'desktop' ? StyledChatContainerContent : MobileStyledChatContainerContent;
66
+ const handleTabs = React.useCallback(e => {
67
+ if (onComposerKeyDown) onComposerKeyDown(e);else {
68
+ const ref = actionRef || listRef;
69
+
70
+ if (e.shiftKey && e.key === 'Tab') {
71
+ e.preventDefault();
72
+
73
+ if (['top', 'bottom'].includes(bannerPosition)) {
74
+ bannerRef.current.focusOnWrapper();
75
+ } else {
76
+ const last = ref.current.getMessagesInView().pop();
77
+ ref.current.focusToIndexWithoutScroll(last);
78
+ }
79
+ }
80
+ }
81
+ }, [actionRef, onComposerKeyDown, bannerPosition]); // Map actions
82
+
83
+ React.useEffect(() => {
84
+ if (actionRef && actionRef.current) {
85
+ actionRef.current.focusOnBanner = bannerRef.current.focusOnWrapper;
86
+ actionRef.current.focusOnBannerLink = bannerRef.current.focusOnLink;
87
+ }
88
+ }, [actionRef]);
56
89
  return /*#__PURE__*/jsxs(Container, {
57
90
  rows: ['minmax(0, 1fr)', 'auto'],
58
91
  ref: containerRef,
@@ -60,7 +93,8 @@ const ChatContainer = props => {
60
93
  "aria-label": "chat window",
61
94
  children: [/*#__PURE__*/_jsx("div", {
62
95
  style: {
63
- position: 'relative'
96
+ position: 'relative',
97
+ overflow: 'hidden'
64
98
  }
65
99
  }, void 0, /*#__PURE__*/jsx(ChatContainerMessagesList, _objectSpread(_objectSpread({}, messagesListProps), {}, {
66
100
  actionRef: actionRef || listRef,
@@ -68,13 +102,17 @@ const ChatContainer = props => {
68
102
  hasMoreItems: hasMoreItems,
69
103
  getMoreMessages: getMoreMessages,
70
104
  sendingMessages: sendingMessages,
71
- sendingText: sendingText
105
+ sendingText: sendingText,
106
+ bannerPosition: bannerPosition,
107
+ bannerRef: bannerRef
72
108
  })), ['top', 'bottom'].includes(isLoading) ? /*#__PURE__*/_jsx(StyledIndicatorContainer, {
73
- isLoading: isLoading
109
+ isLoading: isLoading,
110
+ "data-testid": "ds-chat-loading-indicator"
74
111
  }, void 0, _DSIndeterminateProgr || (_DSIndeterminateProgr = /*#__PURE__*/_jsx(DSIndeterminateProgressIndicator, {
75
112
  processing: true,
76
113
  title: "Loading"
77
- }))) : null, ['top', 'bottom'].includes(bannerPosition) ? /*#__PURE__*/_jsx(StyledIndicatorContainer, {
114
+ }))) : null, /*#__PURE__*/_jsx(StyledIndicatorContainer, {
115
+ linear: true,
78
116
  isLoading: bannerPosition
79
117
  }, void 0, /*#__PURE__*/_jsx(DSBanner, {
80
118
  showCloseButton: true,
@@ -88,15 +126,18 @@ const ChatContainer = props => {
88
126
  },
89
127
  onClose: bannerProps.onClose,
90
128
  isOpen: true,
91
- type: BANNER_TYPES.INFO
92
- })) : null, sendingMessages && sendingMessages.length > 0 ? _StyledIndicatorConta || (_StyledIndicatorConta = /*#__PURE__*/_jsx(StyledIndicatorContainer, {
129
+ type: bannerProps.type || BANNER_TYPES.INFO,
130
+ actionRef: bannerRef
131
+ })), sendingMessages && sendingMessages.length > 0 ? _StyledIndicatorConta || (_StyledIndicatorConta = /*#__PURE__*/_jsx(StyledIndicatorContainer, {
93
132
  isLoading: "bottom",
94
- linear: true
133
+ linear: true,
134
+ "data-testid": "ds-chat-loading-indicator"
95
135
  }, void 0, /*#__PURE__*/_jsx(DSIndeterminateProgressIndicator, {
96
136
  processing: true,
97
137
  title: "Loading",
98
138
  lineOnly: true
99
139
  }))) : null), /*#__PURE__*/jsx(ChatComposer, _objectSpread(_objectSpread({}, composerProps), {}, {
140
+ onKeyDown: handleTabs,
100
141
  onResize: onResize,
101
142
  maxHeight: (((_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.clientHeight) || 0) / 2,
102
143
  actionRef: actionRef || listRef
@@ -31,20 +31,10 @@ const containerProps = {
31
31
  hasMoreItems: PropTypes.oneOf(['top', 'bottom', undefined]).description('Show Load More button at the `top` or `bottom` of the container'),
32
32
  getMoreMessages: PropTypes.func.description('Called when the user clicks on load more').defaultValue(defaultProps.getMoreMessages),
33
33
  bannerPosition: PropTypes.oneOf(['top', 'bottom', undefined]).description('Show Loading messages indicator at the `top` or `bottom` of the container'),
34
- bannerProps: PropTypes.object.description('Banner configuration').defaultValue(defaultProps.bannerProps),
34
+ bannerProps: PropTypes.object.description("Banner configuration:\n label: string;\n viewLabel: string;\n type: BANNER_TYPES;\n onView: () => void;\n onClose: () => void;\n ").defaultValue(defaultProps.bannerProps),
35
35
  sendingMessages: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.number)]).description('Array of ids of sending messages').defaultValue(defaultProps.sendingMessages),
36
36
  sendingText: PropTypes.string.description('Label description for messages in sending status').defaultValue(defaultProps.sendingText),
37
- actionRef: PropTypes.object.description(`Reference:
38
- {
39
- scrollToIndex: (n: number) => void,
40
- getMessagesInView: () => number[],
41
- focusToIndex: (n: number) => void,
42
- focusToList: () => void,
43
- focusToComposer: () => void,
44
- focusToSend: () => void,
45
- }
46
-
47
- `).defaultValue(defaultProps.actionRef)
37
+ actionRef: PropTypes.object.description("Reference: \n { \n scrollToIndex: (n: number) => void,\n getMessagesInView: () => number[],\n focusToIndex: (n: number) => void,\n focusToList: () => void,\n focusToComposer: () => void,\n focusToSend: () => void,\n }\n \n ").defaultValue(defaultProps.actionRef)
48
38
  };
49
39
 
50
40
  export { containerProps, defaultProps };
@@ -10,7 +10,7 @@ const StyledChatContainerContent = /*#__PURE__*/styled(Grid).withConfig({
10
10
  })(["height:100%;min-width:", "px;overflow:hidden;background:", ";position:relative;"], CONTAINER_WIDTH, props => props.theme.colors.neutral['050']);
11
11
  const StyledIndicatorContainer = /*#__PURE__*/styled.div.withConfig({
12
12
  componentId: "sc-d732wo-1"
13
- })(["position:absolute;width:calc(100% + ", ");padding:", ";left:-", ";right:-", ";background:", ";", " ", ""], props => props.theme.space.xxs, props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.space.xxs, props => props.theme.space.xxs, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? 'top: 0;' : '', props => props.isLoading === 'bottom' ? 'bottom: 0;' : '');
13
+ })(["transition:0.4s ease-in-out;position:absolute;visibility:hidden;pointer-events:none;width:100%;padding:0 ", ";left:0;right:0;background:", ";", " ", ""], props => !props.linear ? props.theme.space.xxs : 0, props => props.theme.colors.neutral['000'], props => props.isLoading === 'top' ? "\n top: -41px;\n transform: translate(0, 41px);\n visibility: visible;\n pointer-events: auto\n " : '', props => props.isLoading === 'bottom' ? "\n bottom: -41px;\n transform: translate(0, -41px);\n visibility: visible;\n pointer-events: auto;\n " : '');
14
14
  const MobileStyledChatContainerContent = /*#__PURE__*/styled(StyledChatContainerContent).withConfig({
15
15
  componentId: "sc-d732wo-2"
16
16
  })(["", ",", "{font-size:16px;}", "{font-size:13px;}", "{background-color:#fff;box-shadow:-0 1px 3px rgba(53,60,70,0.8);}", ",", "{font-size:16px;text-align:left;}", "{font-size:13px;}", "{font-size:16px;}"], StyledTitleLeftBubble, StyleBodyWrapper, StyledTitleRightBubble, Wrapper, Title, Message, Time, StyledComposerInput);
@@ -1,7 +1,8 @@
1
1
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
- import React from 'react';
2
+ import 'react';
3
3
  import { SimpleTruncatedTooltipText } from '@elliemae/ds-truncated-tooltip-text';
4
4
  import { StyledChatContainerContentHeader, StyledChatContainerContentHeaderLabel, StyledChatContainerContentHeaderActions } from './styled.js';
5
+ import { validHeaderChilds } from './helpers.js';
5
6
 
6
7
  const ChatContainerHeader = props => {
7
8
  const {
@@ -21,7 +22,7 @@ const ChatContainerHeader = props => {
21
22
  }, void 0, /*#__PURE__*/_jsx(SimpleTruncatedTooltipText, {
22
23
  value: title
23
24
  })), /*#__PURE__*/_jsx(StyledChatContainerContentHeaderActions, {
24
- cols: React.Children.toArray(children).filter(e => e).map(() => '32px'),
25
+ cols: validHeaderChilds(children),
25
26
  justifyContent: "flex-end",
26
27
  "data-testid": "chat-container-header-children"
27
28
  }, void 0, children));
@@ -0,0 +1,23 @@
1
+ import 'core-js/modules/esnext.async-iterator.map.js';
2
+ import 'core-js/modules/esnext.iterator.map.js';
3
+ import 'core-js/modules/esnext.async-iterator.filter.js';
4
+ import 'core-js/modules/esnext.iterator.constructor.js';
5
+ import 'core-js/modules/esnext.iterator.filter.js';
6
+ import 'core-js/modules/esnext.async-iterator.constructor.js';
7
+ import 'core-js/modules/esnext.async-iterator.to-array.js';
8
+ import 'core-js/modules/esnext.iterator.to-array.js';
9
+ import React from 'react';
10
+
11
+ const validHeaderChilds = children => React.Children.toArray(children).filter(e => {
12
+ if (Array.isArray(e.props.children)) {
13
+ return false;
14
+ }
15
+
16
+ if (e.props.children === undefined && e.$$typeof.toString() === 'Symbol(react.element)') {
17
+ return true;
18
+ }
19
+
20
+ return !!e.props.children;
21
+ }).map(() => '32px');
22
+
23
+ export { validHeaderChilds };
@@ -1,14 +1,14 @@
1
1
  import styled from 'styled-components';
2
2
  import { Grid } from '@elliemae/ds-grid';
3
3
 
4
- const StyledChatContainerContentHeader = /*#__PURE__*/styled(Grid).withConfig({
4
+ const StyledChatContainerContentHeaderActions = /*#__PURE__*/styled(Grid).withConfig({
5
5
  componentId: "sc-jmynvr-0"
6
- })(["width:100%;min-height:48px;border-bottom:1px solid ", ";background:", ";"], props => props.theme.colors.neutral['200'], props => props.theme.colors.neutral['050']);
7
- const StyledChatContainerContentHeaderLabel = /*#__PURE__*/styled(Grid).withConfig({
6
+ })(["max-width:80px;"]);
7
+ const StyledChatContainerContentHeader = /*#__PURE__*/styled(Grid).withConfig({
8
8
  componentId: "sc-jmynvr-1"
9
- })(["justify-content:center;align-items:center;font-weight:bold;font-size:18px;color:", ";"], props => props.theme.colors.neutral['700']);
10
- const StyledChatContainerContentHeaderActions = /*#__PURE__*/styled(Grid).withConfig({
9
+ })(["width:100%;min-height:48px;border-bottom:1px solid ", ";background:", ";", "{& button{background:transparent;}}"], props => props.theme.colors.neutral['200'], props => props.theme.colors.neutral['050'], StyledChatContainerContentHeaderActions);
10
+ const StyledChatContainerContentHeaderLabel = /*#__PURE__*/styled(Grid).withConfig({
11
11
  componentId: "sc-jmynvr-2"
12
- })(["max-width:80px;"]);
12
+ })(["justify-content:center;align-items:center;font-weight:bold;font-size:18px;color:", ";"], props => props.theme.colors.neutral['700']);
13
13
 
14
14
  export { StyledChatContainerContentHeader, StyledChatContainerContentHeaderActions, StyledChatContainerContentHeaderLabel };
@@ -1,9 +1,18 @@
1
+ import 'core-js/modules/esnext.async-iterator.filter.js';
2
+ import 'core-js/modules/esnext.iterator.filter.js';
1
3
  import _jsx from '@babel/runtime/helpers/esm/jsx';
2
4
  import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
5
+ import 'core-js/modules/esnext.async-iterator.for-each.js';
6
+ import 'core-js/modules/esnext.iterator.constructor.js';
7
+ import 'core-js/modules/esnext.iterator.for-each.js';
8
+ import 'core-js/modules/esnext.async-iterator.map.js';
9
+ import 'core-js/modules/esnext.iterator.map.js';
10
+ import 'core-js/modules/esnext.async-iterator.find.js';
11
+ import 'core-js/modules/esnext.iterator.find.js';
3
12
  import React from 'react';
4
13
  import { describe } from 'react-desc';
5
14
  import { useVirtual } from 'react-virtual';
6
- import DSButton from '@elliemae/ds-basic/Button';
15
+ import DSButton from '@elliemae/ds-button';
7
16
  import { ChatBubble } from '../bubble/ChatBubble.js';
8
17
  import { BUBBLE_TYPES } from '../bubble/props.js';
9
18
  import { MessageDelimiter } from '../message-delimiter/MessageDelimiter.js';
@@ -30,14 +39,17 @@ const ChatContainerMessagesList = props => {
30
39
  useValidateProps(props, ChatContainerMessagesListWithSchema.toTypescript());
31
40
  const {
32
41
  sendingText,
42
+ bannerPosition,
33
43
  sendingMessages,
34
44
  messages,
35
45
  actionRef,
36
46
  onScrollEnds,
37
47
  getMoreMessages,
38
48
  hasMoreItems,
39
- isLoading
49
+ isLoading,
50
+ bannerRef
40
51
  } = props;
52
+ const loadMoreRef = React.useRef(null);
41
53
  const virtualListRef = React.useRef(); // estimateSize should not be really required given what was stated on
42
54
  // https://github.com/tannerlinsley/react-virtual/issues/23
43
55
 
@@ -53,86 +65,177 @@ const ChatContainerMessagesList = props => {
53
65
  estimateSize: React.useCallback(() => 80, [])
54
66
  }));
55
67
  const onScrollStop = React.useCallback(() => {
68
+ var _virtualListRef$curre;
69
+
56
70
  const {
57
71
  bottom,
58
72
  top
59
- } = virtualListRef.current.getBoundingClientRect();
73
+ } = (_virtualListRef$curre = virtualListRef.current) === null || _virtualListRef$curre === void 0 ? void 0 : _virtualListRef$curre.getBoundingClientRect();
60
74
  const list = virtualListRef.current.querySelectorAll('[data-element="chat-element-list-item"]');
61
75
  const indexList = [];
62
76
  list.forEach(item => {
63
77
  const {
64
- top: itemTop
78
+ top: itemTop,
79
+ bottom: itemBottom
65
80
  } = item.getBoundingClientRect();
66
- if (itemTop < bottom && itemTop > top) indexList.push(item.dataset.index);
81
+ const itemPadding = 10;
82
+ if (itemTop + itemPadding < bottom && itemBottom - itemPadding > top) indexList.push(item.dataset.index);
67
83
  });
68
84
  if (onScrollEnds) onScrollEnds(indexList);
69
85
  return indexList;
70
86
  }, [onScrollEnds]);
71
- const scrollAndFocus = React.useCallback(index => {
72
- vScroll(index, {
73
- align: 'center'
74
- });
87
+ const onlyFocus = React.useCallback(index => {
75
88
  setTimeout(() => {
76
- virtualListRef.current.querySelector(`[data-index="${index}"]`).focus();
89
+ virtualListRef.current.querySelector("[data-index=\"".concat(index, "\"]")).focus();
77
90
  }, 300);
78
- }, [vScroll]);
91
+ }, []);
92
+ const scrollAndFocus = React.useCallback(function (index) {
93
+ let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
94
+ align: 'center'
95
+ };
96
+ vScroll(index, b);
97
+ onlyFocus(index);
98
+ }, [onlyFocus, vScroll]);
79
99
  React.useEffect(() => {
80
100
  if (actionRef && actionRef.current) {
81
- actionRef.current.scrollToIndex = (a, b = {
82
- align: 'center'
83
- }) => {
101
+ actionRef.current.scrollToIndex = function (a) {
102
+ let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
103
+ align: 'center'
104
+ };
84
105
  vScroll(a, b);
85
106
  };
86
107
 
87
108
  actionRef.current.getMessagesInView = onScrollStop;
88
109
 
89
- actionRef.current.focusToIndex = (a, b = {
90
- align: 'center'
91
- }) => {
92
- scrollAndFocus(a);
110
+ actionRef.current.focusToIndex = function (a) {
111
+ let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
112
+ align: 'center'
113
+ };
114
+ scrollAndFocus(a, b);
93
115
  };
94
116
 
95
- actionRef.current.focusToList = (a, b = {
96
- align: 'center'
97
- }) => {
98
- var _virtualListRef$curre;
117
+ actionRef.current.focusToIndexWithoutScroll = onlyFocus;
99
118
 
100
- (_virtualListRef$curre = virtualListRef.current) === null || _virtualListRef$curre === void 0 ? void 0 : _virtualListRef$curre.focus();
119
+ actionRef.current.focusToList = a => {
120
+ var _virtualListRef$curre2;
121
+
122
+ (_virtualListRef$curre2 = virtualListRef.current) === null || _virtualListRef$curre2 === void 0 ? void 0 : _virtualListRef$curre2.focus();
101
123
  };
102
124
  }
103
- }, [actionRef, onScrollStop, scrollAndFocus, vScroll]);
125
+ }, [actionRef, onScrollStop, onlyFocus, scrollAndFocus, vScroll]);
104
126
  const handleKey = React.useCallback(e => {
105
- e.preventDefault();
106
127
  const {
107
128
  key
108
129
  } = e;
109
130
  const index = Number(e.currentTarget.dataset.index);
131
+ e.stopPropagation();
110
132
 
111
133
  switch (key) {
112
134
  case 'ArrowUp':
113
- if (index > 0) virtualListRef.current.querySelector(`[data-index="${index - 1}"]`).focus();
135
+ e.preventDefault();
136
+ if (index > 0) virtualListRef.current.querySelector("[data-index=\"".concat(index - 1, "\"]")).focus();
114
137
  break;
115
138
 
116
139
  case 'ArrowDown':
140
+ e.preventDefault();
141
+
117
142
  if (index < messages.length - 1) {
118
- virtualListRef.current.querySelector(`[data-index="${index + 1}"]`).focus();
143
+ virtualListRef.current.querySelector("[data-index=\"".concat(index + 1, "\"]")).focus();
144
+ }
145
+
146
+ break;
147
+
148
+ case 'Home':
149
+ e.preventDefault();
150
+ scrollAndFocus(0);
151
+ break;
152
+
153
+ case 'End':
154
+ e.preventDefault();
155
+ scrollAndFocus(messages.length - 1);
156
+ break;
157
+
158
+ case 'Tab':
159
+ e.preventDefault();
160
+
161
+ if (hasMoreItems === 'bottom' && !e.shiftKey) {
162
+ var _loadMoreRef$current;
163
+
164
+ (_loadMoreRef$current = loadMoreRef.current) === null || _loadMoreRef$current === void 0 ? void 0 : _loadMoreRef$current.focus();
165
+ } else if (hasMoreItems === 'top' && e.shiftKey) {
166
+ var _loadMoreRef$current2;
167
+
168
+ (_loadMoreRef$current2 = loadMoreRef.current) === null || _loadMoreRef$current2 === void 0 ? void 0 : _loadMoreRef$current2.focus();
169
+ } else if (!e.shiftKey) {
170
+ if (bannerPosition) {
171
+ bannerRef.current.focusOnWrapper();
172
+ } else {
173
+ actionRef.current.focusToComposer();
174
+ }
175
+ } else {
176
+ var _virtualListRef$curre3;
177
+
178
+ (_virtualListRef$curre3 = virtualListRef.current) === null || _virtualListRef$curre3 === void 0 ? void 0 : _virtualListRef$curre3.focus();
119
179
  }
120
180
 
121
181
  break;
182
+ }
183
+ }, [actionRef, hasMoreItems, messages.length, bannerPosition, scrollAndFocus]);
184
+ const handleListKey = React.useCallback(e => {
185
+ const {
186
+ key
187
+ } = e;
188
+
189
+ switch (key) {
190
+ case 'ArrowUp':
191
+ e.preventDefault();
192
+ onlyFocus(onScrollStop().shift());
193
+ break;
194
+
195
+ case 'ArrowDown':
196
+ e.preventDefault();
197
+ onlyFocus(onScrollStop().pop());
198
+ break;
122
199
 
123
200
  case 'Home':
201
+ e.preventDefault();
124
202
  scrollAndFocus(0);
125
203
  break;
126
204
 
127
205
  case 'End':
206
+ e.preventDefault();
128
207
  scrollAndFocus(messages.length - 1);
129
208
  break;
130
209
 
131
210
  case 'Tab':
132
- actionRef.current.focusToComposer();
211
+ if (document.activeElement !== loadMoreRef.current) {
212
+ if (hasMoreItems === 'bottom' && !e.shiftKey) {
213
+ var _loadMoreRef$current3;
214
+
215
+ e.preventDefault();
216
+ (_loadMoreRef$current3 = loadMoreRef.current) === null || _loadMoreRef$current3 === void 0 ? void 0 : _loadMoreRef$current3.focus();
217
+ } else if (hasMoreItems === 'top' && e.shiftKey) {
218
+ var _loadMoreRef$current4;
219
+
220
+ e.preventDefault();
221
+ (_loadMoreRef$current4 = loadMoreRef.current) === null || _loadMoreRef$current4 === void 0 ? void 0 : _loadMoreRef$current4.focus();
222
+ } else if (!e.shiftKey) {
223
+ e.preventDefault();
224
+ actionRef.current.focusToComposer();
225
+ }
226
+ } else if (!e.shiftKey) {
227
+ e.preventDefault();
228
+ actionRef.current.focusToComposer();
229
+ } else {
230
+ var _virtualListRef$curre4;
231
+
232
+ e.preventDefault();
233
+ (_virtualListRef$curre4 = virtualListRef.current) === null || _virtualListRef$curre4 === void 0 ? void 0 : _virtualListRef$curre4.focus();
234
+ }
235
+
133
236
  break;
134
237
  }
135
- }, [actionRef, messages.length, scrollAndFocus]);
238
+ }, [actionRef, hasMoreItems, messages.length, onScrollStop, onlyFocus, scrollAndFocus]);
136
239
  React.useEffect(() => {
137
240
  let handlerCallback = null;
138
241
  const localVirtualRef = virtualListRef.current;
@@ -163,7 +266,7 @@ const ChatContainerMessagesList = props => {
163
266
  position: 'absolute',
164
267
  top: 0,
165
268
  left: 0,
166
- transform: `translateY(${start}px)`,
269
+ transform: "translateY(".concat(start, "px)"),
167
270
  width: '100%'
168
271
  };
169
272
  return /*#__PURE__*/jsx(ListItem, {
@@ -182,7 +285,7 @@ const ChatContainerMessagesList = props => {
182
285
  jsx(ChatBubble, _objectSpread(_objectSpread({}, row), {}, {
183
286
  helpMessage: isSending ? sendingText : row.helpMessage
184
287
  })) : null, row.type === BUBBLE_TYPES.DELIMITER ? /*#__PURE__*/jsx(MessageDelimiter, _objectSpread({}, row)) : null, row.type === BUBBLE_TYPES.SYSTEM ? /*#__PURE__*/jsx(SystemMessage, _objectSpread({}, row)) : null)
185
- }, `ds-chat-bubble-${index}`);
288
+ }, "ds-chat-bubble-".concat(index, "-").concat(row.dsId, "-").concat(row.type));
186
289
  }), [virtualItems, messages, sendingMessages, handleKey, sendingText]);
187
290
  return /*#__PURE__*/_jsx(StyledWrapper, {}, void 0, /*#__PURE__*/jsx(StyledChatContainerContentList, {
188
291
  "data-testid": "chat-thread-container",
@@ -190,15 +293,17 @@ const ChatContainerMessagesList = props => {
190
293
  "aria-live": "polite",
191
294
  ref: virtualListRef,
192
295
  role: "list",
193
- onKeyDown: handleKey,
296
+ onKeyDown: handleListKey,
194
297
  children: /*#__PURE__*/_jsx(StyledChatContainerContentListScroll, {
195
- height: `${totalSize}px`
298
+ height: "".concat(totalSize, "px")
196
299
  }, void 0, render, !isLoading && hasMoreItems ? /*#__PURE__*/_jsx(StyledLoadMoreContainer, {
197
300
  hasMoreItems: hasMoreItems
198
301
  }, void 0, /*#__PURE__*/_jsx(DSButton, {
199
302
  labelText: "Load More",
200
303
  buttonType: "secondary",
201
- onClick: getMoreMessages
304
+ onClick: getMoreMessages,
305
+ "data-testid": "ds-chat-load-more-btn",
306
+ innerRef: loadMoreRef
202
307
  })) : null)
203
308
  }));
204
309
  };