botframework-webchat-component 4.14.1 → 4.15.2-main.20220413.af6e8a3

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 (411) hide show
  1. package/.eslintrc.yml +4 -106
  2. package/.prettierrc.yml +1 -1
  3. package/lib/Activity/CarouselFilmStrip.js +1 -1
  4. package/lib/Activity/CarouselFilmStripAttachment.js +1 -1
  5. package/lib/Activity/CarouselLayout.js +3 -3
  6. package/lib/Activity/Speak.d.ts +2 -2
  7. package/lib/Activity/Speak.d.ts.map +1 -1
  8. package/lib/Activity/Speak.js +15 -8
  9. package/lib/Activity/StackedLayout.d.ts +5 -3
  10. package/lib/Activity/StackedLayout.d.ts.map +1 -1
  11. package/lib/Activity/StackedLayout.js +16 -21
  12. package/lib/Attachment/FileAttachment.js +2 -2
  13. package/lib/Attachment/FileContent.d.ts.map +1 -1
  14. package/lib/Attachment/FileContent.js +11 -1
  15. package/lib/BasicSendBox.d.ts.map +1 -1
  16. package/lib/BasicSendBox.js +8 -9
  17. package/lib/BasicToast.js +1 -1
  18. package/lib/BasicToaster.js +6 -3
  19. package/lib/BasicTranscript.d.ts +7 -0
  20. package/lib/BasicTranscript.d.ts.map +1 -0
  21. package/lib/BasicTranscript.js +386 -728
  22. package/lib/BasicTypingIndicator.d.ts +2 -1
  23. package/lib/BasicTypingIndicator.d.ts.map +1 -1
  24. package/lib/BasicTypingIndicator.js +12 -4
  25. package/lib/Composer.d.ts.map +1 -1
  26. package/lib/Composer.js +37 -32
  27. package/lib/ConnectivityStatus/Connecting.js +1 -1
  28. package/lib/Dictation.js +1 -1
  29. package/lib/Middleware/Activity/createCoreMiddleware.d.ts.map +1 -1
  30. package/lib/Middleware/Activity/createCoreMiddleware.js +9 -12
  31. package/lib/Middleware/ActivityStatus/SendStatus/SendFailedRetry.js +1 -1
  32. package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts +2 -2
  33. package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.d.ts.map +1 -1
  34. package/lib/Middleware/ActivityStatus/SendStatus/SendStatus.js +5 -4
  35. package/lib/Middleware/ActivityStatus/Timestamp.d.ts +2 -2
  36. package/lib/Middleware/ActivityStatus/Timestamp.d.ts.map +1 -1
  37. package/lib/Middleware/ActivityStatus/Timestamp.js +4 -2
  38. package/lib/Middleware/ActivityStatus/createSendStatusMiddleware.js +20 -17
  39. package/lib/Middleware/Attachment/createCoreMiddleware.js +2 -5
  40. package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.d.ts.map +1 -1
  41. package/lib/Middleware/AttachmentForScreenReader/createCoreMiddleware.js +1 -1
  42. package/lib/Middleware/Avatar/createCoreMiddleware.d.ts.map +1 -1
  43. package/lib/Middleware/Avatar/createCoreMiddleware.js +2 -3
  44. package/lib/Middleware/CardAction/createCoreMiddleware.js +10 -3
  45. package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.d.ts.map +1 -1
  46. package/lib/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.js +1 -1
  47. package/lib/Middleware/Toast/createCoreMiddleware.d.ts.map +1 -1
  48. package/lib/Middleware/Toast/createCoreMiddleware.js +1 -1
  49. package/lib/Middleware/TypingIndicator/createCoreMiddleware.d.ts.map +1 -1
  50. package/lib/Middleware/TypingIndicator/createCoreMiddleware.js +8 -6
  51. package/lib/ReactWebChat.js +2 -2
  52. package/lib/ScreenReaderActivity.js +15 -6
  53. package/lib/ScreenReaderText.d.ts +9 -0
  54. package/lib/ScreenReaderText.d.ts.map +1 -0
  55. package/lib/ScreenReaderText.js +22 -8
  56. package/lib/SendBox/AutoResizeTextArea.js +1 -1
  57. package/lib/SendBox/DictationInterims.js +1 -1
  58. package/lib/SendBox/IconButton.d.ts +11 -0
  59. package/lib/SendBox/IconButton.d.ts.map +1 -0
  60. package/lib/SendBox/IconButton.js +26 -6
  61. package/lib/SendBox/MicrophoneButton.js +2 -2
  62. package/lib/SendBox/SendButton.js +2 -2
  63. package/lib/SendBox/SuggestedAction.d.ts +17 -0
  64. package/lib/SendBox/SuggestedAction.d.ts.map +1 -0
  65. package/lib/SendBox/SuggestedAction.js +26 -17
  66. package/lib/SendBox/SuggestedActions.d.ts.map +1 -1
  67. package/lib/SendBox/SuggestedActions.js +26 -9
  68. package/lib/SendBox/TextBox.js +4 -4
  69. package/lib/SendBox/UploadButton.js +4 -4
  70. package/lib/Styles/StyleSet/BasicTranscript.d.ts +13 -5
  71. package/lib/Styles/StyleSet/BasicTranscript.d.ts.map +1 -1
  72. package/lib/Styles/StyleSet/BasicTranscript.js +21 -9
  73. package/lib/Styles/StyleSet/Bubble.d.ts.map +1 -1
  74. package/lib/Styles/StyleSet/Bubble.js +3 -3
  75. package/lib/Styles/StyleSet/CarouselFilmStrip.d.ts.map +1 -1
  76. package/lib/Styles/StyleSet/CarouselFilmStrip.js +3 -3
  77. package/lib/Styles/StyleSet/CarouselFilmStripAttachment.d.ts.map +1 -1
  78. package/lib/Styles/StyleSet/CarouselFilmStripAttachment.js +3 -3
  79. package/lib/Styles/StyleSet/KeyboardHelp.d.ts +123 -0
  80. package/lib/Styles/StyleSet/KeyboardHelp.d.ts.map +1 -0
  81. package/lib/Styles/StyleSet/KeyboardHelp.js +145 -0
  82. package/lib/Styles/StyleSet/ScrollToEndButton.d.ts +1 -1
  83. package/lib/Styles/StyleSet/ScrollToEndButton.js +2 -2
  84. package/lib/Styles/StyleSet/SendBoxButton.d.ts +56 -10
  85. package/lib/Styles/StyleSet/SendBoxButton.d.ts.map +1 -1
  86. package/lib/Styles/StyleSet/SendBoxButton.js +76 -12
  87. package/lib/Styles/StyleSet/SendBoxTextBox.d.ts +1 -1
  88. package/lib/Styles/StyleSet/SendBoxTextBox.d.ts.map +1 -1
  89. package/lib/Styles/StyleSet/SendBoxTextBox.js +3 -3
  90. package/lib/Styles/StyleSet/StackedLayout.d.ts.map +1 -1
  91. package/lib/Styles/StyleSet/StackedLayout.js +1 -1
  92. package/lib/Styles/StyleSet/SuggestedAction.d.ts +73 -28
  93. package/lib/Styles/StyleSet/SuggestedAction.d.ts.map +1 -1
  94. package/lib/Styles/StyleSet/SuggestedAction.js +116 -41
  95. package/lib/Styles/StyleSet/SuggestedActions.d.ts +13 -6
  96. package/lib/Styles/StyleSet/SuggestedActions.d.ts.map +1 -1
  97. package/lib/Styles/StyleSet/SuggestedActions.js +13 -6
  98. package/lib/Styles/createStyleSet.d.ts +324 -55
  99. package/lib/Styles/createStyleSet.d.ts.map +1 -1
  100. package/lib/Styles/createStyleSet.js +6 -3
  101. package/lib/Styles/mirrorStyle.js +12 -3
  102. package/lib/Toast/NotificationIcon.js +7 -3
  103. package/lib/Toast/createToastMiddleware.d.ts.map +1 -1
  104. package/lib/Toast/createToastMiddleware.js +1 -1
  105. package/lib/Transcript/ActivityRow.d.ts +9 -0
  106. package/lib/Transcript/ActivityRow.d.ts.map +1 -0
  107. package/lib/Transcript/ActivityRow.js +159 -0
  108. package/lib/Transcript/ActivityTextAlt.js +57 -0
  109. package/lib/Transcript/FocusTrap.d.ts +8 -0
  110. package/lib/Transcript/FocusTrap.d.ts.map +1 -0
  111. package/lib/Transcript/FocusTrap.js +74 -0
  112. package/lib/Transcript/KeyboardHelp.d.ts +4 -0
  113. package/lib/Transcript/KeyboardHelp.d.ts.map +1 -0
  114. package/lib/Transcript/KeyboardHelp.js +550 -0
  115. package/lib/Transcript/LiveRegionTranscript.d.ts +8 -0
  116. package/lib/Transcript/LiveRegionTranscript.d.ts.map +1 -0
  117. package/lib/Transcript/LiveRegionTranscript.js +229 -0
  118. package/lib/Transcript/types.d.ts +2 -0
  119. package/lib/Transcript/types.d.ts.map +1 -0
  120. package/lib/Transcript/types.js +2 -0
  121. package/lib/Transcript/useActivityAccessibleName.d.ts +4 -0
  122. package/lib/Transcript/useActivityAccessibleName.d.ts.map +1 -0
  123. package/lib/Transcript/useActivityAccessibleName.js +97 -0
  124. package/lib/Transcript/useTypistNames.d.ts +3 -0
  125. package/lib/Transcript/useTypistNames.d.ts.map +1 -0
  126. package/lib/Transcript/useTypistNames.js +61 -0
  127. package/lib/Utils/AccessKeySink/Surface.js +1 -1
  128. package/lib/Utils/AccessibleButton.d.ts +11 -0
  129. package/lib/Utils/AccessibleButton.d.ts.map +1 -0
  130. package/lib/Utils/AccessibleButton.js +12 -7
  131. package/lib/Utils/AccessibleInputText.js +1 -1
  132. package/lib/Utils/AccessibleTextArea.js +1 -1
  133. package/lib/Utils/CroppedImage.js +1 -1
  134. package/lib/Utils/FocusRedirector.d.ts +9 -0
  135. package/lib/Utils/FocusRedirector.d.ts.map +1 -0
  136. package/lib/Utils/FocusRedirector.js +17 -14
  137. package/lib/Utils/InlineMarkdown.js +15 -8
  138. package/lib/Utils/TypeFocusSink/FocusBox.js +1 -1
  139. package/lib/Utils/TypeFocusSink/inputtableKey.d.ts +2 -0
  140. package/lib/Utils/TypeFocusSink/inputtableKey.d.ts.map +1 -0
  141. package/lib/Utils/TypeFocusSink/inputtableKey.js +5 -2
  142. package/lib/Utils/activityAltText.d.ts +8 -0
  143. package/lib/Utils/activityAltText.d.ts.map +1 -0
  144. package/lib/Utils/addTargetBlankToHyperlinksMarkdown.js +2 -2
  145. package/lib/Utils/createCustomEvent.js +8 -2
  146. package/lib/Utils/detectBrowser.js +4 -2
  147. package/lib/Utils/downscaleImageToDataURL/downscaleImageToDataURLUsingWorker.js +1 -1
  148. package/lib/Utils/downscaleImageToDataURL/index.js +1 -1
  149. package/lib/Utils/findAncestor.js +17 -0
  150. package/lib/Utils/getActivityUniqueId.js +4 -2
  151. package/lib/Utils/intersectionOf.d.ts +5 -0
  152. package/lib/Utils/intersectionOf.d.ts.map +1 -0
  153. package/lib/Utils/intersectionOf.js +17 -2
  154. package/lib/Utils/isZeroOrPositive.d.ts +5 -0
  155. package/lib/Utils/isZeroOrPositive.d.ts.map +1 -0
  156. package/lib/Utils/isZeroOrPositive.js +4 -1
  157. package/lib/Utils/mapMap.js +9 -2
  158. package/lib/Utils/readDataURIToBlob.js +1 -1
  159. package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts +7 -0
  160. package/lib/Utils/scrollIntoViewWithBlockNearest.d.ts.map +1 -0
  161. package/lib/Utils/scrollIntoViewWithBlockNearest.js +48 -0
  162. package/lib/Utils/shallowEquals.js +7 -3
  163. package/lib/Utils/supportPseudoClass.d.ts +2 -0
  164. package/lib/Utils/supportPseudoClass.d.ts.map +1 -0
  165. package/lib/Utils/supportPseudoClass.js +23 -0
  166. package/lib/Utils/tabbableElements.d.ts +2 -0
  167. package/lib/Utils/tabbableElements.d.ts.map +1 -0
  168. package/lib/Utils/tabbableElements.js +2 -2
  169. package/lib/connectToWebChat.js +15 -7
  170. package/lib/hooks/index.js +24 -24
  171. package/lib/hooks/internal/BypassSpeechSynthesisPonyfill.js +8 -7
  172. package/lib/hooks/internal/useChanged.js +8 -1
  173. package/lib/hooks/internal/useDispatchScrollPosition.js +3 -7
  174. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts +2 -0
  175. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.d.ts.map +1 -0
  176. package/lib/hooks/internal/useDispatchTranscriptFocusByActivityKey.js +15 -0
  177. package/lib/hooks/internal/useFocusVisible.d.ts +3 -0
  178. package/lib/hooks/internal/useFocusVisible.d.ts.map +1 -0
  179. package/lib/hooks/internal/useFocusVisible.js +48 -0
  180. package/lib/hooks/internal/useMemoWithPrevious.d.ts +3 -0
  181. package/lib/hooks/internal/useMemoWithPrevious.d.ts.map +1 -0
  182. package/lib/hooks/internal/useMemoWithPrevious.js +22 -0
  183. package/lib/hooks/internal/useMemoize.d.ts +14 -0
  184. package/lib/hooks/internal/useMemoize.d.ts.map +1 -0
  185. package/lib/hooks/internal/useMemoize.js +11 -2
  186. package/lib/hooks/internal/useObserveFocusVisible.d.ts +3 -0
  187. package/lib/hooks/internal/useObserveFocusVisible.d.ts.map +1 -0
  188. package/lib/hooks/internal/useObserveFocusVisible.js +208 -0
  189. package/lib/hooks/internal/usePrevious.d.ts +2 -0
  190. package/lib/hooks/internal/usePrevious.d.ts.map +1 -0
  191. package/lib/hooks/internal/usePrevious.js +18 -0
  192. package/lib/hooks/internal/useStateRef.d.ts +3 -0
  193. package/lib/hooks/internal/useStateRef.d.ts.map +1 -0
  194. package/lib/hooks/internal/useStateRef.js +40 -0
  195. package/lib/hooks/internal/useValueRef.d.ts +3 -0
  196. package/lib/hooks/internal/useValueRef.d.ts.map +1 -0
  197. package/lib/hooks/internal/useValueRef.js +25 -0
  198. package/lib/hooks/useFocus.d.ts +1 -1
  199. package/lib/hooks/useFocus.d.ts.map +1 -1
  200. package/lib/hooks/useFocus.js +1 -1
  201. package/lib/hooks/useObserveTranscriptFocus.d.ts +2 -2
  202. package/lib/hooks/useObserveTranscriptFocus.d.ts.map +1 -1
  203. package/lib/hooks/useObserveTranscriptFocus.js +1 -1
  204. package/lib/hooks/useRenderMarkdownAsHTML.d.ts.map +1 -1
  205. package/lib/hooks/useRenderMarkdownAsHTML.js +1 -1
  206. package/lib/hooks/useScrollTo.d.ts.map +1 -1
  207. package/lib/hooks/useScrollTo.js +1 -1
  208. package/lib/hooks/useScrollToEnd.d.ts.map +1 -1
  209. package/lib/hooks/useScrollToEnd.js +1 -1
  210. package/lib/hooks/useSendFiles.d.ts.map +1 -1
  211. package/lib/hooks/useSendFiles.js +4 -4
  212. package/lib/index.d.ts +13 -3
  213. package/lib/index.d.ts.map +1 -1
  214. package/lib/index.js +20 -18
  215. package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts +5 -0
  216. package/lib/providers/ActivityTree/ActivityTreeComposer.d.ts.map +1 -0
  217. package/lib/providers/ActivityTree/ActivityTreeComposer.js +86 -0
  218. package/lib/providers/ActivityTree/private/Context.d.ts +9 -0
  219. package/lib/providers/ActivityTree/private/Context.d.ts.map +1 -0
  220. package/lib/providers/ActivityTree/private/Context.js +13 -0
  221. package/lib/providers/ActivityTree/private/types.d.ts +10 -0
  222. package/lib/providers/ActivityTree/private/types.d.ts.map +1 -0
  223. package/lib/providers/ActivityTree/private/types.js +2 -0
  224. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts +4 -0
  225. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.d.ts.map +1 -0
  226. package/lib/providers/ActivityTree/private/useActivitiesWithRenderer.js +58 -0
  227. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts +5 -0
  228. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.d.ts.map +1 -0
  229. package/lib/providers/ActivityTree/private/useActivityTreeWithRenderer.js +166 -0
  230. package/lib/providers/ActivityTree/private/useContext.d.ts +3 -0
  231. package/lib/providers/ActivityTree/private/useContext.d.ts.map +1 -0
  232. package/lib/providers/ActivityTree/private/useContext.js +24 -0
  233. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts +8 -0
  234. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.d.ts.map +1 -0
  235. package/lib/providers/ActivityTree/useActivityTreeWithRenderer.js +17 -0
  236. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts +37 -0
  237. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.d.ts.map +1 -0
  238. package/lib/providers/LiveRegionTwin/LiveRegionTwinComposer.js +160 -0
  239. package/lib/providers/LiveRegionTwin/private/Context.d.ts +10 -0
  240. package/lib/providers/LiveRegionTwin/private/Context.d.ts.map +1 -0
  241. package/lib/providers/LiveRegionTwin/private/Context.js +13 -0
  242. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts +12 -0
  243. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.d.ts.map +1 -0
  244. package/lib/providers/LiveRegionTwin/private/LiveRegionTwinContainer.js +100 -0
  245. package/lib/providers/LiveRegionTwin/private/types.d.ts +8 -0
  246. package/lib/providers/LiveRegionTwin/private/types.d.ts.map +1 -0
  247. package/lib/providers/LiveRegionTwin/private/types.js +2 -0
  248. package/lib/providers/LiveRegionTwin/private/useContext.d.ts +3 -0
  249. package/lib/providers/LiveRegionTwin/private/useContext.d.ts.map +1 -0
  250. package/lib/providers/LiveRegionTwin/private/useContext.js +24 -0
  251. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts +2 -0
  252. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.d.ts.map +1 -0
  253. package/lib/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.js +24 -0
  254. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts +3 -0
  255. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.d.ts.map +1 -0
  256. package/lib/providers/LiveRegionTwin/private/useStaticElementEntries.js +15 -0
  257. package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts +8 -0
  258. package/lib/providers/LiveRegionTwin/useQueueStaticElement.d.ts.map +1 -0
  259. package/lib/providers/LiveRegionTwin/useQueueStaticElement.js +20 -0
  260. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts +7 -0
  261. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.d.ts.map +1 -0
  262. package/lib/providers/TranscriptFocus/TranscriptFocusComposer.js +184 -0
  263. package/lib/providers/TranscriptFocus/private/Context.d.ts +12 -0
  264. package/lib/providers/TranscriptFocus/private/Context.d.ts.map +1 -0
  265. package/lib/providers/TranscriptFocus/private/Context.js +13 -0
  266. package/lib/providers/TranscriptFocus/private/useContext.d.ts +2 -0
  267. package/lib/providers/TranscriptFocus/private/useContext.d.ts.map +1 -0
  268. package/lib/providers/TranscriptFocus/private/useContext.js +24 -0
  269. package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts +2 -0
  270. package/lib/providers/TranscriptFocus/useActiveDescendantId.d.ts.map +1 -0
  271. package/lib/providers/TranscriptFocus/useActiveDescendantId.js +15 -0
  272. package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts +8 -0
  273. package/lib/providers/TranscriptFocus/useFocusByActivityKey.d.ts.map +1 -0
  274. package/lib/providers/TranscriptFocus/useFocusByActivityKey.js +21 -0
  275. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts +2 -0
  276. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.d.ts.map +1 -0
  277. package/lib/providers/TranscriptFocus/useFocusRelativeActivity.js +15 -0
  278. package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts +2 -0
  279. package/lib/providers/TranscriptFocus/useFocusedActivityKey.d.ts.map +1 -0
  280. package/lib/providers/TranscriptFocus/useFocusedActivityKey.js +15 -0
  281. package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts +2 -0
  282. package/lib/providers/TranscriptFocus/useFocusedExplicitly.d.ts.map +1 -0
  283. package/lib/providers/TranscriptFocus/useFocusedExplicitly.js +15 -0
  284. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts +2 -0
  285. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.d.ts.map +1 -0
  286. package/lib/providers/TranscriptFocus/useGetDescendantIdByActivityKey.js +15 -0
  287. package/package.json +30 -33
  288. package/src/Activity/CarouselFilmStripAttachment.js +0 -1
  289. package/src/Activity/Speak.tsx +21 -18
  290. package/src/Activity/StackedLayout.tsx +30 -35
  291. package/src/Attachment/FileAttachment.js +1 -1
  292. package/src/Attachment/FileContent.tsx +12 -0
  293. package/src/BasicSendBox.tsx +3 -2
  294. package/src/BasicToaster.js +5 -1
  295. package/src/BasicTranscript.tsx +894 -0
  296. package/src/BasicTypingIndicator.tsx +3 -2
  297. package/src/Composer.tsx +41 -29
  298. package/src/Middleware/Activity/createCoreMiddleware.tsx +47 -46
  299. package/src/Middleware/ActivityStatus/SendStatus/SendStatus.tsx +5 -3
  300. package/src/Middleware/ActivityStatus/Timestamp.tsx +5 -3
  301. package/src/Middleware/ActivityStatus/createSendStatusMiddleware.js +2 -0
  302. package/src/Middleware/Attachment/createCoreMiddleware.tsx +28 -28
  303. package/src/Middleware/AttachmentForScreenReader/createCoreMiddleware.tsx +25 -23
  304. package/src/Middleware/Avatar/createCoreMiddleware.tsx +9 -8
  305. package/src/Middleware/CardAction/createCoreMiddleware.js +9 -2
  306. package/src/Middleware/ScrollToEndButton/createScrollToEndButtonMiddleware.ts +11 -9
  307. package/src/Middleware/Toast/createCoreMiddleware.tsx +12 -10
  308. package/src/Middleware/TypingIndicator/createCoreMiddleware.tsx +8 -2
  309. package/src/ScreenReaderActivity.js +18 -3
  310. package/src/{ScreenReaderText.js → ScreenReaderText.tsx} +27 -9
  311. package/src/SendBox/{IconButton.js → IconButton.tsx} +19 -3
  312. package/src/SendBox/{SuggestedAction.js → SuggestedAction.tsx} +72 -35
  313. package/src/SendBox/SuggestedActions.tsx +48 -21
  314. package/src/Styles/StyleSet/BasicTranscript.ts +34 -20
  315. package/src/Styles/StyleSet/Bubble.ts +0 -1
  316. package/src/Styles/StyleSet/CarouselFilmStrip.ts +12 -10
  317. package/src/Styles/StyleSet/CarouselFilmStripAttachment.ts +4 -3
  318. package/src/Styles/StyleSet/KeyboardHelp.ts +157 -0
  319. package/src/Styles/StyleSet/ScrollToEndButton.ts +1 -1
  320. package/src/Styles/StyleSet/SendBoxButton.ts +84 -13
  321. package/src/Styles/StyleSet/SendBoxTextBox.ts +1 -2
  322. package/src/Styles/StyleSet/StackedLayout.ts +13 -11
  323. package/src/Styles/StyleSet/SuggestedAction.ts +129 -42
  324. package/src/Styles/StyleSet/SuggestedActions.ts +13 -5
  325. package/src/Styles/createStyleSet.ts +2 -1
  326. package/src/Styles/mirrorStyle.js +10 -2
  327. package/src/Toast/NotificationIcon.js +4 -1
  328. package/src/Toast/createToastMiddleware.tsx +4 -1
  329. package/src/Transcript/ActivityRow.tsx +123 -0
  330. package/src/Transcript/ActivityTextAlt.tsx +31 -0
  331. package/src/Transcript/FocusTrap.tsx +64 -0
  332. package/src/Transcript/KeyboardHelp.tsx +282 -0
  333. package/src/Transcript/LiveRegionTranscript.tsx +196 -0
  334. package/src/Transcript/types.ts +1 -0
  335. package/src/Transcript/useActivityAccessibleName.ts +84 -0
  336. package/src/Transcript/useTypistNames.ts +37 -0
  337. package/src/Utils/{AccessibleButton.js → AccessibleButton.tsx} +19 -4
  338. package/src/Utils/{FocusRedirector.js → FocusRedirector.tsx} +21 -8
  339. package/src/Utils/InlineMarkdown.js +18 -2
  340. package/src/Utils/TypeFocusSink/inputtableKey.ts +5 -1
  341. package/src/Utils/createCustomEvent.js +7 -1
  342. package/src/Utils/detectBrowser.js +2 -1
  343. package/src/Utils/findAncestor.ts +12 -0
  344. package/src/Utils/getActivityUniqueId.ts +5 -0
  345. package/src/Utils/intersectionOf.ts +14 -0
  346. package/src/Utils/isZeroOrPositive.ts +7 -0
  347. package/src/Utils/mapMap.js +7 -1
  348. package/src/Utils/scrollIntoViewWithBlockNearest.ts +20 -0
  349. package/src/Utils/shallowEquals.js +8 -1
  350. package/src/Utils/supportPseudoClass.ts +17 -0
  351. package/src/Utils/{tabbableElements.js → tabbableElements.ts} +6 -5
  352. package/src/connectToWebChat.js +11 -4
  353. package/src/hooks/internal/BypassSpeechSynthesisPonyfill.js +3 -1
  354. package/src/hooks/internal/useChanged.ts +17 -0
  355. package/src/hooks/internal/useDispatchScrollPosition.js +1 -3
  356. package/src/hooks/internal/useDispatchTranscriptFocusByActivityKey.ts +5 -0
  357. package/src/hooks/internal/useFocusVisible.ts +22 -0
  358. package/src/hooks/internal/useMemoWithPrevious.ts +16 -0
  359. package/src/hooks/internal/useMemoize.spec.js +1 -1
  360. package/src/hooks/internal/useMemoize.ts +53 -0
  361. package/src/hooks/internal/useObserveFocusVisible.ts +252 -0
  362. package/src/hooks/internal/usePrevious.ts +12 -0
  363. package/src/hooks/internal/useStateRef.ts +31 -0
  364. package/src/hooks/internal/useValueRef.ts +22 -0
  365. package/src/hooks/useFocus.ts +1 -1
  366. package/src/hooks/useObserveTranscriptFocus.ts +2 -2
  367. package/src/hooks/useRenderMarkdownAsHTML.ts +4 -5
  368. package/src/hooks/useScrollTo.ts +4 -3
  369. package/src/hooks/useScrollToEnd.ts +4 -3
  370. package/src/hooks/useSendFiles.ts +7 -5
  371. package/src/index.ts +0 -1
  372. package/src/providers/ActivityTree/ActivityTreeComposer.tsx +74 -0
  373. package/src/providers/ActivityTree/private/Context.ts +12 -0
  374. package/src/providers/ActivityTree/private/types.ts +12 -0
  375. package/src/providers/ActivityTree/private/useActivitiesWithRenderer.ts +64 -0
  376. package/src/providers/ActivityTree/private/useActivityTreeWithRenderer.ts +135 -0
  377. package/src/providers/ActivityTree/private/useContext.ts +15 -0
  378. package/src/providers/ActivityTree/useActivityTreeWithRenderer.ts +16 -0
  379. package/src/providers/LiveRegionTwin/LiveRegionTwinComposer.tsx +161 -0
  380. package/src/providers/LiveRegionTwin/private/Context.ts +15 -0
  381. package/src/providers/LiveRegionTwin/private/LiveRegionTwinContainer.tsx +80 -0
  382. package/src/providers/LiveRegionTwin/private/types.ts +10 -0
  383. package/src/providers/LiveRegionTwin/private/useContext.ts +15 -0
  384. package/src/providers/LiveRegionTwin/private/useMarkAllAsRenderedEffect.ts +13 -0
  385. package/src/providers/LiveRegionTwin/private/useStaticElementEntries.ts +7 -0
  386. package/src/providers/LiveRegionTwin/useQueueStaticElement.ts +12 -0
  387. package/src/providers/TranscriptFocus/TranscriptFocusComposer.tsx +180 -0
  388. package/src/providers/TranscriptFocus/private/Context.ts +16 -0
  389. package/src/providers/TranscriptFocus/private/useContext.ts +13 -0
  390. package/src/providers/TranscriptFocus/useActiveDescendantId.ts +5 -0
  391. package/src/providers/TranscriptFocus/useFocusByActivityKey.ts +14 -0
  392. package/src/providers/TranscriptFocus/useFocusRelativeActivity.ts +5 -0
  393. package/src/providers/TranscriptFocus/useFocusedActivityKey.ts +5 -0
  394. package/src/providers/TranscriptFocus/useFocusedExplicitly.ts +5 -0
  395. package/src/providers/TranscriptFocus/useGetDescendantIdByActivityKey.ts +5 -0
  396. package/.eslintignore +0 -1
  397. package/lib/Middleware/GroupActivities/createCoreMiddleware.js +0 -69
  398. package/lib/Utils/findLastIndex.js +0 -32
  399. package/lib/hooks/internal/useAcknowledgedActivity.js +0 -90
  400. package/lib/hooks/internal/useDispatchTranscriptFocus.js +0 -19
  401. package/src/BasicTranscript.js +0 -1139
  402. package/src/Middleware/GroupActivities/createCoreMiddleware.js +0 -57
  403. package/src/Utils/findLastIndex.js +0 -11
  404. package/src/Utils/findLastIndex.spec.js +0 -31
  405. package/src/Utils/getActivityUniqueId.js +0 -3
  406. package/src/Utils/intersectionOf.js +0 -11
  407. package/src/Utils/isZeroOrPositive.js +0 -4
  408. package/src/hooks/internal/useAcknowledgedActivity.js +0 -65
  409. package/src/hooks/internal/useChanged.js +0 -10
  410. package/src/hooks/internal/useDispatchTranscriptFocus.js +0 -7
  411. package/src/hooks/internal/useMemoize.js +0 -37
