@mitodl/smoot-design 0.0.0-355a925

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 (247) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +35 -0
  3. package/dist/bundles/remoteTutorDrawer.es.js +38364 -0
  4. package/dist/bundles/remoteTutorDrawer.umd.js +207 -0
  5. package/dist/cjs/ai.d.ts +3 -0
  6. package/dist/cjs/ai.js +9 -0
  7. package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.d.ts +9 -0
  8. package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.js +87 -0
  9. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +56 -0
  10. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +276 -0
  11. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +16 -0
  12. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +284 -0
  13. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts +1 -0
  14. package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +241 -0
  15. package/dist/cjs/bundles/remoteTutorDrawer.d.ts +7 -0
  16. package/dist/cjs/bundles/remoteTutorDrawer.js +40 -0
  17. package/dist/cjs/components/AiChat/AiChat.d.ts +5 -0
  18. package/dist/cjs/components/AiChat/AiChat.js +226 -0
  19. package/dist/cjs/components/AiChat/AiChat.stories.d.ts +21 -0
  20. package/dist/cjs/components/AiChat/AiChat.stories.js +243 -0
  21. package/dist/cjs/components/AiChat/AiChat.test.d.ts +1 -0
  22. package/dist/cjs/components/AiChat/AiChat.test.js +211 -0
  23. package/dist/cjs/components/AiChat/AiChatContext.d.ts +22 -0
  24. package/dist/cjs/components/AiChat/AiChatContext.js +105 -0
  25. package/dist/cjs/components/AiChat/AiChatContext.stories.d.ts +14 -0
  26. package/dist/cjs/components/AiChat/AiChatContext.stories.js +75 -0
  27. package/dist/cjs/components/AiChat/ChatTitle.d.ts +8 -0
  28. package/dist/cjs/components/AiChat/ChatTitle.js +43 -0
  29. package/dist/cjs/components/AiChat/EntryScreen.d.ts +11 -0
  30. package/dist/cjs/components/AiChat/EntryScreen.js +123 -0
  31. package/dist/cjs/components/AiChat/TimLogo.d.ts +5 -0
  32. package/dist/cjs/components/AiChat/TimLogo.js +15 -0
  33. package/dist/cjs/components/AiChat/test-utils/api.d.ts +2 -0
  34. package/dist/cjs/components/AiChat/test-utils/api.js +125 -0
  35. package/dist/cjs/components/AiChat/types.d.ts +86 -0
  36. package/dist/cjs/components/AiChat/types.js +3 -0
  37. package/dist/cjs/components/Alert/Alert.d.ts +15 -0
  38. package/dist/cjs/components/Alert/Alert.js +62 -0
  39. package/dist/cjs/components/Alert/Alert.stories.d.ts +8 -0
  40. package/dist/cjs/components/Alert/Alert.stories.js +53 -0
  41. package/dist/cjs/components/Button/ActionButton.d.ts +30 -0
  42. package/dist/cjs/components/Button/ActionButton.js +73 -0
  43. package/dist/cjs/components/Button/ActionButton.stories.d.ts +15 -0
  44. package/dist/cjs/components/Button/ActionButton.stories.js +113 -0
  45. package/dist/cjs/components/Button/Button.d.ts +54 -0
  46. package/dist/cjs/components/Button/Button.js +240 -0
  47. package/dist/cjs/components/Button/Button.stories.d.ts +17 -0
  48. package/dist/cjs/components/Button/Button.stories.js +135 -0
  49. package/dist/cjs/components/Button/Button.test.d.ts +1 -0
  50. package/dist/cjs/components/Button/Button.test.js +46 -0
  51. package/dist/cjs/components/ImageAdapter/ImageAdapter.d.ts +23 -0
  52. package/dist/cjs/components/ImageAdapter/ImageAdapter.js +30 -0
  53. package/dist/cjs/components/Input/Input.d.ts +116 -0
  54. package/dist/cjs/components/Input/Input.js +237 -0
  55. package/dist/cjs/components/Input/Input.stories.d.ts +19 -0
  56. package/dist/cjs/components/Input/Input.stories.js +135 -0
  57. package/dist/cjs/components/Input/Input.test.d.ts +1 -0
  58. package/dist/cjs/components/Input/Input.test.js +32 -0
  59. package/dist/cjs/components/LinkAdapter/LinkAdapter.d.ts +23 -0
  60. package/dist/cjs/components/LinkAdapter/LinkAdapter.js +34 -0
  61. package/dist/cjs/components/ScrollSnap/ScrollSnap.d.ts +19 -0
  62. package/dist/cjs/components/ScrollSnap/ScrollSnap.js +59 -0
  63. package/dist/cjs/components/ScrollSnap/ScrollSnap.stories.d.ts +6 -0
  64. package/dist/cjs/components/ScrollSnap/ScrollSnap.stories.js +43 -0
  65. package/dist/cjs/components/ScrollSnap/useScrollSnap.d.ts +6 -0
  66. package/dist/cjs/components/ScrollSnap/useScrollSnap.js +36 -0
  67. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.d.ts +25 -0
  68. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.js +43 -0
  69. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.stories.d.ts +6 -0
  70. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.stories.js +44 -0
  71. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.test.d.ts +1 -0
  72. package/dist/cjs/components/SrAnnouncer/SrAnnouncer.test.js +62 -0
  73. package/dist/cjs/components/TabButtons/TabButtonList.d.ts +25 -0
  74. package/dist/cjs/components/TabButtons/TabButtonList.js +97 -0
  75. package/dist/cjs/components/TabButtons/TabButtonList.stories.d.ts +24 -0
  76. package/dist/cjs/components/TabButtons/TabButtonList.stories.js +139 -0
  77. package/dist/cjs/components/TextField/TextField.d.ts +29 -0
  78. package/dist/cjs/components/TextField/TextField.js +33 -0
  79. package/dist/cjs/components/TextField/TextField.stories.d.ts +10 -0
  80. package/dist/cjs/components/TextField/TextField.stories.js +136 -0
  81. package/dist/cjs/components/TextField/TextField.test.d.ts +1 -0
  82. package/dist/cjs/components/TextField/TextField.test.js +77 -0
  83. package/dist/cjs/components/ThemeProvider/ThemeProvider.d.ts +21 -0
  84. package/dist/cjs/components/ThemeProvider/ThemeProvider.js +86 -0
  85. package/dist/cjs/components/ThemeProvider/ThemeProvider.stories.d.ts +63 -0
  86. package/dist/cjs/components/ThemeProvider/ThemeProvider.stories.js +102 -0
  87. package/dist/cjs/components/ThemeProvider/Typography.stories.d.ts +39 -0
  88. package/dist/cjs/components/ThemeProvider/Typography.stories.js +65 -0
  89. package/dist/cjs/components/ThemeProvider/breakpoints.d.ts +4 -0
  90. package/dist/cjs/components/ThemeProvider/breakpoints.js +19 -0
  91. package/dist/cjs/components/ThemeProvider/buttons.d.ts +7 -0
  92. package/dist/cjs/components/ThemeProvider/buttons.js +20 -0
  93. package/dist/cjs/components/ThemeProvider/chips.d.ts +3 -0
  94. package/dist/cjs/components/ThemeProvider/chips.js +154 -0
  95. package/dist/cjs/components/ThemeProvider/colors.d.ts +32 -0
  96. package/dist/cjs/components/ThemeProvider/colors.js +35 -0
  97. package/dist/cjs/components/ThemeProvider/typography.d.ts +18 -0
  98. package/dist/cjs/components/ThemeProvider/typography.js +173 -0
  99. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.d.ts +24 -0
  100. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.js +33 -0
  101. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.stories.d.ts +6 -0
  102. package/dist/cjs/components/VisuallyHidden/VisuallyHidden.stories.js +13 -0
  103. package/dist/cjs/components/internal/FormHelpers/FormHelpers.d.ts +39 -0
  104. package/dist/cjs/components/internal/FormHelpers/FormHelpers.js +78 -0
  105. package/dist/cjs/components/internal/FormHelpers/FormHelpers.test.d.ts +1 -0
  106. package/dist/cjs/components/internal/FormHelpers/FormHelpers.test.js +93 -0
  107. package/dist/cjs/index.d.ts +16 -0
  108. package/dist/cjs/index.js +30 -0
  109. package/dist/cjs/jest-setup.d.ts +1 -0
  110. package/dist/cjs/jest-setup.js +18 -0
  111. package/dist/cjs/jsdom-extended.d.ts +6 -0
  112. package/dist/cjs/jsdom-extended.js +14 -0
  113. package/dist/cjs/story-utils/index.d.ts +6 -0
  114. package/dist/cjs/story-utils/index.js +17 -0
  115. package/dist/cjs/utils/composeRefs.d.ts +7 -0
  116. package/dist/cjs/utils/composeRefs.js +20 -0
  117. package/dist/cjs/utils/composeRefs.test.d.ts +1 -0
  118. package/dist/cjs/utils/composeRefs.test.js +19 -0
  119. package/dist/cjs/utils/useDevCheckStable.d.ts +8 -0
  120. package/dist/cjs/utils/useDevCheckStable.js +29 -0
  121. package/dist/cjs/utils/useInterval.d.ts +7 -0
  122. package/dist/cjs/utils/useInterval.js +25 -0
  123. package/dist/esm/ai.d.ts +3 -0
  124. package/dist/esm/ai.js +2 -0
  125. package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.d.ts +9 -0
  126. package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.js +83 -0
  127. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +56 -0
  128. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +273 -0
  129. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts +16 -0
  130. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +281 -0
  131. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts +1 -0
  132. package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.js +239 -0
  133. package/dist/esm/bundles/remoteTutorDrawer.d.ts +7 -0
  134. package/dist/esm/bundles/remoteTutorDrawer.js +37 -0
  135. package/dist/esm/components/AiChat/AiChat.d.ts +5 -0
  136. package/dist/esm/components/AiChat/AiChat.js +222 -0
  137. package/dist/esm/components/AiChat/AiChat.stories.d.ts +21 -0
  138. package/dist/esm/components/AiChat/AiChat.stories.js +240 -0
  139. package/dist/esm/components/AiChat/AiChat.test.d.ts +1 -0
  140. package/dist/esm/components/AiChat/AiChat.test.js +209 -0
  141. package/dist/esm/components/AiChat/AiChatContext.d.ts +22 -0
  142. package/dist/esm/components/AiChat/AiChatContext.js +101 -0
  143. package/dist/esm/components/AiChat/AiChatContext.stories.d.ts +14 -0
  144. package/dist/esm/components/AiChat/AiChatContext.stories.js +72 -0
  145. package/dist/esm/components/AiChat/ChatTitle.d.ts +8 -0
  146. package/dist/esm/components/AiChat/ChatTitle.js +40 -0
  147. package/dist/esm/components/AiChat/EntryScreen.d.ts +11 -0
  148. package/dist/esm/components/AiChat/EntryScreen.js +120 -0
  149. package/dist/esm/components/AiChat/TimLogo.d.ts +5 -0
  150. package/dist/esm/components/AiChat/TimLogo.js +13 -0
  151. package/dist/esm/components/AiChat/test-utils/api.d.ts +2 -0
  152. package/dist/esm/components/AiChat/test-utils/api.js +122 -0
  153. package/dist/esm/components/AiChat/types.d.ts +86 -0
  154. package/dist/esm/components/AiChat/types.js +2 -0
  155. package/dist/esm/components/Alert/Alert.d.ts +15 -0
  156. package/dist/esm/components/Alert/Alert.js +59 -0
  157. package/dist/esm/components/Alert/Alert.stories.d.ts +8 -0
  158. package/dist/esm/components/Alert/Alert.stories.js +50 -0
  159. package/dist/esm/components/Button/ActionButton.d.ts +30 -0
  160. package/dist/esm/components/Button/ActionButton.js +68 -0
  161. package/dist/esm/components/Button/ActionButton.stories.d.ts +15 -0
  162. package/dist/esm/components/Button/ActionButton.stories.js +110 -0
  163. package/dist/esm/components/Button/Button.d.ts +54 -0
  164. package/dist/esm/components/Button/Button.js +232 -0
  165. package/dist/esm/components/Button/Button.stories.d.ts +17 -0
  166. package/dist/esm/components/Button/Button.stories.js +132 -0
  167. package/dist/esm/components/Button/Button.test.d.ts +1 -0
  168. package/dist/esm/components/Button/Button.test.js +44 -0
  169. package/dist/esm/components/ImageAdapter/ImageAdapter.d.ts +23 -0
  170. package/dist/esm/components/ImageAdapter/ImageAdapter.js +27 -0
  171. package/dist/esm/components/Input/Input.d.ts +116 -0
  172. package/dist/esm/components/Input/Input.js +232 -0
  173. package/dist/esm/components/Input/Input.stories.d.ts +19 -0
  174. package/dist/esm/components/Input/Input.stories.js +132 -0
  175. package/dist/esm/components/Input/Input.test.d.ts +1 -0
  176. package/dist/esm/components/Input/Input.test.js +30 -0
  177. package/dist/esm/components/LinkAdapter/LinkAdapter.d.ts +23 -0
  178. package/dist/esm/components/LinkAdapter/LinkAdapter.js +31 -0
  179. package/dist/esm/components/ScrollSnap/ScrollSnap.d.ts +19 -0
  180. package/dist/esm/components/ScrollSnap/ScrollSnap.js +56 -0
  181. package/dist/esm/components/ScrollSnap/ScrollSnap.stories.d.ts +6 -0
  182. package/dist/esm/components/ScrollSnap/ScrollSnap.stories.js +40 -0
  183. package/dist/esm/components/ScrollSnap/useScrollSnap.d.ts +6 -0
  184. package/dist/esm/components/ScrollSnap/useScrollSnap.js +33 -0
  185. package/dist/esm/components/SrAnnouncer/SrAnnouncer.d.ts +25 -0
  186. package/dist/esm/components/SrAnnouncer/SrAnnouncer.js +40 -0
  187. package/dist/esm/components/SrAnnouncer/SrAnnouncer.stories.d.ts +6 -0
  188. package/dist/esm/components/SrAnnouncer/SrAnnouncer.stories.js +41 -0
  189. package/dist/esm/components/SrAnnouncer/SrAnnouncer.test.d.ts +1 -0
  190. package/dist/esm/components/SrAnnouncer/SrAnnouncer.test.js +60 -0
  191. package/dist/esm/components/TabButtons/TabButtonList.d.ts +25 -0
  192. package/dist/esm/components/TabButtons/TabButtonList.js +92 -0
  193. package/dist/esm/components/TabButtons/TabButtonList.stories.d.ts +24 -0
  194. package/dist/esm/components/TabButtons/TabButtonList.stories.js +136 -0
  195. package/dist/esm/components/TextField/TextField.d.ts +29 -0
  196. package/dist/esm/components/TextField/TextField.js +30 -0
  197. package/dist/esm/components/TextField/TextField.stories.d.ts +10 -0
  198. package/dist/esm/components/TextField/TextField.stories.js +133 -0
  199. package/dist/esm/components/TextField/TextField.test.d.ts +1 -0
  200. package/dist/esm/components/TextField/TextField.test.js +75 -0
  201. package/dist/esm/components/ThemeProvider/ThemeProvider.d.ts +21 -0
  202. package/dist/esm/components/ThemeProvider/ThemeProvider.js +82 -0
  203. package/dist/esm/components/ThemeProvider/ThemeProvider.stories.d.ts +63 -0
  204. package/dist/esm/components/ThemeProvider/ThemeProvider.stories.js +99 -0
  205. package/dist/esm/components/ThemeProvider/Typography.stories.d.ts +39 -0
  206. package/dist/esm/components/ThemeProvider/Typography.stories.js +62 -0
  207. package/dist/esm/components/ThemeProvider/breakpoints.d.ts +4 -0
  208. package/dist/esm/components/ThemeProvider/breakpoints.js +15 -0
  209. package/dist/esm/components/ThemeProvider/buttons.d.ts +7 -0
  210. package/dist/esm/components/ThemeProvider/buttons.js +17 -0
  211. package/dist/esm/components/ThemeProvider/chips.d.ts +3 -0
  212. package/dist/esm/components/ThemeProvider/chips.js +151 -0
  213. package/dist/esm/components/ThemeProvider/colors.d.ts +32 -0
  214. package/dist/esm/components/ThemeProvider/colors.js +32 -0
  215. package/dist/esm/components/ThemeProvider/typography.d.ts +18 -0
  216. package/dist/esm/components/ThemeProvider/typography.js +167 -0
  217. package/dist/esm/components/VisuallyHidden/VisuallyHidden.d.ts +24 -0
  218. package/dist/esm/components/VisuallyHidden/VisuallyHidden.js +30 -0
  219. package/dist/esm/components/VisuallyHidden/VisuallyHidden.stories.d.ts +6 -0
  220. package/dist/esm/components/VisuallyHidden/VisuallyHidden.stories.js +10 -0
  221. package/dist/esm/components/internal/FormHelpers/FormHelpers.d.ts +39 -0
  222. package/dist/esm/components/internal/FormHelpers/FormHelpers.js +73 -0
  223. package/dist/esm/components/internal/FormHelpers/FormHelpers.test.d.ts +1 -0
  224. package/dist/esm/components/internal/FormHelpers/FormHelpers.test.js +91 -0
  225. package/dist/esm/index.d.ts +16 -0
  226. package/dist/esm/index.js +11 -0
  227. package/dist/esm/jest-setup.d.ts +1 -0
  228. package/dist/esm/jest-setup.js +16 -0
  229. package/dist/esm/jsdom-extended.d.ts +6 -0
  230. package/dist/esm/jsdom-extended.js +12 -0
  231. package/dist/esm/story-utils/index.d.ts +6 -0
  232. package/dist/esm/story-utils/index.js +13 -0
  233. package/dist/esm/utils/composeRefs.d.ts +7 -0
  234. package/dist/esm/utils/composeRefs.js +17 -0
  235. package/dist/esm/utils/composeRefs.test.d.ts +1 -0
  236. package/dist/esm/utils/composeRefs.test.js +17 -0
  237. package/dist/esm/utils/useDevCheckStable.d.ts +8 -0
  238. package/dist/esm/utils/useDevCheckStable.js +26 -0
  239. package/dist/esm/utils/useInterval.d.ts +7 -0
  240. package/dist/esm/utils/useInterval.js +22 -0
  241. package/dist/tsconfig.tsbuildinfo +1 -0
  242. package/dist/type-augmentation/TypescriptDocs.mdx +17 -0
  243. package/dist/type-augmentation/imports.d.ts +3 -0
  244. package/dist/type-augmentation/index.d.ts +3 -0
  245. package/dist/type-augmentation/theme.d.ts +105 -0
  246. package/dist/type-augmentation/typography.d.ts +54 -0
  247. package/package.json +155 -0
