botframework-webchat 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/.eslintrc.yml +5 -104
  2. package/.prettierrc.yml +1 -1
  3. package/README.md +33 -36
  4. package/babel.config.json +2 -2
  5. package/babel.sanitize-html.config.json +10 -0
  6. package/lib/AddFullBundle.d.ts +30 -0
  7. package/lib/AddFullBundle.d.ts.map +1 -0
  8. package/lib/AddFullBundle.js +76 -0
  9. package/lib/FullComposer.d.ts +8 -0
  10. package/lib/FullComposer.d.ts.map +1 -0
  11. package/lib/FullComposer.js +6 -16
  12. package/lib/FullReactWebChat.d.ts +8 -0
  13. package/lib/FullReactWebChat.d.ts.map +1 -0
  14. package/lib/FullReactWebChat.js +8 -23
  15. package/lib/adaptiveCards/AdaptiveCardsComposer.d.ts +10 -0
  16. package/lib/adaptiveCards/AdaptiveCardsComposer.d.ts.map +1 -0
  17. package/lib/adaptiveCards/AdaptiveCardsComposer.js +6 -2
  18. package/lib/adaptiveCards/AdaptiveCardsContext.d.ts +8 -0
  19. package/lib/adaptiveCards/AdaptiveCardsContext.d.ts.map +1 -0
  20. package/lib/adaptiveCards/AdaptiveCardsContext.js +3 -3
  21. package/lib/adaptiveCards/AdaptiveCardsStyleOptions.d.ts +5 -3
  22. package/lib/adaptiveCards/AdaptiveCardsStyleOptions.d.ts.map +1 -1
  23. package/lib/adaptiveCards/AdaptiveCardsStyleSet.d.ts +7 -0
  24. package/lib/adaptiveCards/AdaptiveCardsStyleSet.d.ts.map +1 -0
  25. package/lib/{FullBundleStyleOptions.js → adaptiveCards/AdaptiveCardsStyleSet.js} +0 -0
  26. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.d.ts +9 -0
  27. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.d.ts.map +1 -0
  28. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.js +1 -1
  29. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.d.ts +28 -0
  30. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.d.ts.map +1 -0
  31. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.js +13 -6
  32. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.d.ts +9 -0
  33. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.d.ts.map +1 -0
  34. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.js +8 -4
  35. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts +12 -0
  36. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts.map +1 -0
  37. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.js +310 -85
  38. package/lib/adaptiveCards/Attachment/AnimationCardContent.d.ts +10 -0
  39. package/lib/adaptiveCards/Attachment/AnimationCardContent.d.ts.map +1 -0
  40. package/lib/adaptiveCards/Attachment/AnimationCardContent.js +1 -1
  41. package/lib/adaptiveCards/Attachment/AudioCardContent.d.ts +10 -0
  42. package/lib/adaptiveCards/Attachment/AudioCardContent.d.ts.map +1 -0
  43. package/lib/adaptiveCards/Attachment/AudioCardContent.js +1 -1
  44. package/lib/adaptiveCards/Attachment/CommonCard.js +11 -6
  45. package/lib/adaptiveCards/Attachment/HeroCardContent.d.ts +10 -0
  46. package/lib/adaptiveCards/Attachment/HeroCardContent.d.ts.map +1 -0
  47. package/lib/adaptiveCards/Attachment/HeroCardContent.js +11 -6
  48. package/lib/adaptiveCards/Attachment/OAuthCardContent.d.ts +10 -0
  49. package/lib/adaptiveCards/Attachment/OAuthCardContent.d.ts.map +1 -0
  50. package/lib/adaptiveCards/Attachment/OAuthCardContent.js +11 -6
  51. package/lib/adaptiveCards/Attachment/ReceiptCardContent.d.ts +10 -0
  52. package/lib/adaptiveCards/Attachment/ReceiptCardContent.d.ts.map +1 -0
  53. package/lib/adaptiveCards/Attachment/ReceiptCardContent.js +11 -6
  54. package/lib/adaptiveCards/Attachment/SignInCardContent.d.ts +10 -0
  55. package/lib/adaptiveCards/Attachment/SignInCardContent.d.ts.map +1 -0
  56. package/lib/adaptiveCards/Attachment/SignInCardContent.js +1 -1
  57. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.d.ts +10 -0
  58. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.d.ts.map +1 -0
  59. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.js +11 -6
  60. package/lib/adaptiveCards/Attachment/VideoCardContent.d.ts +20 -0
  61. package/lib/adaptiveCards/Attachment/VideoCardContent.d.ts.map +1 -0
  62. package/lib/adaptiveCards/Attachment/VideoCardContent.js +7 -5
  63. package/lib/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js +7 -3
  64. package/lib/adaptiveCards/AttachmentForScreenReader/RichCardAttachment.js +1 -1
  65. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.d.ts +59 -0
  66. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.d.ts.map +1 -0
  67. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.js +8 -1
  68. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.d.ts +126 -0
  69. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.d.ts.map +1 -0
  70. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.js +29 -9
  71. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.d.ts +5 -0
  72. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.d.ts.map +1 -0
  73. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.js +2 -2
  74. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.d.ts +3 -0
  75. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.d.ts.map +1 -0
  76. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.js +5 -11
  77. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.d.ts +3 -0
  78. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.d.ts.map +1 -0
  79. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.js +2 -15
  80. package/lib/adaptiveCards/defaultStyleOptions.d.ts +4 -0
  81. package/lib/adaptiveCards/defaultStyleOptions.d.ts.map +1 -0
  82. package/lib/adaptiveCards/defaultStyleOptions.js +3 -2
  83. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.d.ts +3 -0
  84. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.d.ts.map +1 -0
  85. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.js +1 -1
  86. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.d.ts +2 -0
  87. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.d.ts.map +1 -0
  88. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.js +7 -5
  89. package/lib/adaptiveCards/hooks/internal/useUniqueId.js +1 -1
  90. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.d.ts +2 -0
  91. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.d.ts.map +1 -0
  92. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.js +3 -5
  93. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.d.ts +3 -0
  94. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.d.ts.map +1 -0
  95. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.js +1 -1
  96. package/lib/adaptiveCards/normalizeStyleOptions.d.ts +3 -0
  97. package/lib/adaptiveCards/normalizeStyleOptions.d.ts.map +1 -0
  98. package/lib/adaptiveCards/normalizeStyleOptions.js +1 -1
  99. package/lib/addVersion.js +2 -2
  100. package/lib/createBrowserWebSpeechPonyfillFactory.d.ts +3 -0
  101. package/lib/createBrowserWebSpeechPonyfillFactory.d.ts.map +1 -0
  102. package/lib/createBrowserWebSpeechPonyfillFactory.js +7 -4
  103. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.d.ts +17 -0
  104. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.d.ts.map +1 -0
  105. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.js +17 -11
  106. package/lib/createDirectLine.d.ts +17 -0
  107. package/lib/createDirectLine.d.ts.map +1 -0
  108. package/lib/createDirectLine.js +3 -2
  109. package/lib/createDirectLineAppServiceExtension.d.ts +7 -0
  110. package/lib/createDirectLineAppServiceExtension.d.ts.map +1 -0
  111. package/lib/createDirectLineAppServiceExtension.js +1 -1
  112. package/lib/createDirectLineSpeechAdapters.d.ts +25 -0
  113. package/lib/createDirectLineSpeechAdapters.d.ts.map +1 -0
  114. package/lib/createDirectLineSpeechAdapters.js +57 -3
  115. package/lib/createFullStyleSet.d.ts +1375 -0
  116. package/lib/createFullStyleSet.d.ts.map +1 -0
  117. package/lib/createFullStyleSet.js +2 -2
  118. package/lib/fullBundleDefaultStyleOptions.js +4 -4
  119. package/lib/hooks/useStyleOptions.d.ts +3 -0
  120. package/lib/hooks/useStyleOptions.d.ts.map +1 -0
  121. package/lib/hooks/useStyleOptions.js +21 -0
  122. package/lib/index-es5.d.ts +4 -20
  123. package/lib/index-es5.d.ts.map +1 -1
  124. package/lib/index-es5.js +3 -43
  125. package/lib/index-minimal.d.ts +9 -6
  126. package/lib/index-minimal.d.ts.map +1 -1
  127. package/lib/index-minimal.js +27 -21
  128. package/lib/index.d.ts +164 -18
  129. package/lib/index.d.ts.map +1 -1
  130. package/lib/index.js +19 -12
  131. package/lib/polyfill.d.ts +23 -0
  132. package/lib/polyfill.d.ts.map +1 -0
  133. package/lib/polyfill.js +46 -0
  134. package/lib/renderMarkdown.d.ts +6 -0
  135. package/lib/renderMarkdown.d.ts.map +1 -0
  136. package/lib/renderMarkdown.js +36 -5
  137. package/lib/renderWebChat.d.ts +3 -0
  138. package/lib/renderWebChat.d.ts.map +1 -0
  139. package/lib/renderWebChat.js +1 -1
  140. package/lib/speech/CustomAudioInputStream.d.ts +94 -0
  141. package/lib/speech/CustomAudioInputStream.d.ts.map +1 -0
  142. package/lib/speech/CustomAudioInputStream.js +441 -0
  143. package/lib/speech/bytesPerSample.d.ts +2 -0
  144. package/lib/speech/bytesPerSample.d.ts.map +1 -0
  145. package/lib/speech/bytesPerSample.js +12 -0
  146. package/lib/speech/createAudioConfig.d.ts +23 -0
  147. package/lib/speech/createAudioConfig.d.ts.map +1 -0
  148. package/lib/speech/createAudioConfig.js +90 -0
  149. package/lib/speech/createAudioContext.d.ts +3 -0
  150. package/lib/speech/createAudioContext.d.ts.map +1 -0
  151. package/lib/speech/createAudioContext.js +22 -0
  152. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.d.ts +9 -0
  153. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.d.ts.map +1 -0
  154. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.js +161 -0
  155. package/lib/speech/getUserMedia.d.ts +2 -0
  156. package/lib/speech/getUserMedia.d.ts.map +1 -0
  157. package/lib/speech/getUserMedia.js +25 -0
  158. package/lib/types/AdaptiveCardsPackage.d.ts +13 -0
  159. package/lib/types/AdaptiveCardsPackage.d.ts.map +1 -0
  160. package/lib/types/AdaptiveCardsPackage.js +2 -0
  161. package/lib/types/CognitiveServicesAudioOutputFormat.d.ts +3 -0
  162. package/lib/types/CognitiveServicesAudioOutputFormat.d.ts.map +1 -0
  163. package/lib/types/CognitiveServicesAudioOutputFormat.js +2 -0
  164. package/lib/types/CognitiveServicesCredentials.d.ts +18 -0
  165. package/lib/types/CognitiveServicesCredentials.d.ts.map +1 -0
  166. package/lib/types/CognitiveServicesCredentials.js +2 -0
  167. package/lib/types/CognitiveServicesTextNormalization.d.ts +3 -0
  168. package/lib/types/CognitiveServicesTextNormalization.d.ts.map +1 -0
  169. package/lib/types/CognitiveServicesTextNormalization.js +2 -0
  170. package/lib/{FullBundleStyleOptions.d.ts → types/FullBundleStyleOptions.d.ts} +1 -1
  171. package/lib/types/FullBundleStyleOptions.d.ts.map +1 -0
  172. package/lib/types/FullBundleStyleOptions.js +2 -0
  173. package/lib/useComposerProps.d.ts +22 -0
  174. package/lib/useComposerProps.d.ts.map +1 -0
  175. package/lib/useComposerProps.js +6 -8
  176. package/package.json +46 -48
  177. package/src/AddFullBundle.tsx +93 -0
  178. package/src/FullComposer.tsx +38 -0
  179. package/src/FullReactWebChat.tsx +32 -0
  180. package/src/__tests__/createDirectLine.spec.js +2 -0
  181. package/src/__tests__/renderMarkdown.spec.js +1 -1
  182. package/src/__tests__/versionTag.es5.spec.js +3 -0
  183. package/src/__tests__/versionTag.full.spec.js +3 -0
  184. package/src/__tests__/versionTag.minimal.spec.js +3 -0
  185. package/src/adaptiveCards/{AdaptiveCardsComposer.js → AdaptiveCardsComposer.tsx} +17 -5
  186. package/src/adaptiveCards/AdaptiveCardsContext.ts +12 -0
  187. package/src/adaptiveCards/AdaptiveCardsStyleOptions.ts +5 -3
  188. package/src/adaptiveCards/AdaptiveCardsStyleSet.ts +7 -0
  189. package/src/adaptiveCards/Attachment/{AdaptiveCardAttachment.js → AdaptiveCardAttachment.tsx} +8 -2
  190. package/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts +27 -17
  191. package/src/adaptiveCards/Attachment/{AdaptiveCardContent.js → AdaptiveCardContent.tsx} +8 -2
  192. package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +768 -0
  193. package/src/adaptiveCards/Attachment/{AnimationCardContent.js → AnimationCardContent.tsx} +9 -2
  194. package/src/adaptiveCards/Attachment/{AudioCardContent.js → AudioCardContent.tsx} +9 -2
  195. package/src/adaptiveCards/Attachment/CommonCard.js +2 -1
  196. package/src/adaptiveCards/Attachment/{HeroCardContent.js → HeroCardContent.tsx} +12 -3
  197. package/src/adaptiveCards/Attachment/{OAuthCardContent.js → OAuthCardContent.tsx} +11 -3
  198. package/src/adaptiveCards/Attachment/{ReceiptCardContent.js → ReceiptCardContent.tsx} +58 -28
  199. package/src/adaptiveCards/Attachment/{SignInCardContent.js → SignInCardContent.tsx} +9 -2
  200. package/src/adaptiveCards/Attachment/{ThumbnailCardContent.js → ThumbnailCardContent.tsx} +12 -3
  201. package/src/adaptiveCards/Attachment/{VideoCardContent.js → VideoCardContent.tsx} +15 -3
  202. package/src/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.ts +9 -1
  203. package/src/adaptiveCards/Styles/adaptiveCardHostConfig.ts +28 -8
  204. package/src/adaptiveCards/Styles/createAdaptiveCardsStyleSet.spec.js +2 -0
  205. package/src/adaptiveCards/Styles/createAdaptiveCardsStyleSet.ts +7 -4
  206. package/src/adaptiveCards/{createAdaptiveCardsAttachmentForScreenReaderMiddleware.js → createAdaptiveCardsAttachmentForScreenReaderMiddleware.tsx} +16 -15
  207. package/src/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.tsx +44 -0
  208. package/src/adaptiveCards/defaultStyleOptions.ts +2 -1
  209. package/src/adaptiveCards/hooks/internal/{useAdaptiveCardsContext.js → useAdaptiveCardsContext.ts} +1 -1
  210. package/src/adaptiveCards/hooks/internal/{useParseAdaptiveCardJSON.js → useParseAdaptiveCardJSON.ts} +5 -2
  211. package/src/adaptiveCards/hooks/internal/{useUniqueId.js → useUniqueId.ts} +1 -1
  212. package/src/adaptiveCards/hooks/{useAdaptiveCardsHostConfig.js → useAdaptiveCardsHostConfig.ts} +6 -8
  213. package/src/adaptiveCards/hooks/{useAdaptiveCardsPackage.js → useAdaptiveCardsPackage.ts} +2 -1
  214. package/src/createBrowserWebSpeechPonyfillFactory.ts +21 -0
  215. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.spec.js +36 -6
  216. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.ts +78 -0
  217. package/src/createDirectLine.ts +65 -0
  218. package/src/createDirectLineAppServiceExtension.ts +22 -0
  219. package/src/createDirectLineSpeechAdapters.ts +84 -0
  220. package/src/createFullStyleSet.ts +2 -2
  221. package/src/fullBundleDefaultStyleOptions.ts +3 -3
  222. package/src/hooks/useStyleOptions.ts +9 -0
  223. package/src/index-es5.ts +3 -28
  224. package/src/index-minimal.ts +3 -2
  225. package/src/index.ts +22 -10
  226. package/src/polyfill.ts +29 -0
  227. package/src/renderMarkdown.ts +140 -0
  228. package/src/renderWebChat.tsx +6 -0
  229. package/src/speech/CustomAudioInputStream.ts +356 -0
  230. package/src/speech/bytesPerSample.ts +4 -0
  231. package/src/speech/createAudioConfig.spec.js +23 -0
  232. package/src/speech/createAudioConfig.ts +77 -0
  233. package/src/speech/createAudioContext.ts +16 -0
  234. package/src/speech/createMicrophoneAudioConfigAndAudioContext.ts +146 -0
  235. package/src/speech/getUserMedia.ts +14 -0
  236. package/src/tsconfig.json +1 -1
  237. package/src/types/AdaptiveCardsPackage.ts +23 -0
  238. package/src/types/CognitiveServicesAudioOutputFormat.ts +28 -0
  239. package/src/types/CognitiveServicesCredentials.ts +28 -0
  240. package/src/types/CognitiveServicesTextNormalization.ts +3 -0
  241. package/src/{FullBundleStyleOptions.ts → types/FullBundleStyleOptions.ts} +1 -1
  242. package/src/{useComposerProps.js → useComposerProps.ts} +21 -11
  243. package/webpack.config.js +46 -6
  244. package/.eslintignore +0 -9
  245. package/lib/FullBundleStyleOptions.d.ts.map +0 -1
  246. package/src/FullComposer.js +0 -40
  247. package/src/FullReactWebChat.js +0 -47
  248. package/src/adaptiveCards/AdaptiveCardsContext.js +0 -5
  249. package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js +0 -492
  250. package/src/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.js +0 -41
  251. package/src/createBrowserWebSpeechPonyfillFactory.js +0 -16
  252. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.js +0 -58
  253. package/src/createDirectLine.js +0 -38
  254. package/src/createDirectLineAppServiceExtension.js +0 -12
  255. package/src/createDirectLineSpeechAdapters.js +0 -5
  256. package/src/index-es5.tsx +0 -49
  257. package/src/index-minimal.tsx +0 -9
  258. package/src/index.tsx +0 -45
  259. package/src/renderMarkdown.js +0 -97
  260. package/src/renderWebChat.js +0 -6