@@ -0,0 +1,196 @@
1
+ import { hooks } from 'botframework-webchat-api';
2
+ import classNames from 'classnames';
3
+ import PropTypes from 'prop-types';
4
+ import random from 'math-random';
5
+ import React, { useEffect, useMemo, useRef } from 'react';
6
+ import type { FC, RefObject, VFC } from 'react';
7
+ import type { WebChatActivity } from 'botframework-webchat-core';
8
+
9
+ import LiveRegionTwinComposer from '../providers/LiveRegionTwin/LiveRegionTwinComposer';
10
+ import ScreenReaderActivity from '../ScreenReaderActivity';
11
+ import tabbableElements from '../Utils/tabbableElements';
12
+ import useActivityTreeWithRenderer from '../providers/ActivityTree/useActivityTreeWithRenderer';
13
+ import useQueueStaticElement from '../providers/LiveRegionTwin/useQueueStaticElement';
14
+ import useStyleToEmotionObject from '../hooks/internal/useStyleToEmotionObject';
15
+ import useTypistNames from './useTypistNames';
16
+
17
+ import type { ActivityElementMap } from './types';
18
+
19
+ const { useGetKeyByActivity, useLocalizer, useStyleOptions } = hooks;
20
+
21
+ const ROOT_STYLE = {
22
+ '&.webchat__live-region-transcript': {
23
+ '& .webchat__live-region-transcript__interactive-note, & .webchat__live-region-transcript__text-element': {
24
+ color: 'transparent',
25
+ height: 1,
26
+ overflow: 'hidden',
27
+ position: 'absolute',
28
+ top: 0,
29
+ whiteSpace: 'nowrap',
30
+ width: 1
31
+ }
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Checks if the rendering activity is presentational or not. Returns `true` if presentational, otherwise, `false`.
37
+ *
38
+ * Presentational activity, will be rendered visually but not going through screen reader.
39
+ */
40
+ function isPresentational(activity: WebChatActivity): boolean {
41
+ if (activity.type !== 'message') {
42
+ return;
43
+ }
44
+
45
+ const { channelData } = activity;
46
+
47
+ // "Fallback text" includes both message text and narratives for attachments.
48
+ // Emptying out "fallback text" essentially mute for both message and attachments.
49
+ const fallbackText = channelData?.['webchat:fallback-text'];
50
+
51
+ if (typeof fallbackText === 'string') {
52
+ return !fallbackText;
53
+ }
54
+
55
+ // If there are "displayText" (MessageBack), "text", or any attachments, there are something to narrate.
56
+ return !(channelData?.messageBack?.displayText || activity.text || activity.attachments?.length);
57
+ }
58
+
59
+ type RenderingActivities = Map<string, WebChatActivity>;
60
+
61
+ type LiveRegionTranscriptCoreProps = {
62
+ activityElementMapRef: RefObject<ActivityElementMap>;
63
+ };
64
+
65
+ const LiveRegionTranscriptCore: FC<LiveRegionTranscriptCoreProps> = ({ activityElementMapRef }) => {
66
+ const [flattenedActivityTree] = useActivityTreeWithRenderer({ flat: true });
67
+ const [typistNames] = useTypistNames();
68
+ const getKeyByActivity = useGetKeyByActivity();
69
+ const localize = useLocalizer();
70
+ const queueStaticElement = useQueueStaticElement();
71
+
72
+ const liveRegionInteractiveLabelAlt = localize('TRANSCRIPT_LIVE_REGION_INTERACTIVE_LABEL_ALT');
73
+ const liveRegionInteractiveWithLinkLabelAlt = localize('TRANSCRIPT_LIVE_REGION_INTERACTIVE_WITH_LINKS_LABEL_ALT');
74
+ const typingIndicator =
75
+ !!typistNames.length &&
76
+ localize(
77
+ typistNames.length > 1 ? 'TYPING_INDICATOR_MULTIPLE_TEXT' : 'TYPING_INDICATOR_SINGLE_TEXT',
78
+ typistNames[0]
79
+ );
80
+
81
+ const renderingActivities = useMemo<Readonly<RenderingActivities>>(
82
+ () =>
83
+ Object.freeze(
84
+ flattenedActivityTree.reduce<RenderingActivities>(
85
+ (intermediate, { activity }) => intermediate.set(getKeyByActivity(activity), activity),
86
+ new Map<string, WebChatActivity>()
87
+ )
88
+ ),
89
+ [flattenedActivityTree, getKeyByActivity]
90
+ );
91
+
92
+ const prevRenderingActivitiesRef = useRef<Readonly<RenderingActivities>>();
93
+
94
+ useEffect(() => {
95
+ const { current: prevRenderingActivities } = prevRenderingActivitiesRef;
96
+ const appendedActivities: { activity: WebChatActivity; key: string }[] = [];
97
+
98
+ // Bottom-up, find activities which are recently appended (i.e. new activity will have a new key).
99
+ // We only consider new activities added to the bottom of the chat history.
100
+ // Based on how `aria-relevant="additions"` works, activities that are updated, deleted, or reordered, should be ignored.
101
+ for (const [key, activity] of Array.from(renderingActivities.entries()).reverse()) {
102
+ if (prevRenderingActivities?.has(key)) {
103
+ break;
104
+ }
105
+
106
+ appendedActivities.unshift({ activity, key });
107
+
108
+ isPresentational(activity) || queueStaticElement(<ScreenReaderActivity activity={activity} />);
109
+ }
110
+
111
+ const hasNewLink = appendedActivities.some(({ key }) => activityElementMapRef.current.get(key)?.querySelector('a'));
112
+
113
+ const hasNewWidget = appendedActivities.some(
114
+ ({ key }) =>
115
+ !!tabbableElements(
116
+ activityElementMapRef.current.get(key)?.querySelector('.webchat__basic-transcript__activity-body')
117
+ ).length
118
+ );
119
+
120
+ if (hasNewLink || hasNewWidget) {
121
+ // eslint-disable-next-line no-magic-numbers
122
+ const labelId = `webchat__live-region-transcript__interactive-note--${random().toString(36).substr(2, 5)}`;
123
+
124
+ queueStaticElement(
125
+ // Inside ARIA live region:
126
+ // - Edge + Narrator:
127
+ // - It read if `aria-labelledby` or `aria-label` is set;
128
+ // - It read nothing if `aria-labelledby` or `aria-label` are not set (in this case, it read "note").
129
+ // - Safari + VoiceOver and Chrome + NVDA:
130
+ // - They read its content and ignore `aria-labelledby` or `aria-label`
131
+ // - They will not read if it is simply <div aria-label="Something" /> without content (self-closing tag).
132
+ // For best compatibility, we need both `aria-labelledby` and contented <div>.
133
+ <div
134
+ aria-atomic="true"
135
+ aria-labelledby={labelId}
136
+ className="webchat__live-region-transcript__interactive-note"
137
+ role="note"
138
+ >
139
+ {/* "id" is required for "aria-activedescendant" */}
140
+ {/* eslint-disable-next-line react/forbid-dom-props */}
141
+ <span id={labelId}>{hasNewLink ? liveRegionInteractiveWithLinkLabelAlt : liveRegionInteractiveLabelAlt}</span>
142
+ </div>
143
+ );
144
+ }
145
+
146
+ prevRenderingActivitiesRef.current = renderingActivities;
147
+ }, [
148
+ activityElementMapRef,
149
+ getKeyByActivity,
150
+ liveRegionInteractiveLabelAlt,
151
+ liveRegionInteractiveWithLinkLabelAlt,
152
+ prevRenderingActivitiesRef,
153
+ queueStaticElement,
154
+ renderingActivities
155
+ ]);
156
+
157
+ useEffect(() => {
158
+ typingIndicator && queueStaticElement(typingIndicator);
159
+ }, [queueStaticElement, typingIndicator]);
160
+
161
+ return null;
162
+ };
163
+
164
+ type LiveRegionTranscriptProps = {
165
+ activityElementMapRef: RefObject<ActivityElementMap>;
166
+ };
167
+
168
+ const LiveRegionTranscript: VFC<LiveRegionTranscriptProps> = ({ activityElementMapRef }) => {
169
+ const [{ internalLiveRegionFadeAfter }] = useStyleOptions();
170
+ const localize = useLocalizer();
171
+ const rootClassName = useStyleToEmotionObject()(ROOT_STYLE) + '';
172
+
173
+ const transcriptRoleDescription = localize('TRANSCRIPT_ARIA_ROLE_ALT');
174
+
175
+ return (
176
+ <LiveRegionTwinComposer
177
+ aria-roledescription={transcriptRoleDescription}
178
+ className={classNames('webchat__live-region-transcript', rootClassName)}
179
+ fadeAfter={internalLiveRegionFadeAfter}
180
+ role="log"
181
+ textElementClassName="webchat__live-region-transcript__text-element"
182
+ >
183
+ <LiveRegionTranscriptCore activityElementMapRef={activityElementMapRef} />
184
+ </LiveRegionTwinComposer>
185
+ );
186
+ };
187
+
188
+ LiveRegionTranscript.propTypes = {
189
+ // PropTypes cannot be fully expressed in TypeScript.
190
+ // @ts-ignore
191
+ activityElementMapRef: PropTypes.shape({
192
+ current: PropTypes.instanceOf(Map)
193
+ }).isRequired
194
+ };
195
+
196
+ export default LiveRegionTranscript;
@@ -0,0 +1 @@
1
+ export type ActivityElementMap = Map<string, HTMLElement>;
@@ -0,0 +1,84 @@
1
+ import { hooks } from 'botframework-webchat-api';
2
+ import { useEffect, useMemo, useState } from 'react';
3
+ import type { RefObject } from 'react';
4
+ import type { WebChatActivity } from 'botframework-webchat-core';
5
+
6
+ import activityAltText from '../Utils/activityAltText';
7
+ import tabbableElements from '../Utils/tabbableElements';
8
+ import useRenderMarkdownAsHTML from '../hooks/useRenderMarkdownAsHTML';
9
+ import useValueRef from '../hooks/internal/useValueRef';
10
+
11
+ enum InteractiveType {
12
+ LINK,
13
+ WIDGET
14
+ }
15
+
16
+ const { useAvatarForBot, useLocalizer } = hooks;
17
+
18
+ const ACTIVITY_NUM_ATTACHMENTS_ALT_IDS = {
19
+ few: 'ACTIVITY_NUM_ATTACHMENTS_FEW_ALT',
20
+ many: 'ACTIVITY_NUM_ATTACHMENTS_MANY_ALT',
21
+ one: 'ACTIVITY_NUM_ATTACHMENTS_ONE_ALT',
22
+ other: 'ACTIVITY_NUM_ATTACHMENTS_OTHER_ALT',
23
+ two: 'ACTIVITY_NUM_ATTACHMENTS_TWO_ALT'
24
+ };
25
+
26
+ export default function useActivityAccessibleName(activity: WebChatActivity, bodyRef: RefObject<HTMLElement>) {
27
+ const [{ initials: botInitials }] = useAvatarForBot();
28
+ const [interactiveType, setInteractiveType] = useState<InteractiveType | false>(false);
29
+ const fromSelf = activity.from?.role === 'user';
30
+ const localize = useLocalizer();
31
+ const localizeWithPlural = useLocalizer({ plural: true });
32
+ const numAttachments = activity.type === 'message' ? activity.attachments?.length || 0 : 0;
33
+ const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
34
+
35
+ const activityInteractiveAlt = localize('ACTIVITY_INTERACTIVE_LABEL_ALT'); // "Click to interact."
36
+ const activityInteractiveWithLinkAlt = localize('ACTIVITY_INTERACTIVE_WITH_LINKS_LABEL_ALT'); // "Click to interact."
37
+ const greetingAlt = useMemo(
38
+ () =>
39
+ (fromSelf ? localize('ACTIVITY_YOU_SAID_ALT') : localize('ACTIVITY_BOT_SAID_ALT', botInitials || '')).replace(
40
+ /\s{2,}/gu,
41
+ ' '
42
+ ),
43
+ [botInitials, fromSelf, localize]
44
+ );
45
+ const interactiveTypeRef = useValueRef(interactiveType);
46
+ const messageTextAlt = useMemo(
47
+ () => activityAltText(activity, renderMarkdownAsHTML),
48
+ [activity, renderMarkdownAsHTML]
49
+ );
50
+ const numAttachmentsAlt = useMemo(
51
+ () => (numAttachments ? localizeWithPlural(ACTIVITY_NUM_ATTACHMENTS_ALT_IDS, numAttachments) : ''),
52
+ [localizeWithPlural, numAttachments]
53
+ );
54
+
55
+ const accessibleName = useMemo(
56
+ // We are concatenating in a single string for Safari. If we split it up, Safari will only narrate the first section.
57
+ () =>
58
+ `${greetingAlt} ${messageTextAlt} ${numAttachmentsAlt} ${
59
+ interactiveType === InteractiveType.LINK
60
+ ? activityInteractiveWithLinkAlt
61
+ : interactiveType === InteractiveType.WIDGET
62
+ ? activityInteractiveAlt
63
+ : ''
64
+ }`,
65
+ [
66
+ activityInteractiveAlt,
67
+ activityInteractiveWithLinkAlt,
68
+ greetingAlt,
69
+ interactiveType,
70
+ messageTextAlt,
71
+ numAttachmentsAlt
72
+ ]
73
+ );
74
+
75
+ useEffect(() => {
76
+ const hasLinks = !!bodyRef.current?.querySelector('a');
77
+ const hasWidgets = !!tabbableElements(bodyRef.current).length;
78
+
79
+ interactiveTypeRef.current !== hasWidgets &&
80
+ setInteractiveType(hasLinks ? InteractiveType.LINK : hasWidgets ? InteractiveType.WIDGET : false);
81
+ }, [bodyRef, interactiveTypeRef, setInteractiveType]);
82
+
83
+ return [accessibleName];
84
+ }
@@ -0,0 +1,37 @@
1
+ import { hooks } from 'botframework-webchat-api';
2
+ import { useEffect, useRef } from 'react';
3
+
4
+ const { useActiveTyping } = hooks;
5
+
6
+ function arrayEquals<T>(x: readonly T[], y: readonly T[]): boolean {
7
+ return x.length === y.length && x.every((value, index) => y[+index] === value);
8
+ }
9
+
10
+ /** Gets names of users who are actively typing, sorted by the time they started typing. */
11
+ export default function useTypistNames(): readonly [readonly string[]] {
12
+ const [activeTyping] = useActiveTyping();
13
+ const prevTypistNamesStateRef = useRef<readonly [readonly string[]]>(
14
+ Object.freeze([Object.freeze([] as string[])]) as [readonly string[]]
15
+ );
16
+
17
+ const activeTypingFromOthersValues = Object.values(activeTyping).filter(({ role }) => role !== 'user');
18
+
19
+ // Sort the list by the first typist.
20
+ const sortedActiveTypingFromOthersValues = activeTypingFromOthersValues.sort(({ at: x }, { at: y }) => x - y);
21
+
22
+ const typistNamesState: readonly [readonly string[]] = Object.freeze([
23
+ Object.freeze(sortedActiveTypingFromOthersValues.map(({ name }) => name))
24
+ ]) as readonly [readonly string[]];
25
+
26
+ const { current: prevTypistNamesState } = prevTypistNamesStateRef;
27
+
28
+ const nextTypistNamesState = arrayEquals(typistNamesState[0], prevTypistNamesState[0])
29
+ ? prevTypistNamesState
30
+ : typistNamesState;
31
+
32
+ useEffect(() => {
33
+ prevTypistNamesStateRef.current = nextTypistNamesState;
34
+ }, [prevTypistNamesStateRef, nextTypistNamesState]);
35
+
36
+ return nextTypistNamesState;
37
+ }
@@ -1,10 +1,19 @@
1
1
  /* eslint no-magic-numbers: ["error", { "ignore": [-1] }] */
2
2
 
3
3
  import PropTypes from 'prop-types';
4
- import React, { forwardRef, useRef } from 'react';
4
+ import React, { forwardRef, MouseEventHandler, ReactNode, useRef } from 'react';
5
5
 
6
6
  const PREVENT_DEFAULT_HANDLER = event => event.preventDefault();
7
7
 
8
+ type AccessibleButtonProps = {
9
+ 'aria-hidden'?: boolean;
10
+ children?: ReactNode;
11
+ disabled?: boolean;
12
+ onClick?: MouseEventHandler<HTMLButtonElement>;
13
+ tabIndex?: number;
14
+ type: 'button';
15
+ } & React.ButtonHTMLAttributes<HTMLButtonElement>;
16
+
8
17
  // Differences between <button> and <AccessibleButton>:
9
18
  // - Disable behavior
10
19
  // - When the widget is disabled
@@ -23,8 +32,8 @@ const PREVENT_DEFAULT_HANDLER = event => event.preventDefault();
23
32
  // - aria-disabled="true" is the source of truth
24
33
  // - If the widget is contained by a <form>, the developer need to filter out some `onSubmit` event caused by this widget
25
34
 
26
- const AccessibleButton = forwardRef(
27
- ({ 'aria-hidden': ariaHidden, disabled, onClick, tabIndex, ...props }, forwardedRef) => {
35
+ const AccessibleButton = forwardRef<HTMLButtonElement, AccessibleButtonProps>(
36
+ ({ 'aria-hidden': ariaHidden, children, disabled, onClick, tabIndex, ...props }, forwardedRef) => {
28
37
  const targetRef = useRef();
29
38
 
30
39
  const ref = forwardedRef || targetRef;
@@ -38,13 +47,16 @@ const AccessibleButton = forwardRef(
38
47
  tabIndex={disabled ? -1 : tabIndex}
39
48
  {...props}
40
49
  type="button"
41
- />
50
+ >
51
+ {children}
52
+ </button>
42
53
  );
43
54
  }
44
55
  );
45
56
 
46
57
  AccessibleButton.defaultProps = {
47
58
  'aria-hidden': undefined,
59
+ children: undefined,
48
60
  disabled: undefined,
49
61
  onClick: undefined,
50
62
  tabIndex: undefined
@@ -54,9 +66,12 @@ AccessibleButton.displayName = 'AccessibleButton';
54
66
 
55
67
  AccessibleButton.propTypes = {
56
68
  'aria-hidden': PropTypes.bool,
69
+ children: PropTypes.any,
57
70
  disabled: PropTypes.bool,
58
71
  onClick: PropTypes.func,
59
72
  tabIndex: PropTypes.number,
73
+ // TypeScript class is not mappable to PropTypes.oneOf(['button'])
74
+ // @ts-ignore
60
75
  type: PropTypes.oneOf(['button']).isRequired
61
76
  };
62
77
 
@@ -1,6 +1,8 @@
1
1
  import PropTypes from 'prop-types';
2
2
  import React, { useCallback } from 'react';
3
3
 
4
+ import type { FC, MutableRefObject } from 'react';
5
+
4
6
  // This is an element, when focused, will send the focus to the ref specified in "redirectRef".
5
7
  // Although the focus is being redirected, browser will scroll this redirector element into view.
6
8
 
@@ -10,28 +12,39 @@ import React, { useCallback } from 'react';
10
12
  // When this focus redirector is put inside a scrollable container, you may want to resize or reposition
11
13
  // it to prevent unintentional scroll done by the browser default behavior.
12
14
 
13
- const FocusRedirector = ({ className, onFocus, redirectRef }) => {
14
- const handleFocus = useCallback(() => {
15
- const { current } = redirectRef;
15
+ type FocusRedirectorProps = {
16
+ className?: string;
17
+ onFocus?: () => void;
18
+ redirectRef?: MutableRefObject<HTMLElement>;
19
+ };
16
20
 
17
- current && current.focus();
21
+ const FocusRedirector: FC<FocusRedirectorProps> = ({ className, onFocus, redirectRef }) => {
22
+ const handleFocus = useCallback(() => {
23
+ redirectRef?.current?.focus();
18
24
  onFocus && onFocus();
19
25
  }, [onFocus, redirectRef]);
20
26
 
21
- return <div className={className} onFocus={handleFocus} tabIndex={0} />;
27
+ // For NVDA, we should set aria-hidden="true".
28
+ // When using NVDA in browse mode, press up/down arrow keys will focus on this redirector.
29
+ // This redirector is designed to capture TAB only and should not react on browse mode.
30
+ // However, reacting with browse mode is currently okay. Just better to leave it alone.
31
+ return <div aria-hidden="true" className={className} onFocus={handleFocus} tabIndex={0} />;
22
32
  };
23
33
 
24
34
  FocusRedirector.defaultProps = {
25
35
  className: undefined,
26
- onFocus: undefined
36
+ onFocus: undefined,
37
+ redirectRef: undefined
27
38
  };
28
39
 
29
40
  FocusRedirector.propTypes = {
30
41
  className: PropTypes.string,
31
42
  onFocus: PropTypes.func,
43
+ // PropTypes is not fully compatible with TypeScript.
44
+ // @ts-ignore
32
45
  redirectRef: PropTypes.shape({
33
- current: PropTypes.any
34
- }).isRequired
46
+ current: PropTypes.instanceOf(HTMLElement)
47
+ })
35
48
  };
36
49
 
37
50
  export default FocusRedirector;
@@ -1,6 +1,7 @@
1
1
  /* eslint react/no-danger: "off" */
2
2
 
3
3
  import { hooks } from 'botframework-webchat-api';
4
+ import { isForbiddenPropertyName } from 'botframework-webchat-core';
4
5
  import PropTypes from 'prop-types';
5
6
  import React, { useCallback, useMemo } from 'react';
6
7
  import updateIn from 'simple-update-in';
@@ -89,7 +90,13 @@ const InlineMarkdown = ({ children, onReference, references }) => {
89
90
 
90
91
  const html = useMemo(() => {
91
92
  const tree = markdownIt.parseInline(children, {
92
- references: references.reduce((references, key) => ({ ...references, [key]: { href: `#${refToHref[key]}` } }), {})
93
+ references: references.reduce(
94
+ (references, key) =>
95
+ // Mitigated through denylisting.
96
+ // eslint-disable-next-line security/detect-object-injection
97
+ isForbiddenPropertyName(key) ? references : { ...references, [key]: { href: `#${refToHref[key]}` } },
98
+ {}
99
+ )
93
100
  });
94
101
 
95
102
  // Turn "<a href="#retry">Retry</a>" into "<button data-ref="retry" type="button">Retry</button>"
@@ -104,7 +111,16 @@ const InlineMarkdown = ({ children, onReference, references }) => {
104
111
 
105
112
  const href = event.target.getAttribute('data-markdown-href');
106
113
 
107
- href && onReference && onReference(createCustomEvent('reference', { data: hrefToRef[href] }));
114
+ href &&
115
+ onReference &&
116
+ onReference(
117
+ createCustomEvent(
118
+ 'reference',
119
+ // Mitigated through denylisting.
120
+ // eslint-disable-next-line security/detect-object-injection
121
+ isForbiddenPropertyName(href) ? {} : { data: hrefToRef[href] }
122
+ )
123
+ );
108
124
  },
109
125
  [hrefToRef, onReference]
110
126
  );
@@ -10,6 +10,10 @@ const INPUTTABLE_KEY = {
10
10
  Subtract: '-' // Numpad subtract key
11
11
  };
12
12
 
13
+ const INPUTTABLE_KEY_KEYS = Object.keys(INPUTTABLE_KEY);
14
+
13
15
  export default function inputtableKey(key) {
14
- return key.length === 1 ? key : INPUTTABLE_KEY[key];
16
+ // Mitigated through allowlisting.
17
+ // eslint-disable-next-line security/detect-object-injection
18
+ return key.length === 1 ? key : INPUTTABLE_KEY_KEYS.includes(key) ? INPUTTABLE_KEY[key] : undefined;
15
19
  }
@@ -1,3 +1,5 @@
1
+ import { isForbiddenPropertyName } from 'botframework-webchat-core';
2
+
1
3
  export default function createCustomEvent(name, eventInitDict) {
2
4
  let event;
3
5
 
@@ -10,7 +12,11 @@ export default function createCustomEvent(name, eventInitDict) {
10
12
  }
11
13
 
12
14
  Object.entries(eventInitDict).forEach(([key, value]) => {
13
- event[key] = value;
15
+ if (!isForbiddenPropertyName(key)) {
16
+ // Mitigated through denylisting.
17
+ // eslint-disable-next-line security/detect-object-injection
18
+ event[key] = value;
19
+ }
14
20
  });
15
21
 
16
22
  return event;
@@ -1,6 +1,7 @@
1
1
  const { userAgent } = navigator;
2
2
 
3
3
  // not DRY so that list can be alphabetical
4
+ const android = /Linux; Android/u.test(userAgent);
4
5
  const chromium = !/Edge\//u.test(userAgent) && /Chrome\//u.test(userAgent);
5
6
  const edgeAnaheim = /Edg\//u.test(userAgent);
6
7
  const edgeUWP = /Edge\//u.test(userAgent);
@@ -10,4 +11,4 @@ const ie11 = /Trident\/7.0/u.test(userAgent);
10
11
  const chrome = chromium && !edgeAnaheim;
11
12
  const safari = !(chrome || edgeUWP || ie11 || firefox);
12
13
 
13
- export { chrome, chromium, edgeAnaheim, edgeUWP, firefox, ie11, safari };
14
+ export { android, chrome, chromium, edgeAnaheim, edgeUWP, firefox, ie11, safari };
@@ -0,0 +1,12 @@
1
+ export default function findAncestor(
2
+ node: HTMLElement,
3
+ predicate: (ancestor: HTMLElement) => boolean
4
+ ): HTMLElement | undefined {
5
+ let ancestor = node;
6
+
7
+ while ((ancestor = ancestor.parentElement)) {
8
+ if (predicate(ancestor)) {
9
+ return ancestor;
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { WebChatActivity } from 'botframework-webchat-core';
2
+
3
+ export default function getActivityUniqueId(activity: WebChatActivity): string {
4
+ return activity?.channelData?.clientActivityID || activity?.id;
5
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Intersects 2 or more arrays and return an array of values which are common to all of them.
3
+ */
4
+ export default function intersectionOf<T>(arg0: readonly T[], ...args: readonly (readonly T[])[]): T[] {
5
+ return args.reduce<T[]>(
6
+ (interim: T[], arg: readonly T[]) =>
7
+ interim.reduce((intersection: T[], item: T) => {
8
+ arg.includes(item) && intersection.push(item);
9
+
10
+ return intersection;
11
+ }, []),
12
+ [...arg0]
13
+ );
14
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns `true`, if the number is zero or positive, otherwise, `false`, including minus zeroes.
3
+ */
4
+ export default function isZeroOrPositive(value: number): boolean {
5
+ // This will handle minus-zero.
6
+ return 1 / value >= 0;
7
+ }
@@ -1,6 +1,12 @@
1
+ import { isForbiddenPropertyName } from 'botframework-webchat-core';
2
+
1
3
  export default function mapMap(map, mapper) {
2
4
  return Object.entries(map).reduce((result, [key, value]) => {
3
- result[key] = mapper(value, key);
5
+ if (!isForbiddenPropertyName(key)) {
6
+ // Mitigated through denylisting.
7
+ // eslint-disable-next-line security/detect-object-injection
8
+ result[key] = mapper(value, key);
9
+ }
4
10
 
5
11
  return result;
6
12
  }, {});
@@ -0,0 +1,20 @@
1
+ import computeScrollIntoView from 'compute-scroll-into-view';
2
+
3
+ /**
4
+ * Calls `targetElement.scrollIntoView({ block: 'nearest' })`.
5
+ *
6
+ * If browser do not support options for `scrollIntoView`, fallback to polyfill.
7
+ */
8
+ export default function scrollIntoViewWithBlockNearest(targetElement: HTMLElement): void {
9
+ // Checks if `scrollIntoView` support options or not.
10
+ // - https://github.com/Modernizr/Modernizr/issues/1568#issuecomment-419457972
11
+ // - https://stackoverflow.com/questions/46919627/is-it-possible-to-test-for-scrollintoview-browser-compatibility
12
+ if ('scrollBehavior' in document.documentElement.style) {
13
+ return targetElement.scrollIntoView({ block: 'nearest' });
14
+ }
15
+
16
+ // We should only move transcript scrollable, and not other scrollable, such as document.body which is from the hosting page.
17
+ const [action] = computeScrollIntoView(targetElement, { block: 'nearest' });
18
+
19
+ action.el.scrollTop = action.top;
20
+ }
@@ -1,3 +1,5 @@
1
+ import { isForbiddenPropertyName } from 'botframework-webchat-core';
2
+
1
3
  export default function shallowEquals(x, y) {
2
4
  if (x === y) {
3
5
  return true;
@@ -6,5 +8,10 @@ export default function shallowEquals(x, y) {
6
8
  const xKeys = Object.keys(x);
7
9
  const yKeys = Object.keys(y);
8
10
 
9
- return xKeys.length === yKeys.length && xKeys.every(key => yKeys.includes(key) && x[key] === y[key]);
11
+ return (
12
+ xKeys.length === yKeys.length &&
13
+ // Mitigated through denylisting.
14
+ // eslint-disable-next-line security/detect-object-injection
15
+ xKeys.every(key => !isForbiddenPropertyName(key) && yKeys.includes(key) && x[key] === y[key])
16
+ );
10
17
  }
@@ -0,0 +1,17 @@
1
+ // This function is adopted from https://stackoverflow.com/questions/8531940/how-to-detect-if-browser-support-specified-css-pseudo-class
2
+ export default function supportPseudoClass(pseudoClass: string, nonce?: string): boolean {
3
+ const styleElement = document.createElement('style');
4
+
5
+ nonce && styleElement.setAttribute('nonce', nonce);
6
+ document.head.appendChild(styleElement);
7
+
8
+ try {
9
+ styleElement.sheet.insertRule('html' + pseudoClass + '{}', 0);
10
+
11
+ return true;
12
+ } catch (error) {
13
+ return false;
14
+ } finally {
15
+ document.head.removeChild(styleElement);
16
+ }
17
+ }
@@ -1,11 +1,12 @@
1
- export default function tabbableElements(element) {
1
+ export default function tabbableElements(element?: HTMLElement): HTMLElement[] {
2
2
  // This is an incomplete list, due to the complexity of testing for every scenario.
3
3
  // For full-list, please refer to https://allyjs.io/data-tables/focusable.html
4
- const candidates = element.querySelectorAll(
5
- 'a[href], area[href], audio[controls], button:not(:disabled), iframe, input:not(:disabled), keygen, select:not(:disabled), summary, textarea:not(:disabled), video[controls], [contenteditable], [tabindex]'
6
- );
4
+ const candidates: ArrayLike<HTMLElement> =
5
+ element?.querySelectorAll(
6
+ 'a[href], area[href], audio[controls], button:not(:disabled), iframe, input:not(:disabled), keygen, select:not(:disabled), summary, textarea:not(:disabled), video[controls], [contenteditable], [tabindex]'
7
+ ) || [];
7
8
 
8
- return [].filter.call(candidates, element => {
9
+ return ([] as HTMLElement[]).filter.call(candidates, (element: HTMLElement) => {
9
10
  const tabIndexAttribute = element.attributes.getNamedItem('tabindex');
10
11
 
11
12
  if (tabIndexAttribute && tabIndexAttribute.specified) {