@@ -0,0 +1,239 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { act, render, screen } from "@testing-library/react";
11
+ import user from "@testing-library/user-event";
12
+ import { RemoteTutorDrawer } from "./RemoteTutorDrawer";
13
+ import { ThemeProvider } from "../../components/ThemeProvider/ThemeProvider";
14
+ import * as React from "react";
15
+ import { http, HttpResponse } from "msw";
16
+ import { setupServer } from "msw/node";
17
+ const TEST_API_STREAMING = "http://localhost:4567/test";
18
+ const CONTENT_FILE_URL = "http://localhost:4567/api/v1/contentfiles/1";
19
+ const CONTENT_RESPONSE = {
20
+ count: 1,
21
+ next: null,
22
+ previous: null,
23
+ results: [
24
+ {
25
+ id: 1,
26
+ summary: "This is a test summary",
27
+ flashcards: [
28
+ {
29
+ question: "Test question 1?",
30
+ answer: "Test answer 1",
31
+ },
32
+ {
33
+ question: "Test question 2?",
34
+ answer: "Test answer 2",
35
+ },
36
+ {
37
+ question: "Test question 3?",
38
+ answer: "Test answer 3",
39
+ },
40
+ ],
41
+ },
42
+ ],
43
+ };
44
+ class MockResizeObserver {
45
+ constructor() {
46
+ this.observe = jest.fn();
47
+ this.unobserve = jest.fn();
48
+ this.disconnect = jest.fn();
49
+ }
50
+ }
51
+ global.ResizeObserver = MockResizeObserver;
52
+ describe("RemoteTutorDrawer", () => {
53
+ const server = setupServer(http.post(TEST_API_STREAMING, () => __awaiter(void 0, void 0, void 0, function* () {
54
+ return HttpResponse.text("AI Response");
55
+ })), http.get(CONTENT_FILE_URL, () => {
56
+ return HttpResponse.json(CONTENT_RESPONSE);
57
+ }));
58
+ beforeEach(() => {
59
+ jest.resetAllMocks();
60
+ });
61
+ afterEach(() => {
62
+ server.resetHandlers();
63
+ });
64
+ afterAll(() => server.close());
65
+ const setup = (message) => __awaiter(void 0, void 0, void 0, function* () {
66
+ server.listen();
67
+ render(React.createElement(RemoteTutorDrawer, { "data-testid": "remote-tutor-drawer", messageOrigin: "http://localhost:6006", target: "ai-chat" }), { wrapper: ThemeProvider });
68
+ yield screen.findByTestId("remote-tutor-drawer-waiting");
69
+ const event = new MessageEvent("message", {
70
+ data: message,
71
+ origin: "http://localhost:6006",
72
+ });
73
+ yield act(() => __awaiter(void 0, void 0, void 0, function* () {
74
+ window.dispatchEvent(event);
75
+ yield new Promise((resolve) => setTimeout(resolve, 100));
76
+ }));
77
+ });
78
+ test("Problem drawer opens showing title", () => __awaiter(void 0, void 0, void 0, function* () {
79
+ yield setup({
80
+ type: "smoot-design::tutor-drawer-open",
81
+ payload: {
82
+ blockType: "problem",
83
+ target: "ai-chat",
84
+ title: "Drawer Title",
85
+ chat: {
86
+ apiUrl: TEST_API_STREAMING,
87
+ },
88
+ },
89
+ });
90
+ screen.getByRole("heading", { name: "Drawer Title" });
91
+ }));
92
+ test("Video drawer opens showing chat entry screen and tabs", () => __awaiter(void 0, void 0, void 0, function* () {
93
+ yield setup({
94
+ type: "smoot-design::tutor-drawer-open",
95
+ payload: {
96
+ blockType: "video",
97
+ target: "ai-chat",
98
+ chat: {
99
+ entryScreenTitle: "Entry screen title",
100
+ apiUrl: TEST_API_STREAMING,
101
+ conversationStarters: [
102
+ { content: "Prompt 1" },
103
+ { content: "Prompt 2" },
104
+ { content: "Prompt 3" },
105
+ ],
106
+ },
107
+ summary: {
108
+ apiUrl: CONTENT_FILE_URL,
109
+ },
110
+ },
111
+ });
112
+ screen.getByText("Entry screen title");
113
+ screen.getByRole("tab", { name: "Chat" });
114
+ screen.getByRole("tab", { name: "Flashcards" });
115
+ screen.getByRole("tab", { name: "Summary" });
116
+ screen.getByRole("button", { name: "Prompt 1" });
117
+ screen.getByRole("button", { name: "Prompt 2" });
118
+ screen.getByRole("button", { name: "Prompt 3" });
119
+ }));
120
+ test("Video drawer chat entry screen selects starters from flashcards", () => __awaiter(void 0, void 0, void 0, function* () {
121
+ yield setup({
122
+ type: "smoot-design::tutor-drawer-open",
123
+ payload: {
124
+ blockType: "video",
125
+ target: "ai-chat",
126
+ chat: {
127
+ entryScreenTitle: "Entry screen title",
128
+ apiUrl: TEST_API_STREAMING,
129
+ },
130
+ summary: {
131
+ apiUrl: CONTENT_FILE_URL,
132
+ },
133
+ },
134
+ });
135
+ screen.getByRole("button", { name: "Test question 1?" });
136
+ screen.getByRole("button", { name: "Test question 2?" });
137
+ screen.getByRole("button", { name: "Test question 3?" });
138
+ }));
139
+ test("Video drawer chat entry screen shows default starters where no flashcards are available", server.boundary(() => __awaiter(void 0, void 0, void 0, function* () {
140
+ const contentResponse = JSON.parse(JSON.stringify(CONTENT_RESPONSE));
141
+ contentResponse.results[0].flashcards = null;
142
+ server.use(http.get(CONTENT_FILE_URL, () => {
143
+ return HttpResponse.json(contentResponse);
144
+ }));
145
+ yield setup({
146
+ type: "smoot-design::tutor-drawer-open",
147
+ payload: {
148
+ blockType: "video",
149
+ target: "ai-chat",
150
+ chat: {
151
+ entryScreenTitle: "Entry screen title",
152
+ apiUrl: TEST_API_STREAMING,
153
+ },
154
+ summary: {
155
+ apiUrl: CONTENT_FILE_URL,
156
+ },
157
+ },
158
+ });
159
+ screen.getByRole("button", {
160
+ name: "What are the most important concepts introduced in the video?",
161
+ });
162
+ screen.getByRole("button", {
163
+ name: "What examples are used to illustrate concepts covered in the video?",
164
+ });
165
+ screen.getByRole("button", {
166
+ name: "What are the key terms introduced in this video?",
167
+ });
168
+ })));
169
+ test("Video drawer chat entry screen displays default title", () => __awaiter(void 0, void 0, void 0, function* () {
170
+ yield setup({
171
+ type: "smoot-design::tutor-drawer-open",
172
+ payload: {
173
+ blockType: "video",
174
+ target: "ai-chat",
175
+ chat: {
176
+ apiUrl: TEST_API_STREAMING,
177
+ },
178
+ summary: {
179
+ apiUrl: CONTENT_FILE_URL,
180
+ },
181
+ },
182
+ });
183
+ screen.getByText("What do you want to know about this video?");
184
+ }));
185
+ test("Flashcard shows content and can be click navigated", server.boundary(() => __awaiter(void 0, void 0, void 0, function* () {
186
+ yield setup({
187
+ type: "smoot-design::tutor-drawer-open",
188
+ payload: {
189
+ blockType: "video",
190
+ target: "ai-chat",
191
+ chat: {
192
+ apiUrl: TEST_API_STREAMING,
193
+ },
194
+ summary: {
195
+ apiUrl: CONTENT_FILE_URL,
196
+ },
197
+ },
198
+ });
199
+ yield user.click(screen.getByRole("tab", { name: "Flashcards" }));
200
+ yield user.click(screen.getByText("Q: Test question 1?"));
201
+ screen.getByText("Answer: Test answer 1");
202
+ yield user.click(screen.getByRole("button", { name: "Next card" }));
203
+ yield user.click(screen.getByText("Q: Test question 2?"));
204
+ screen.getByText("Answer: Test answer 2");
205
+ yield user.click(screen.getByRole("button", { name: "Previous card" }));
206
+ screen.getByText("Q: Test question 1?");
207
+ })));
208
+ test("Flashcard shows content and can be keyboard navigated and cycles", server.boundary(() => __awaiter(void 0, void 0, void 0, function* () {
209
+ yield setup({
210
+ type: "smoot-design::tutor-drawer-open",
211
+ payload: {
212
+ blockType: "video",
213
+ target: "ai-chat",
214
+ chat: {
215
+ apiUrl: TEST_API_STREAMING,
216
+ },
217
+ summary: {
218
+ apiUrl: CONTENT_FILE_URL,
219
+ },
220
+ },
221
+ });
222
+ yield user.click(screen.getByRole("tab", { name: "Flashcards" }));
223
+ screen.getByText("Q: Test question 1?");
224
+ yield user.keyboard("{enter}");
225
+ screen.getByText("Answer: Test answer 1");
226
+ yield user.keyboard("{arrowright}");
227
+ screen.getByText("Q: Test question 2?");
228
+ yield user.keyboard("{enter}");
229
+ screen.getByText("Answer: Test answer 2");
230
+ yield user.keyboard("{arrowleft}");
231
+ screen.getByText("Q: Test question 1?");
232
+ yield user.keyboard("{arrowleft}");
233
+ screen.getByText("Q: Test question 3?");
234
+ yield user.keyboard("{arrowright}");
235
+ yield user.keyboard("{arrowright}");
236
+ yield user.keyboard("{arrowright}");
237
+ screen.getByText("Q: Test question 3?");
238
+ })));
239
+ });
@@ -0,0 +1,7 @@
1
+ import type { RemoteTutorDrawerProps } from "./RemoteTutorDrawer/RemoteTutorDrawer";
2
+ /**
3
+ * Renders the RemoteTutorDrawer in an shadow DOM in order to isolate the drawer
4
+ * styles from external stylesheets.
5
+ */
6
+ declare const init: (opts: RemoteTutorDrawerProps) => void;
7
+ export { init };
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { RemoteTutorDrawer } from "./RemoteTutorDrawer/RemoteTutorDrawer";
4
+ import { ThemeProvider, createTheme, } from "../components/ThemeProvider/ThemeProvider";
5
+ import { CacheProvider } from "@emotion/react";
6
+ import createCache from "@emotion/cache";
7
+ /**
8
+ * Renders the RemoteTutorDrawer in an shadow DOM in order to isolate the drawer
9
+ * styles from external stylesheets.
10
+ */
11
+ const init = (opts) => {
12
+ const container = document.createElement("div");
13
+ document.body.appendChild(container);
14
+ const shadowContainer = container.attachShadow({ mode: "open" });
15
+ const shadowRootEl = document.createElement("div");
16
+ shadowRootEl.id = "smoot-chat-drawer-root";
17
+ shadowContainer.append(shadowRootEl);
18
+ // See https://mui.com/material-ui/customization/shadow-dom/
19
+ // Ensure style tags are rendered in shadow root
20
+ const cache = createCache({
21
+ key: "css",
22
+ prepend: true,
23
+ container: shadowContainer,
24
+ });
25
+ const theme = createTheme({
26
+ components: {
27
+ // Ensure modals, etc, are rendered in shadow root
28
+ MuiPopover: { defaultProps: { container: shadowRootEl } },
29
+ MuiPopper: { defaultProps: { container: shadowRootEl } },
30
+ MuiModal: { defaultProps: { container: shadowRootEl } },
31
+ },
32
+ });
33
+ createRoot(shadowRootEl).render(React.createElement(CacheProvider, { value: cache },
34
+ React.createElement(ThemeProvider, { theme: theme },
35
+ React.createElement(RemoteTutorDrawer, Object.assign({}, opts)))));
36
+ };
37
+ export { init };
@@ -0,0 +1,5 @@
1
+ import type { FC } from "react";
2
+ import type { AiChatDisplayProps, AiChatProps } from "./types";
3
+ declare const AiChatDisplay: FC<AiChatDisplayProps>;
4
+ declare const AiChat: FC<AiChatProps>;
5
+ export { AiChatDisplay, AiChat };
@@ -0,0 +1,222 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import * as React from "react";
13
+ import { useEffect, useRef, useState } from "react";
14
+ import styled from "@emotion/styled";
15
+ import Typography from "@mui/material/Typography";
16
+ import classNames from "classnames";
17
+ import { RiSendPlaneFill, RiStopFill, RiMoreFill } from "@remixicon/react";
18
+ import { Input, AdornmentButton } from "../Input/Input";
19
+ import Markdown from "react-markdown";
20
+ import { ScrollSnap } from "../ScrollSnap/ScrollSnap";
21
+ import { SrAnnouncer } from "../SrAnnouncer/SrAnnouncer";
22
+ import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden";
23
+ import { Alert } from "../Alert/Alert";
24
+ import { ChatTitle } from "./ChatTitle";
25
+ import { AiChatProvider, useAiChat } from "./AiChatContext";
26
+ import { useScrollSnap } from "../ScrollSnap/useScrollSnap";
27
+ import { EntryScreen } from "./EntryScreen";
28
+ const classes = {
29
+ root: "MitAiChat--root",
30
+ title: "MitAiChat--title",
31
+ entryScreenContainer: "MitAiChat--entryScreenContainer",
32
+ conversationStarter: "MitAiChat--conversationStarter",
33
+ chatScreenContainer: "MitAiChat--chatScreenContainer",
34
+ messagesContainer: "MitAiChat--messagesContainer",
35
+ messageRow: "MitAiChat--messageRow",
36
+ messageRowUser: "MitAiChat--messageRowUser",
37
+ messageRowAssistant: "MitAiChat--messageRowAssistant",
38
+ message: "MitAiChat--message",
39
+ input: "MitAiChat--input",
40
+ bottomSection: "MitAiChat--bottomSection",
41
+ };
42
+ const Container = styled.div();
43
+ const ChatScreen = styled.div(({ externalScroll, theme }) => (Object.assign({ padding: "16px 32px 0", [theme.breakpoints.down("md")]: {
44
+ padding: "16px 16px 0",
45
+ }, boxSizing: "border-box", background: "white", position: "absolute", bottom: 0, top: 0, left: 0, right: 0, zIndex: 1 }, (externalScroll && {
46
+ padding: "0 32px",
47
+ [theme.breakpoints.down("md")]: {
48
+ padding: "0 16px",
49
+ },
50
+ }))));
51
+ const ChatContainer = styled.div(({ externalScroll }) => ({
52
+ width: "100%",
53
+ height: externalScroll ? "auto" : "100%",
54
+ minHeight: externalScroll ? "100%" : "auto",
55
+ display: "flex",
56
+ flexDirection: "column",
57
+ }));
58
+ const MessagesContainer = styled(ScrollSnap)(({ externalScroll }) => ({
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ flex: 1,
62
+ padding: "14px 0",
63
+ overflow: externalScroll ? "visible" : "auto",
64
+ gap: "16px",
65
+ [`> .${classes.messageRowAssistant}:first-child`]: {
66
+ marginTop: "16px",
67
+ },
68
+ }));
69
+ const MessageRow = styled.div({
70
+ display: "flex",
71
+ width: "100%",
72
+ gap: "10px",
73
+ [`&.${classes.messageRowUser}`]: {
74
+ flexDirection: "row-reverse",
75
+ },
76
+ "> *": {
77
+ minWidth: 0,
78
+ },
79
+ position: "relative",
80
+ });
81
+ const Message = styled.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white }, theme.typography.body2), { "p:first-of-type": {
82
+ marginTop: 0,
83
+ }, "p:last-of-type": {
84
+ marginBottom: 0,
85
+ }, "ol, ul": {
86
+ paddingInlineStart: "16px",
87
+ marginLeft: "6px",
88
+ "ol, ul": {
89
+ marginLeft: 0,
90
+ },
91
+ li: {
92
+ margin: "16px 0",
93
+ },
94
+ }, a: {
95
+ color: theme.custom.colors.red,
96
+ fontWeight: "normal",
97
+ }, borderRadius: "12px", [`.${classes.messageRowUser} &`]: {
98
+ padding: "12px 16px",
99
+ borderRadius: "8px 0px 8px 8px",
100
+ backgroundColor: theme.custom.colors.lightGray1,
101
+ } })));
102
+ const StarterContainer = styled.div({
103
+ alignSelf: "flex-end",
104
+ alignItems: "end",
105
+ display: "flex",
106
+ flexDirection: "column",
107
+ gap: "12px",
108
+ });
109
+ const Starter = styled.button(({ theme }) => (Object.assign(Object.assign({ border: `1px solid ${theme.custom.colors.lightGray2}`, backgroundColor: theme.custom.colors.white, padding: "8px 16px" }, theme.typography.body3), { cursor: "pointer", boxSizing: "border-box", "&:hover": {
110
+ color: theme.custom.colors.white,
111
+ backgroundColor: theme.custom.colors.silverGrayDark,
112
+ borderColor: "transparent",
113
+ }, borderRadius: "8px" })));
114
+ const StyledSendButton = styled(RiSendPlaneFill)(({ theme }) => ({
115
+ fill: theme.custom.colors.red,
116
+ }));
117
+ const StyledStopButton = styled(RiStopFill)(({ theme }) => ({
118
+ fill: theme.custom.colors.red,
119
+ }));
120
+ const BottomSection = styled.div(({ externalScroll, theme }) => (Object.assign(Object.assign({ padding: "12px 0 16px" }, (externalScroll && {
121
+ position: "sticky",
122
+ bottom: 0,
123
+ background: theme.custom.colors.white,
124
+ })), { "button:focus-visible": {
125
+ outlineOffset: "-1px",
126
+ borderRadius: "7px",
127
+ } })));
128
+ const Disclaimer = styled(Typography)(({ theme }) => ({
129
+ color: theme.custom.colors.silverGrayDark,
130
+ marginTop: "16px",
131
+ textAlign: "center",
132
+ }));
133
+ const AiChatDisplay = (_a) => {
134
+ var _b, _c;
135
+ var { conversationStarters, askTimTitle, entryScreenEnabled = true, entryScreenTitle, srLoadingMessages, placeholder = "", className, scrollElement, ref } = _a, others = __rest(_a, ["conversationStarters", "askTimTitle", "entryScreenEnabled", "entryScreenTitle", "srLoadingMessages", "placeholder", "className", "scrollElement", "ref"]) // Could contain data attributes
136
+ ;
137
+ const containerRef = useRef(null);
138
+ const messagesContainerRef = useRef(null);
139
+ const chatScreenRef = useRef(null);
140
+ const promptInputRef = useRef(null);
141
+ const { messages, input, handleInputChange, handleSubmit, append, isLoading, stop, error, initialMessages, } = useAiChat();
142
+ useScrollSnap({
143
+ scrollElement: scrollElement || messagesContainerRef.current,
144
+ contentElement: scrollElement ? messagesContainerRef.current : null,
145
+ threshold: 200,
146
+ });
147
+ const [showEntryScreen, setShowEntryScreen] = useState(entryScreenEnabled);
148
+ useEffect(() => {
149
+ var _a, _b;
150
+ if (!showEntryScreen) {
151
+ (_b = (_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input")) === null || _b === void 0 ? void 0 : _b.focus();
152
+ }
153
+ }, [showEntryScreen]);
154
+ const showStarters = messages.length === ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) || 0);
155
+ const waiting = !showStarters && !error && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
156
+ const stoppable = isLoading && ((_c = messages[messages.length - 1]) === null || _c === void 0 ? void 0 : _c.role) !== "user";
157
+ const scrollToBottom = () => {
158
+ const element = scrollElement || messagesContainerRef.current;
159
+ element === null || element === void 0 ? void 0 : element.scrollBy({
160
+ behavior: "instant",
161
+ top: element === null || element === void 0 ? void 0 : element.scrollHeight,
162
+ });
163
+ };
164
+ const lastMsg = messages[messages.length - 1];
165
+ const externalScroll = !!scrollElement;
166
+ return (React.createElement(Container, { className: className, ref: containerRef }, showEntryScreen ? (React.createElement(EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
167
+ if (prompt.trim() === "") {
168
+ return;
169
+ }
170
+ setShowEntryScreen(false);
171
+ append({ role: "user", content: prompt });
172
+ } })) : (React.createElement(ChatScreen, { className: classes.chatScreenContainer, "data-testid": "ai-chat-screen", externalScroll: externalScroll, ref: chatScreenRef },
173
+ React.createElement(ChatContainer, Object.assign({ className: classNames(className, classes.root), externalScroll: externalScroll }, others),
174
+ React.createElement(ChatTitle, { askTimTitle: askTimTitle, externalScroll: externalScroll, className: classNames(className, classes.title) }),
175
+ React.createElement(MessagesContainer, { className: classes.messagesContainer, externalScroll: externalScroll, ref: messagesContainerRef },
176
+ messages.map((m) => (React.createElement(MessageRow, { key: m.id, "data-chat-role": m.role, className: classNames(classes.messageRow, {
177
+ [classes.messageRowUser]: m.role === "user",
178
+ [classes.messageRowAssistant]: m.role === "assistant",
179
+ }) },
180
+ React.createElement(Message, { className: classes.message },
181
+ React.createElement(VisuallyHidden, { as: m.role === "user" ? "h5" : "h6" }, m.role === "user" ? "You said: " : "Assistant said: "),
182
+ React.createElement(Markdown, { skipHtml: true }, m.content))))),
183
+ showStarters ? (React.createElement(StarterContainer, null, conversationStarters === null || conversationStarters === void 0 ? void 0 : conversationStarters.map((m) => (React.createElement(Starter, { className: classes.conversationStarter, key: m.content, onClick: () => {
184
+ scrollToBottom();
185
+ append({ role: "user", content: m.content });
186
+ } }, m.content))))) : null,
187
+ waiting ? (React.createElement(MessageRow, { className: classNames(classes.messageRow, classes.messageRowAssistant), key: "loading" },
188
+ React.createElement(Message, null,
189
+ React.createElement(RiMoreFill, null)))) : null,
190
+ error ? (React.createElement(Alert, { severity: "error", closable: true }, "An unexpected error has occurred.")) : null),
191
+ React.createElement(BottomSection, { externalScroll: externalScroll, className: classes.bottomSection },
192
+ React.createElement("form", { onSubmit: (e) => {
193
+ e.preventDefault();
194
+ if (isLoading && stoppable) {
195
+ stop();
196
+ }
197
+ else {
198
+ scrollToBottom();
199
+ handleSubmit(e);
200
+ }
201
+ } },
202
+ React.createElement(Input, { ref: promptInputRef, fullWidth: true, size: "chat", className: classes.input, placeholder: placeholder, name: "message", sx: { flex: 1 }, value: input, onChange: handleInputChange, inputProps: {
203
+ "aria-label": "Ask a question",
204
+ }, endAdornment: isLoading ? (React.createElement(AdornmentButton, { "aria-label": "Stop", onClick: stop, disabled: !stoppable },
205
+ React.createElement(StyledStopButton, null))) : (React.createElement(AdornmentButton, { "aria-label": "Send", type: "submit", onClick: (e) => {
206
+ if (input.trim() === "") {
207
+ e.preventDefault();
208
+ return;
209
+ }
210
+ scrollToBottom();
211
+ handleSubmit(e);
212
+ } },
213
+ React.createElement(StyledSendButton, null))) })),
214
+ React.createElement(Disclaimer, { variant: "body3" }, "AI-generated content may be incorrect.")),
215
+ React.createElement(SrAnnouncer, { isLoading: isLoading, loadingMessages: srLoadingMessages, message: (lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.role) === "assistant" ? lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.content : "" }))))));
216
+ };
217
+ const AiChat = (_a) => {
218
+ var { requestOpts, initialMessages, chatId, parseContent } = _a, displayProps = __rest(_a, ["requestOpts", "initialMessages", "chatId", "parseContent"]);
219
+ return (React.createElement(AiChatProvider, { requestOpts: requestOpts, chatId: chatId, initialMessages: initialMessages, parseContent: parseContent },
220
+ React.createElement(AiChatDisplay, Object.assign({}, displayProps))));
221
+ };
222
+ export { AiChatDisplay, AiChat };
@@ -0,0 +1,21 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AiChat } from "./AiChat";
3
+ declare const meta: Meta<typeof AiChat>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof AiChat>;
6
+ export declare const StreamingResponses: Story;
7
+ /**
8
+ * Here `AiChat` is used with a non-streaming JSON API. The JSON is converted
9
+ * to text via `parseContent`.
10
+ */
11
+ export declare const JsonResponses: Story;
12
+ /**
13
+ * This story shows the component's builtin markdown styling.
14
+ */
15
+ export declare const MarkdownStyling: Story;
16
+ /**
17
+ * Where a scrollable container exists in the including component, it can be passed in
18
+ * for the AiChat component to use in place of its own height constrained message container
19
+ * overflow scroll. This is useful for cases such as use inside a modal or drawer.
20
+ */
21
+ export declare const ScrollContainer: Story;