@@ -0,0 +1,768 @@
1
+ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 2] }] */
2
+
3
+ import {
4
+ Action as AdaptiveCardAction,
5
+ AdaptiveCard,
6
+ IMarkdownProcessingResult,
7
+ OpenUrlAction,
8
+ SubmitAction
9
+ } from 'adaptivecards';
10
+ import { Components, getTabIndex, hooks } from 'botframework-webchat-component';
11
+ import { DirectLineCardAction } from 'botframework-webchat-core';
12
+ import classNames from 'classnames';
13
+ import PropTypes from 'prop-types';
14
+ import React, {
15
+ KeyboardEventHandler,
16
+ MouseEventHandler,
17
+ useCallback,
18
+ useEffect,
19
+ useLayoutEffect,
20
+ useRef,
21
+ useState,
22
+ VFC
23
+ } from 'react';
24
+
25
+ import { BotFrameworkCardAction } from './AdaptiveCardBuilder';
26
+ import useAdaptiveCardsHostConfig from '../hooks/useAdaptiveCardsHostConfig';
27
+ import useAdaptiveCardsPackage from '../hooks/useAdaptiveCardsPackage';
28
+
29
+ const { ErrorBox } = Components;
30
+ const { useDisabled, useLocalizer, usePerformCardAction, useRenderMarkdownAsHTML, useScrollToEnd, useStyleSet } = hooks;
31
+
32
+ const node_env = process.env.node_env || process.env.NODE_ENV;
33
+
34
+ type UndoFunction = (() => void) | undefined;
35
+
36
+ function bunchUndos(...fns: UndoFunction[]): UndoFunction {
37
+ return () => fns.forEach(fn => fn?.());
38
+ }
39
+
40
+ /**
41
+ * Adds a class to the `HTMLElement`. Returns `true` if the class is added, otherwise, `undefined`.
42
+ */
43
+ function addClass(element: HTMLElement, className: string): true | undefined {
44
+ const { classList } = element;
45
+
46
+ if (!classList.contains(className)) {
47
+ classList.add(className);
48
+
49
+ return true;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Adds a class to the `HTMLElement` and re-add on mutations.
55
+ *
56
+ * @returns {function} A function, when called, will restore to previous state.
57
+ */
58
+ function addPersistentClassWithUndo(element: HTMLElement | undefined, className: string): UndoFunction {
59
+ if (!element) {
60
+ return;
61
+ }
62
+
63
+ if (addClass(element, className)) {
64
+ // After we add the class, keep observing the element to make sure the class is not removed.
65
+ const observer = new MutationObserver(() => addClass(element, className));
66
+
67
+ observer.observe(element, { attributes: true, attributeFilter: ['class'] });
68
+
69
+ return () => {
70
+ const classNames = new Set(element.className.split(' '));
71
+
72
+ classNames.delete(className);
73
+
74
+ element.className = Array.from(classNames).join(' ');
75
+ observer.disconnect();
76
+ };
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Returns `true`, if the object is a plain object and not a class, otherwise, `false`.
82
+ */
83
+ function isPlainObject(obj) {
84
+ return Object.getPrototypeOf(obj) === Object.prototype;
85
+ }
86
+
87
+ /**
88
+ * Sets an attribute.
89
+ *
90
+ * @returns {function} A function, when called, will restore to previous state.
91
+ */
92
+ function setAttributeWithUndo(
93
+ element: HTMLElement | undefined,
94
+ qualifiedName: string,
95
+ nextValue: string
96
+ ): UndoFunction {
97
+ if (!element) {
98
+ return;
99
+ }
100
+
101
+ const value = element.getAttribute(qualifiedName);
102
+
103
+ if (value !== nextValue) {
104
+ element.setAttribute(qualifiedName, nextValue);
105
+
106
+ return () => (value ? element.setAttribute(qualifiedName, value) : element.removeAttribute(qualifiedName));
107
+ }
108
+ }
109
+
110
+ /**
111
+ * An event handler for disabling event bubbling and propagation.
112
+ */
113
+ const disabledHandler = (event: Event) => {
114
+ event.preventDefault();
115
+ event.stopImmediatePropagation();
116
+ event.stopPropagation();
117
+ };
118
+
119
+ /**
120
+ * Listens to event once. Returns a function, when called, will stop listening.
121
+ */
122
+ function addEventListenerOnceWithUndo(
123
+ element: HTMLElement | undefined,
124
+ name: string,
125
+ handler: EventListener
126
+ ): UndoFunction {
127
+ if (!element) {
128
+ return;
129
+ }
130
+
131
+ /* eslint-disable-next-line prefer-const */
132
+ let detach: () => void;
133
+ const detachingHandler = event => {
134
+ try {
135
+ handler(event);
136
+ } finally {
137
+ // IE11 does not support { once: true }, so we need to detach manually.
138
+ detach();
139
+ }
140
+ };
141
+
142
+ detach = () => element.removeEventListener(name, detachingHandler);
143
+
144
+ element.addEventListener(name, detachingHandler, { once: true });
145
+
146
+ return detach;
147
+ }
148
+
149
+ /**
150
+ * Listens to event. Returns a function, when called, will stop listening.
151
+ */
152
+ function addEventListenerWithUndo(
153
+ element: HTMLElement | undefined,
154
+ name: string,
155
+ handler: EventListener
156
+ ): UndoFunction {
157
+ if (!element) {
158
+ return;
159
+ }
160
+
161
+ element.addEventListener(name, handler);
162
+
163
+ return () => element.removeEventListener(name, handler);
164
+ }
165
+
166
+ /**
167
+ * Disables an element with undo function.
168
+ *
169
+ * @returns {function} A function, when called, will restore to previous state.
170
+ */
171
+ function disableElementWithUndo(element: HTMLElement | undefined): UndoFunction {
172
+ if (!element) {
173
+ return;
174
+ }
175
+
176
+ const undoStack: UndoFunction[] = [];
177
+ const isActive = element === document.activeElement;
178
+ const tag = element.nodeName.toLowerCase();
179
+
180
+ /* eslint-disable-next-line default-case */
181
+ switch (tag) {
182
+ case 'button':
183
+ case 'input':
184
+ case 'select':
185
+ case 'textarea':
186
+ undoStack.push(setAttributeWithUndo(element, 'aria-disabled', 'true'));
187
+
188
+ if (isActive) {
189
+ undoStack.push(
190
+ addEventListenerOnceWithUndo(element, 'blur', () =>
191
+ undoStack.push(setAttributeWithUndo(element, 'disabled', 'disabled'))
192
+ )
193
+ );
194
+ } else {
195
+ undoStack.push(setAttributeWithUndo(element, 'disabled', 'disabled'));
196
+ }
197
+
198
+ if (tag === 'input' || tag === 'textarea') {
199
+ undoStack.push(addEventListenerWithUndo(element, 'click', disabledHandler));
200
+ undoStack.push(setAttributeWithUndo(element, 'readonly', 'readonly'));
201
+ } else if (tag === 'select') {
202
+ undoStack.push(
203
+ ...[].map.call(element.querySelectorAll('option'), option =>
204
+ setAttributeWithUndo(option, 'disabled', 'disabled')
205
+ )
206
+ );
207
+ }
208
+
209
+ break;
210
+ }
211
+
212
+ return bunchUndos(...undoStack);
213
+ }
214
+
215
+ /**
216
+ * Disables all inputtable descendants.
217
+ *
218
+ * @param {HTMLElement | undefined} element Container element to start looking for inputtable descendants.
219
+ * @param {boolean} observeSubtree `true` to applies to all future inputtable descendants, otherwise, `false`.
220
+ *
221
+ * @returns {function} A function, when called, will restore to previous state.
222
+ */
223
+ function disableInputElementsWithUndo(element: HTMLElement | undefined, observeSubtree = true): UndoFunction {
224
+ if (!element) {
225
+ return;
226
+ }
227
+
228
+ const undoStack: (() => void)[] = [].map.call(element.querySelectorAll('button, input, select, textarea'), element =>
229
+ disableElementWithUndo(element)
230
+ );
231
+
232
+ const tag = element.nodeName.toLowerCase();
233
+
234
+ // Only set tabindex="-1" on focusable element. Otherwise, we will make <div> focusable by mouse.
235
+ (tag === 'a' || tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea') &&
236
+ undoStack.push(setAttributeWithUndo(element, 'tabindex', '-1'));
237
+
238
+ if (observeSubtree) {
239
+ const observer = new MutationObserver(mutations =>
240
+ mutations.forEach(({ addedNodes }) =>
241
+ undoStack.push(...[].map.call(addedNodes, addedNode => disableInputElementsWithUndo(addedNode, false)))
242
+ )
243
+ );
244
+
245
+ observer.observe(element, { childList: true, subtree: true });
246
+
247
+ undoStack.push(() => observer.disconnect());
248
+ }
249
+
250
+ return bunchUndos(...undoStack);
251
+ }
252
+
253
+ /**
254
+ * Gets the value of an attribute from an element.
255
+ *
256
+ * @returns {false | string} The value of the attribute. `false` if the attribute was not set.
257
+ */
258
+ function getAttribute(element: HTMLElement, qualifiedName: string): false | string {
259
+ return !!element && element.hasAttribute(qualifiedName) && (element.getAttribute(qualifiedName) || '');
260
+ }
261
+
262
+ /**
263
+ * Sets or removes an attribute from an element.
264
+ *
265
+ * @param {HTMLElement} element - The element to set or remove attribute from.
266
+ * @param {string} qualifiedName - The name of the attribute.
267
+ * @param {false | string} value - The value of the attribute. When passing `false`, remove the attribute.
268
+ */
269
+ function setOrRemoveAttribute(element: HTMLElement | undefined, qualifiedName: string, value: false | string): void {
270
+ if (value === false) {
271
+ element?.removeAttribute(qualifiedName);
272
+ } else {
273
+ element?.setAttribute(qualifiedName, value);
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Sets or removes an attribute from an element with an undo function.
279
+ *
280
+ * @param {HTMLElement} element - The element to set or remove attribute from.
281
+ * @param {string} qualifiedName - The name of the attribute.
282
+ * @param {false | string} value - The value of the attribute. When passing `false`, remove the attribute.
283
+ *
284
+ * @returns {() => void} The undo function, when called, will undo all manipulations by restoring values recorded at the time of the function call.
285
+ */
286
+ function setOrRemoveAttributeWithUndo(
287
+ element: HTMLElement | undefined,
288
+ qualifiedName: string,
289
+ value: false | string
290
+ ): UndoFunction {
291
+ if (!element) {
292
+ return;
293
+ }
294
+
295
+ const prevValue = getAttribute(element, qualifiedName);
296
+
297
+ setOrRemoveAttribute(element, qualifiedName, value);
298
+
299
+ return () => setOrRemoveAttribute(element, qualifiedName, prevValue);
300
+ }
301
+
302
+ /**
303
+ * Finds the first ancestor that fulfill the predicate.
304
+ *
305
+ * @param {HTMLElement} element - The starting element. This element will not be checked against the predicate.
306
+ * @param {(ancestor: HTMLElement) => boolean} predicate - The predicate to fulfill.
307
+ *
308
+ * @returns {HTMLElement | undefined} The first ancestor that fulfill the predicate, otherwise, `undefined`.
309
+ */
310
+ function findAncestor(element: HTMLElement, predicate: (ancestor: HTMLElement) => boolean): HTMLElement | undefined {
311
+ let current = element;
312
+
313
+ while ((current = current.parentElement)) {
314
+ if (predicate.call(element, current)) {
315
+ return current;
316
+ }
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Indicates the action selected by performing a series of manipulations, with undo:
322
+ *
323
+ * - Accessibility: set `aria-pressed` to `true`
324
+ * - Applies `styleOptions.actionPerformedClassName`
325
+ *
326
+ * @param {HTMLElement[]} selectedActionElements - An array of elements that are representing the action and is selected.
327
+ * @param {string?} actionPerformedClassName - The name of the class to apply to all elements.
328
+ *
329
+ * @returns {() => void} The undo function, when called, will undo all manipulations by restoring values recorded at the time of the function call.
330
+ */
331
+ function indicateActionSelectionWithUndo(
332
+ selectedActionElements: HTMLElement[] | undefined,
333
+ actionPerformedClassName?: string
334
+ ): UndoFunction {
335
+ if (!selectedActionElements?.length) {
336
+ return;
337
+ }
338
+
339
+ // Verify all input elements are "ac-pushButton", could belongs to ActionSet or "card actions".
340
+ if (selectedActionElements.some(actionElement => !actionElement.classList.contains('ac-pushButton'))) {
341
+ console.warn(
342
+ 'botframework-webchat: Cannot mark selected action in the card, some elements are not an "ac-pushButton".'
343
+ );
344
+
345
+ return;
346
+ }
347
+
348
+ // A distinct set of action set containers which has selections, excluding containers without actions.
349
+ // Multiple submission in an Adaptive Card is still a vague area and TBD.
350
+ // We might want to disable the whole card, just buttons in same container, or do nothing (today).
351
+ const actionSetElements = new Set<HTMLElement>();
352
+
353
+ selectedActionElements.forEach(selectedActionElement => {
354
+ const actionSetElement = findAncestor(
355
+ selectedActionElement,
356
+ ancestor => ancestor.getAttribute('role') === 'menubar'
357
+ );
358
+
359
+ actionSetElement && actionSetElements.add(actionSetElement);
360
+ });
361
+
362
+ const undoStack: (() => void)[] = [];
363
+
364
+ actionSetElements.forEach(actionSetElement => {
365
+ // Remove "role" from every "ac-actionSet" container.
366
+ undoStack.push(setOrRemoveAttributeWithUndo(actionSetElement, 'role', false));
367
+
368
+ // Modify "role" of every actions in the container.
369
+ Array.from(actionSetElement.querySelectorAll('.ac-pushButton') as NodeListOf<HTMLElement>).forEach(
370
+ actionElement => {
371
+ if (selectedActionElements.includes(actionElement)) {
372
+ // Add "aria-pressed" and set "role" attribute to "button" (which is required by "aria-pressed").
373
+ undoStack.push(setOrRemoveAttributeWithUndo(actionElement, 'aria-pressed', 'true'));
374
+ undoStack.push(setOrRemoveAttributeWithUndo(actionElement, 'role', 'button'));
375
+
376
+ // Highlight actions by applying `styleOptions.actionPerformedClassName`.
377
+ actionPerformedClassName &&
378
+ undoStack.push(addPersistentClassWithUndo(actionElement, actionPerformedClassName));
379
+ } else {
380
+ // We removed "role=menubar" from the container, we must remove "role=menuitem" from unselected actions.
381
+ undoStack.push(setOrRemoveAttributeWithUndo(actionElement, 'role', false));
382
+ }
383
+ }
384
+ );
385
+ });
386
+
387
+ return bunchUndos(...undoStack);
388
+ }
389
+
390
+ /**
391
+ * Fixes accessibility issues from Adaptive Card, with undo.
392
+ *
393
+ * @returns {() => void} The undo function, when called, will undo all manipulations by restoring values recorded at the time of the function call.
394
+ */
395
+ function fixAccessibilityIssuesWithUndo(element: HTMLElement): UndoFunction {
396
+ if (!element) {
397
+ return;
398
+ }
399
+
400
+ // These hacks should be done in Adaptive Cards library instead.
401
+ // Related to #3949: All action buttons inside role="menubar" should be role="menuitem".
402
+ const undoStack: UndoFunction[] = Array.from(
403
+ element.querySelectorAll('.ac-actionSet[role="menubar"] [role="button"]') as NodeListOf<HTMLElement>
404
+ ).map(actionButton => setAttributeWithUndo(actionButton, 'role', 'menuitem'));
405
+
406
+ return () => undoStack.forEach(undo => undo?.());
407
+ }
408
+
409
+ function getFocusableElements(element: HTMLElement) {
410
+ return [].filter.call(
411
+ element.querySelectorAll(
412
+ [
413
+ 'a',
414
+ 'body',
415
+ 'button',
416
+ 'frame',
417
+ 'iframe',
418
+ 'img',
419
+ 'input',
420
+ 'isindex',
421
+ 'object',
422
+ 'select',
423
+ 'textarea',
424
+ '[tabindex]'
425
+ ].join(', ')
426
+ ) as NodeListOf<HTMLElement>,
427
+ element => {
428
+ const tabIndex = getTabIndex(element);
429
+
430
+ return typeof tabIndex === 'number' && tabIndex >= 0;
431
+ }
432
+ );
433
+ }
434
+
435
+ function restoreActiveElementIndex(element: HTMLElement, activeElementIndex: number) {
436
+ getFocusableElements(element)[+activeElementIndex]?.focus();
437
+ }
438
+
439
+ function saveActiveElementIndex(element: HTMLElement) {
440
+ return getFocusableElements(element).indexOf(document.activeElement);
441
+ }
442
+
443
+ function restoreInputValues(element: HTMLElement, inputValues: (boolean | string)[]) {
444
+ const inputs = element.querySelectorAll('input, select, textarea') as NodeListOf<
445
+ HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
446
+ >;
447
+
448
+ [].forEach.call(inputs, (input: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, index: number) => {
449
+ const value = inputValues[+index];
450
+
451
+ if (typeof value !== 'undefined') {
452
+ const { tagName, type } = input;
453
+
454
+ if (tagName === 'INPUT' && (type === 'checkbox' || type === 'radio')) {
455
+ if (typeof value === 'boolean') {
456
+ (input as HTMLInputElement).checked = value;
457
+ }
458
+ } else if (typeof value === 'string') {
459
+ input.value = value;
460
+ }
461
+ }
462
+ });
463
+ }
464
+
465
+ function saveInputValues(element: HTMLElement): (boolean | string)[] {
466
+ const inputs = element.querySelectorAll('input, select, textarea') as NodeListOf<
467
+ HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
468
+ >;
469
+
470
+ return [].map.call(inputs, (input: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement) => {
471
+ const { type } = input;
472
+
473
+ if (input.tagName === 'INPUT' && (type === 'checkbox' || type === 'radio')) {
474
+ return (input as HTMLInputElement).checked;
475
+ }
476
+
477
+ return input.value;
478
+ });
479
+ }
480
+
481
+ type AdaptiveCardRendererProps = {
482
+ actionPerformedClassName?: string;
483
+ adaptiveCard: AdaptiveCard;
484
+ disabled?: boolean;
485
+ tapAction?: DirectLineCardAction;
486
+ };
487
+
488
+ const AdaptiveCardRenderer: VFC<AdaptiveCardRendererProps> = ({
489
+ actionPerformedClassName,
490
+ adaptiveCard,
491
+ disabled: disabledFromProps,
492
+ tapAction
493
+ }) => {
494
+ const [{ adaptiveCardRenderer: adaptiveCardRendererStyleSet }] = useStyleSet();
495
+ const [{ GlobalSettings, HostConfig }] = useAdaptiveCardsPackage();
496
+ const [actionsPerformed, setActionsPerformed] = useState<AdaptiveCardAction[]>([]);
497
+ const [adaptiveCardsHostConfig] = useAdaptiveCardsHostConfig();
498
+ const [disabledFromComposer] = useDisabled();
499
+ const [errors, setErrors] = useState([]);
500
+ const [lastRender, setLastRender] = useState(0);
501
+ const activeElementIndexRef = useRef(-1);
502
+ const adaptiveCardElementRef = useRef<HTMLElement>();
503
+ const contentRef = useRef<HTMLDivElement>();
504
+ const inputValuesRef = useRef<(boolean | string)[]>([]);
505
+ const localize = useLocalizer();
506
+ const performCardAction = usePerformCardAction();
507
+ const renderMarkdownAsHTML = useRenderMarkdownAsHTML();
508
+ const scrollToEnd = useScrollToEnd();
509
+
510
+ const disabled = disabledFromComposer || disabledFromProps;
511
+
512
+ // TODO: [P2] #3199 We should consider using `adaptiveCard.selectAction` instead.
513
+ // The null check for "tapAction" is in "handleClickAndKeyPressForTapAction".
514
+ const handleClickAndKeyPress = useCallback(
515
+ (event: KeyboardEvent | MouseEvent): void => {
516
+ const { key, type } = event as KeyboardEvent;
517
+ const target = event.target as HTMLDivElement;
518
+
519
+ // Some items, e.g. tappable text, cannot be disabled thru DOM attributes
520
+ const { current } = contentRef;
521
+ const adaptiveCardRoot = current.querySelector('.ac-adaptiveCard[tabindex="0"]');
522
+
523
+ if (!adaptiveCardRoot) {
524
+ return console.warn(
525
+ 'botframework-webchat: No Adaptive Card root container can be found; the card is probably on an unsupported Adaptive Card version.'
526
+ );
527
+ }
528
+
529
+ // For "keypress" event, we only listen to ENTER and SPACEBAR key.
530
+ if (type === 'keypress') {
531
+ if (key !== 'Enter' && key !== ' ') {
532
+ return;
533
+ }
534
+
535
+ event.preventDefault();
536
+ }
537
+
538
+ // We will call performCardAction if either:
539
+ // 1. We are on the target, or
540
+ // 2. The event-dispatching element is not interactive
541
+ if (target !== adaptiveCardRoot) {
542
+ const tabIndex = getTabIndex(target);
543
+
544
+ // If the user is clicking on something that is already clickable, do not allow them to click the card.
545
+ // E.g. a hero card can be tappable, and image and buttons inside the hero card can also be tappable.
546
+ if (typeof tabIndex === 'number' && tabIndex >= 0) {
547
+ return;
548
+ }
549
+ }
550
+
551
+ performCardAction(tapAction);
552
+ scrollToEnd();
553
+ },
554
+ [contentRef, performCardAction, scrollToEnd, tapAction]
555
+ );
556
+
557
+ // Only listen to event if it is not disabled and have "tapAction" prop.
558
+ const handleClickAndKeyPressForTapAction = !disabled && tapAction ? handleClickAndKeyPress : undefined;
559
+
560
+ const addActionsPerformed = useCallback(
561
+ (action: AdaptiveCardAction): void =>
562
+ !~actionsPerformed.indexOf(action) && setActionsPerformed([...actionsPerformed, action]),
563
+ [actionsPerformed, setActionsPerformed]
564
+ );
565
+
566
+ const handleExecuteAction = useCallback(
567
+ (action: AdaptiveCardAction): void => {
568
+ // Some items, e.g. tappable image, cannot be disabled thru DOM attributes
569
+ if (disabled) {
570
+ return;
571
+ }
572
+
573
+ addActionsPerformed(action);
574
+
575
+ const actionTypeName = action.getJsonTypeName();
576
+ const { iconUrl: image, title } = action;
577
+
578
+ // We cannot use "instanceof" check here, because web devs may bring their own version of Adaptive Cards package.
579
+ // We need to check using "getJsonTypeName()" instead.
580
+ if (actionTypeName === 'Action.OpenUrl') {
581
+ const { url: value } = action as OpenUrlAction;
582
+
583
+ performCardAction({
584
+ image,
585
+ title,
586
+ type: 'openUrl',
587
+ value
588
+ });
589
+ } else if (actionTypeName === 'Action.Submit') {
590
+ const { data } = action as SubmitAction as {
591
+ data: string | BotFrameworkCardAction;
592
+ };
593
+
594
+ if (typeof data !== 'undefined') {
595
+ if (typeof data === 'string') {
596
+ performCardAction({
597
+ image,
598
+ title,
599
+ type: 'imBack',
600
+ value: data
601
+ });
602
+ } else if (data.__isBotFrameworkCardAction) {
603
+ performCardAction(data.cardAction);
604
+ } else {
605
+ performCardAction({
606
+ image,
607
+ title,
608
+ type: 'postBack',
609
+ value: data
610
+ });
611
+ }
612
+ }
613
+
614
+ scrollToEnd();
615
+ } else {
616
+ console.error(`Web Chat: received unknown action from Adaptive Cards`);
617
+ console.error(action);
618
+ }
619
+ },
620
+ [addActionsPerformed, disabled, performCardAction, scrollToEnd]
621
+ );
622
+
623
+ useLayoutEffect(() => {
624
+ const { current } = contentRef;
625
+
626
+ if (!current || !adaptiveCard) {
627
+ activeElementIndexRef.current = -1;
628
+ inputValuesRef.current = [];
629
+ }
630
+
631
+ // Currently, the only way to set the Markdown engine is to set it thru static member of AdaptiveCard class
632
+
633
+ // TODO: [P3] Checks if we could make the "renderMarkdownAsHTML" per card
634
+ // This could be limitations from Adaptive Cards package (not supported as of 1.2.5)
635
+ // Because there could be timing difference between .parse and .render, we could be using wrong Markdown engine
636
+
637
+ // "onProcessMarkdown" is a static function but we are trying to scope it to the current object instead.
638
+ // eslint-disable-next-line dot-notation
639
+ adaptiveCard.constructor['onProcessMarkdown'] = (text: string, result: IMarkdownProcessingResult) => {
640
+ if (renderMarkdownAsHTML) {
641
+ result.outputHtml = renderMarkdownAsHTML(text);
642
+ result.didProcess = true;
643
+ }
644
+ };
645
+
646
+ if (adaptiveCardsHostConfig) {
647
+ adaptiveCard.hostConfig = isPlainObject(adaptiveCardsHostConfig)
648
+ ? new HostConfig(adaptiveCardsHostConfig)
649
+ : adaptiveCardsHostConfig;
650
+ }
651
+
652
+ // For accessibility issue #1340, `tabindex="0"` must not be set for the root container if it is not interactive.
653
+ GlobalSettings.setTabIndexAtCardRoot = !!tapAction;
654
+
655
+ const { validationEvents } = adaptiveCard.validateProperties();
656
+
657
+ if (validationEvents.length) {
658
+ return setErrors(validationEvents.reduce((items, { message }) => [...items, new Error(message)], []));
659
+ }
660
+
661
+ let element: HTMLElement;
662
+
663
+ try {
664
+ element = adaptiveCard.render();
665
+ } catch (error) {
666
+ return setErrors([error]);
667
+ }
668
+
669
+ if (!element) {
670
+ return setErrors([new Error('Adaptive Card rendered as empty element')]);
671
+ }
672
+
673
+ // Clear errors on next render
674
+ setErrors([]);
675
+
676
+ restoreInputValues(element, inputValuesRef.current);
677
+
678
+ current.appendChild(element);
679
+ adaptiveCardElementRef.current = element;
680
+
681
+ // Focus can only be restored after the DOM is attached.
682
+ restoreActiveElementIndex(element, activeElementIndexRef.current);
683
+
684
+ setLastRender(Date.now());
685
+
686
+ return () => {
687
+ activeElementIndexRef.current = saveActiveElementIndex(element);
688
+ inputValuesRef.current = saveInputValues(element);
689
+
690
+ current.removeChild(adaptiveCardElementRef.current);
691
+
692
+ adaptiveCardElementRef.current = undefined;
693
+ };
694
+ }, [
695
+ adaptiveCard,
696
+ adaptiveCardsHostConfig,
697
+ contentRef,
698
+ GlobalSettings,
699
+ HostConfig,
700
+ renderMarkdownAsHTML,
701
+ setErrors,
702
+ tapAction
703
+ ]);
704
+
705
+ useEffect(() => {
706
+ // Set onExecuteAction without causing unnecessary re-render.
707
+ adaptiveCard.onExecuteAction = disabled ? undefined : handleExecuteAction;
708
+ }, [adaptiveCard, disabled, handleExecuteAction]);
709
+
710
+ useEffect(() => fixAccessibilityIssuesWithUndo(adaptiveCardElementRef.current), [adaptiveCardElementRef, lastRender]);
711
+
712
+ useEffect(() => {
713
+ // If the Adaptive Card get re-rendered, re-disable elements as needed.
714
+ if (disabled) {
715
+ return disableInputElementsWithUndo(adaptiveCardElementRef.current);
716
+ }
717
+ }, [adaptiveCardElementRef, disabled, lastRender]);
718
+
719
+ useEffect(() => {
720
+ // If the Adaptive Card changed, reset all actions performed.
721
+ setActionsPerformed([]);
722
+ }, [adaptiveCard]);
723
+
724
+ useEffect(
725
+ () =>
726
+ indicateActionSelectionWithUndo(
727
+ // Actions that do not have "renderedElement" means it is the Adaptive Card itself, such as "selectAction" (AC) or "tapAction" (rich cards).
728
+ // We do not need to mark the whole card as performed.
729
+ actionsPerformed.map(({ renderedElement }) => renderedElement).filter(renderedElement => renderedElement),
730
+ actionPerformedClassName
731
+ ),
732
+ [actionsPerformed, actionPerformedClassName, lastRender]
733
+ );
734
+
735
+ return errors.length ? (
736
+ node_env === 'development' && <ErrorBox error={errors[0]} type={localize('ADAPTIVE_CARD_ERROR_BOX_TITLE_RENDER')} />
737
+ ) : (
738
+ <div
739
+ className={classNames(adaptiveCardRendererStyleSet + '', 'webchat__adaptive-card-renderer')}
740
+ onClick={handleClickAndKeyPressForTapAction as unknown as MouseEventHandler<HTMLDivElement>}
741
+ onKeyPress={handleClickAndKeyPressForTapAction as unknown as KeyboardEventHandler<HTMLDivElement>}
742
+ ref={contentRef}
743
+ />
744
+ );
745
+ };
746
+
747
+ AdaptiveCardRenderer.defaultProps = {
748
+ actionPerformedClassName: '',
749
+ disabled: undefined,
750
+ tapAction: undefined
751
+ };
752
+
753
+ AdaptiveCardRenderer.propTypes = {
754
+ actionPerformedClassName: PropTypes.string,
755
+ adaptiveCard: PropTypes.any.isRequired,
756
+ disabled: PropTypes.bool,
757
+
758
+ // TypeScript class is not mappable to PropTypes.func
759
+ // @ts-ignore
760
+ tapAction: PropTypes.shape({
761
+ image: PropTypes.string,
762
+ title: PropTypes.string,
763
+ type: PropTypes.string.isRequired,
764
+ value: PropTypes.string
765
+ })
766
+ };
767
+
768
+ export default AdaptiveCardRenderer;