@mitodl/smoot-design 3.4.0 → 3.5.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.
- package/dist/bundles/remoteAiChatDrawer.es.js +13741 -12492
- package/dist/bundles/remoteAiChatDrawer.umd.js +114 -41
- package/dist/cjs/bundles/RemoteAiChatDrawer/RemoteAiChatDrawer.stories.js +6 -0
- package/dist/cjs/components/AiChat/AiChat.js +36 -33
- package/dist/cjs/components/AiChat/AiChat.stories.d.ts +4 -0
- package/dist/cjs/components/AiChat/AiChat.stories.js +40 -14
- package/dist/cjs/components/AiChat/AiChat.test.js +25 -17
- package/dist/cjs/components/AiChat/test-utils/api.d.ts +2 -0
- package/dist/{esm/components/AiChat/story-utils.js → cjs/components/AiChat/test-utils/api.js} +77 -51
- package/dist/cjs/components/Alert/Alert.d.ts +15 -0
- package/dist/cjs/components/Alert/Alert.js +62 -0
- package/dist/cjs/components/Alert/Alert.stories.d.ts +8 -0
- package/dist/cjs/components/Alert/Alert.stories.js +53 -0
- package/dist/cjs/components/Input/Input.js +8 -6
- package/dist/esm/bundles/RemoteAiChatDrawer/RemoteAiChatDrawer.stories.js +6 -0
- package/dist/esm/components/AiChat/AiChat.js +36 -33
- package/dist/esm/components/AiChat/AiChat.stories.d.ts +4 -0
- package/dist/esm/components/AiChat/AiChat.stories.js +39 -13
- package/dist/esm/components/AiChat/AiChat.test.js +25 -17
- package/dist/esm/components/AiChat/test-utils/api.d.ts +2 -0
- package/dist/{cjs/components/AiChat/story-utils.js → esm/components/AiChat/test-utils/api.js} +74 -55
- package/dist/esm/components/Alert/Alert.d.ts +15 -0
- package/dist/esm/components/Alert/Alert.js +59 -0
- package/dist/esm/components/Alert/Alert.stories.d.ts +8 -0
- package/dist/esm/components/Alert/Alert.stories.js +50 -0
- package/dist/esm/components/Input/Input.js +8 -6
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -1
- package/dist/cjs/components/AiChat/story-utils.d.ts +0 -3
- package/dist/esm/components/AiChat/story-utils.d.ts +0 -3
|
@@ -5,6 +5,7 @@ exports.StreamingResponses = void 0;
|
|
|
5
5
|
const React = require("react");
|
|
6
6
|
const RemoteAiChatDrawer_1 = require("./RemoteAiChatDrawer");
|
|
7
7
|
const tiny_invariant_1 = require("tiny-invariant");
|
|
8
|
+
const api_1 = require("../../components/AiChat/test-utils/api");
|
|
8
9
|
const TEST_API_STREAMING = "http://localhost:4567/streaming";
|
|
9
10
|
const INITIAL_MESSAGES = [
|
|
10
11
|
{
|
|
@@ -58,6 +59,11 @@ const meta = {
|
|
|
58
59
|
}, title: "button frame" }),
|
|
59
60
|
React.createElement(RemoteAiChatDrawer_1.AiChatDrawer, { messageOrigin: "http://localhost:6006" })));
|
|
60
61
|
},
|
|
62
|
+
parameters: {
|
|
63
|
+
msw: {
|
|
64
|
+
handlers: api_1.handlers,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
61
67
|
};
|
|
62
68
|
exports.default = meta;
|
|
63
69
|
exports.StreamingResponses = {};
|
|
@@ -24,6 +24,7 @@ const classnames_1 = require("classnames");
|
|
|
24
24
|
const SrAnnouncer_1 = require("../SrAnnouncer/SrAnnouncer");
|
|
25
25
|
const VisuallyHidden_1 = require("../VisuallyHidden/VisuallyHidden");
|
|
26
26
|
const Typography_1 = require("@mui/material/Typography");
|
|
27
|
+
const Alert_1 = require("../Alert/Alert");
|
|
27
28
|
const classes = {
|
|
28
29
|
root: "MitAiChat--root",
|
|
29
30
|
title: "MitAiChat--title",
|
|
@@ -34,6 +35,7 @@ const classes = {
|
|
|
34
35
|
messageRowAssistant: "MitAiChat--messageRowAssistant",
|
|
35
36
|
message: "MitAiChat--message",
|
|
36
37
|
input: "MitAiChat--input",
|
|
38
|
+
bottomSection: "MitAiChat--bottomSection",
|
|
37
39
|
};
|
|
38
40
|
const ChatContainer = styled_1.default.div({
|
|
39
41
|
width: "100%",
|
|
@@ -66,10 +68,9 @@ const MessagesContainer = (0, styled_1.default)(ScrollSnap_1.ScrollSnap)({
|
|
|
66
68
|
display: "flex",
|
|
67
69
|
flexDirection: "column",
|
|
68
70
|
flex: 1,
|
|
69
|
-
|
|
70
|
-
paddingBottom: "24px",
|
|
71
|
+
padding: "14px 0",
|
|
71
72
|
overflow: "auto",
|
|
72
|
-
gap: "
|
|
73
|
+
gap: "16px",
|
|
73
74
|
});
|
|
74
75
|
const MessageRow = styled_1.default.div({
|
|
75
76
|
display: "flex",
|
|
@@ -87,16 +88,18 @@ const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign
|
|
|
87
88
|
marginTop: 0,
|
|
88
89
|
}, "p:last-of-type": {
|
|
89
90
|
marginBottom: 0,
|
|
91
|
+
}, "ol, ul": {
|
|
92
|
+
paddingInlineStart: "32px",
|
|
93
|
+
li: {
|
|
94
|
+
margin: "16px 0",
|
|
95
|
+
},
|
|
96
|
+
}, ul: {
|
|
97
|
+
marginInlineStart: "-16px",
|
|
90
98
|
}, a: {
|
|
91
99
|
color: theme.custom.colors.red,
|
|
92
100
|
fontWeight: "normal",
|
|
93
101
|
}, borderRadius: "12px", [`.${classes.messageRowAssistant} &`]: {
|
|
94
|
-
|
|
95
|
-
borderRadius: "0px 8px 8px 8px",
|
|
96
|
-
svg: {
|
|
97
|
-
fill: theme.custom.colors.silverGrayDark,
|
|
98
|
-
display: "block",
|
|
99
|
-
},
|
|
102
|
+
padding: "12px 16px 12px 0",
|
|
100
103
|
}, [`.${classes.messageRowUser} &`]: {
|
|
101
104
|
borderRadius: "8px 0px 8px 8px",
|
|
102
105
|
backgroundColor: theme.custom.colors.lightGray1,
|
|
@@ -117,20 +120,18 @@ const RobotIcon = (0, styled_1.default)(react_1.RiRobot2Line)({
|
|
|
117
120
|
width: "40px",
|
|
118
121
|
height: "40px",
|
|
119
122
|
});
|
|
120
|
-
const StyledInput = (0, styled_1.default)(Input_1.Input)(({ theme }) => ({
|
|
121
|
-
backgroundColor: theme.custom.colors.lightGray1,
|
|
122
|
-
borderRadius: "8px",
|
|
123
|
-
border: `1px solid ${theme.custom.colors.lightGray2}`,
|
|
124
|
-
}));
|
|
125
123
|
const StyledSendButton = (0, styled_1.default)(react_1.RiSendPlaneFill)(({ theme }) => ({
|
|
126
124
|
fill: theme.custom.colors.red,
|
|
127
125
|
}));
|
|
128
126
|
const StyledStopButton = (0, styled_1.default)(react_1.RiStopFill)(({ theme }) => ({
|
|
129
127
|
fill: theme.custom.colors.red,
|
|
130
128
|
}));
|
|
129
|
+
const BottomSection = styled_1.default.div({
|
|
130
|
+
paddingTop: "12px",
|
|
131
|
+
});
|
|
131
132
|
const Disclaimer = (0, styled_1.default)(Typography_1.default)(({ theme }) => ({
|
|
132
133
|
color: theme.custom.colors.silverGrayDark,
|
|
133
|
-
marginTop: "
|
|
134
|
+
marginTop: "14px",
|
|
134
135
|
textAlign: "center",
|
|
135
136
|
}));
|
|
136
137
|
const ChatTitle = (0, styled_1.default)(({ title, askTimTitle, onClose, className }) => {
|
|
@@ -165,7 +166,7 @@ const AiChatInternal = function AiChat(_a) {
|
|
|
165
166
|
const prefix = Math.random().toString().slice(2);
|
|
166
167
|
return initMsgs.map((m, i) => (Object.assign(Object.assign({}, m), { id: `initial-${prefix}-${i}` })));
|
|
167
168
|
}, [initMsgs]);
|
|
168
|
-
const { messages: unparsed, input, handleInputChange, handleSubmit, append, isLoading, stop, } = (0, utils_1.useAiChat)(requestOpts, {
|
|
169
|
+
const { messages: unparsed, input, handleInputChange, handleSubmit, append, isLoading, stop, error, } = (0, utils_1.useAiChat)(requestOpts, {
|
|
169
170
|
initialMessages: initialMessages,
|
|
170
171
|
id: chatId,
|
|
171
172
|
});
|
|
@@ -181,7 +182,7 @@ const AiChatInternal = function AiChat(_a) {
|
|
|
181
182
|
});
|
|
182
183
|
}, [parseContent, unparsed, initialMessages]);
|
|
183
184
|
const showStarters = messages.length === initialMessages.length;
|
|
184
|
-
const waiting = !showStarters && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
|
|
185
|
+
const waiting = !showStarters && !error && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
|
|
185
186
|
const stoppable = isLoading && ((_c = messages[messages.length - 1]) === null || _c === void 0 ? void 0 : _c.role) !== "user";
|
|
186
187
|
const scrollToBottom = () => {
|
|
187
188
|
var _a;
|
|
@@ -207,24 +208,26 @@ const AiChatInternal = function AiChat(_a) {
|
|
|
207
208
|
} }, m.content))))) : null,
|
|
208
209
|
waiting ? (React.createElement(MessageRow, { className: (0, classnames_1.default)(classes.messageRow, classes.messageRowAssistant), key: "loading" },
|
|
209
210
|
React.createElement(Message, null,
|
|
210
|
-
React.createElement(react_1.RiMoreFill, null)))) : null
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
} },
|
|
221
|
-
React.createElement(StyledInput, { fullWidth: true, size: "chat", className: classes.input, placeholder: placeholder, name: "message", sx: { flex: 1 }, value: input, onChange: handleInputChange, endAdornment: isLoading ? (React.createElement(Input_1.AdornmentButton, { "aria-label": "Stop", onClick: stop, disabled: !stoppable },
|
|
222
|
-
React.createElement(StyledStopButton, null))) : (React.createElement(Input_1.AdornmentButton, { "aria-label": "Send", type: "submit", disabled: !input, onClick: (e) => {
|
|
211
|
+
React.createElement(react_1.RiMoreFill, null)))) : null,
|
|
212
|
+
error ? (React.createElement(Alert_1.Alert, { severity: "error", closable: true }, "An unexpected error has occurred.")) : null),
|
|
213
|
+
React.createElement(BottomSection, { className: classes.bottomSection },
|
|
214
|
+
React.createElement("form", { onSubmit: (e) => {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
if (isLoading && stoppable) {
|
|
217
|
+
stop();
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
223
220
|
scrollToBottom();
|
|
224
221
|
handleSubmit(e);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
}
|
|
223
|
+
} },
|
|
224
|
+
React.createElement(Input_1.Input, { fullWidth: true, size: "chat", className: classes.input, placeholder: placeholder, name: "message", sx: { flex: 1 }, value: input, onChange: handleInputChange, endAdornment: isLoading ? (React.createElement(Input_1.AdornmentButton, { "aria-label": "Stop", onClick: stop, disabled: !stoppable },
|
|
225
|
+
React.createElement(StyledStopButton, null))) : (React.createElement(Input_1.AdornmentButton, { "aria-label": "Send", type: "submit", onClick: (e) => {
|
|
226
|
+
scrollToBottom();
|
|
227
|
+
handleSubmit(e);
|
|
228
|
+
} },
|
|
229
|
+
React.createElement(StyledSendButton, null))) })),
|
|
230
|
+
React.createElement(Disclaimer, { variant: "body3" }, "AI-generated content may be incorrect.")),
|
|
228
231
|
React.createElement(SrAnnouncer_1.SrAnnouncer, { isLoading: isLoading, loadingMessages: srLoadingMessages, message: lastMsg.role === "assistant" ? lastMsg.content : "" })));
|
|
229
232
|
};
|
|
230
233
|
const AiChat = (props) => (
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JsonResponses = exports.StreamingResponses = void 0;
|
|
3
|
+
exports.MarkdownStyling = exports.JsonResponses = exports.StreamingResponses = void 0;
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const AiChat_1 = require("./AiChat");
|
|
6
|
-
const story_utils_1 = require("./story-utils");
|
|
7
6
|
const styled_1 = require("@emotion/styled");
|
|
8
7
|
const test_1 = require("@storybook/test");
|
|
8
|
+
const api_1 = require("./test-utils/api");
|
|
9
9
|
const TEST_API_STREAMING = "http://localhost:4567/streaming";
|
|
10
10
|
const TEST_API_JSON = "http://localhost:4567/json";
|
|
11
11
|
const INITIAL_MESSAGES = [
|
|
@@ -34,6 +34,9 @@ const Container = styled_1.default.div({
|
|
|
34
34
|
const meta = {
|
|
35
35
|
title: "smoot-design/AI/AiChat",
|
|
36
36
|
component: AiChat_1.AiChat,
|
|
37
|
+
parameters: {
|
|
38
|
+
msw: { handlers: api_1.handlers },
|
|
39
|
+
},
|
|
37
40
|
render: (args) => React.createElement(AiChat_1.AiChat, Object.assign({}, args)),
|
|
38
41
|
decorators: (Story) => {
|
|
39
42
|
return (React.createElement(Container, null,
|
|
@@ -58,18 +61,6 @@ const meta = {
|
|
|
58
61
|
table: { readonly: true }, // See above
|
|
59
62
|
},
|
|
60
63
|
},
|
|
61
|
-
beforeEach: () => {
|
|
62
|
-
const originalFetch = window.fetch;
|
|
63
|
-
window.fetch = (url, opts) => {
|
|
64
|
-
if (url === TEST_API_STREAMING) {
|
|
65
|
-
return (0, story_utils_1.mockStreaming)();
|
|
66
|
-
}
|
|
67
|
-
else if (url === TEST_API_JSON) {
|
|
68
|
-
return (0, story_utils_1.mockJson)();
|
|
69
|
-
}
|
|
70
|
-
return originalFetch(url, opts);
|
|
71
|
-
};
|
|
72
|
-
},
|
|
73
64
|
};
|
|
74
65
|
exports.default = meta;
|
|
75
66
|
exports.StreamingResponses = {};
|
|
@@ -86,3 +77,38 @@ exports.JsonResponses = {
|
|
|
86
77
|
},
|
|
87
78
|
},
|
|
88
79
|
};
|
|
80
|
+
const DEMO_MARKDOWN = `This shows default markdown styling. Here's a list:
|
|
81
|
+
- Item 1 lorem ipsum dolor sit amet, consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.
|
|
82
|
+
- Item 2
|
|
83
|
+
- Item 3
|
|
84
|
+
- Item 3.1
|
|
85
|
+
- Item 3.2
|
|
86
|
+
- Item 4
|
|
87
|
+
1. Item 4.1
|
|
88
|
+
2. Item 4.2
|
|
89
|
+
3. Item 4.3
|
|
90
|
+
|
|
91
|
+
Here is a longer paragraph and **bold text** and *italic text*. Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
92
|
+
sed do eiusmod tempor [incididunt](https://mit.edu) ut labore et dolore magna aliqua. Ut enim ad minim veniam.
|
|
93
|
+
|
|
94
|
+
And some inline code, \`\`<inline></inline>\`\` and code block:
|
|
95
|
+
\`\`\`
|
|
96
|
+
def f(x):
|
|
97
|
+
print(x)
|
|
98
|
+
\`\`\`
|
|
99
|
+
`;
|
|
100
|
+
/**
|
|
101
|
+
* This story shows the component's builtin markdown styling.
|
|
102
|
+
*/
|
|
103
|
+
exports.MarkdownStyling = {
|
|
104
|
+
args: {
|
|
105
|
+
title: "Markdown Styles",
|
|
106
|
+
requestOpts: { apiUrl: TEST_API_STREAMING },
|
|
107
|
+
initialMessages: [
|
|
108
|
+
{
|
|
109
|
+
role: "assistant",
|
|
110
|
+
content: DEMO_MARKDOWN,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
};
|
|
@@ -17,17 +17,18 @@ const AiChat_1 = require("./AiChat");
|
|
|
17
17
|
const ThemeProvider_1 = require("../ThemeProvider/ThemeProvider");
|
|
18
18
|
const React = require("react");
|
|
19
19
|
const en_1 = require("@faker-js/faker/locale/en");
|
|
20
|
+
const msw_1 = require("msw");
|
|
21
|
+
const node_1 = require("msw/node");
|
|
20
22
|
const counter = jest.fn(); // use jest.fn as counter because it resets on each test
|
|
21
|
-
const
|
|
23
|
+
const API_URL = "http://localhost:4567/test";
|
|
24
|
+
const server = (0, node_1.setupServer)(msw_1.http.post(API_URL, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
25
|
const count = counter.mock.calls.length;
|
|
23
26
|
counter();
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}));
|
|
30
|
-
window.fetch = mockFetch;
|
|
27
|
+
return msw_1.HttpResponse.text(`AI Response ${count}`);
|
|
28
|
+
})));
|
|
29
|
+
beforeAll(() => server.listen());
|
|
30
|
+
afterEach(() => server.resetHandlers());
|
|
31
|
+
afterAll(() => server.close());
|
|
31
32
|
jest.mock("react-markdown", () => {
|
|
32
33
|
return {
|
|
33
34
|
__esModule: true,
|
|
@@ -60,9 +61,9 @@ describe("AiChat", () => {
|
|
|
60
61
|
{ content: en_1.faker.lorem.sentence() },
|
|
61
62
|
{ content: en_1.faker.lorem.sentence() },
|
|
62
63
|
];
|
|
63
|
-
const view = (0, react_1.render)(React.createElement(AiChat_1.AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl:
|
|
64
|
+
const view = (0, react_1.render)(React.createElement(AiChat_1.AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl: API_URL }, placeholder: "Type a message..." }, props)), { wrapper: ThemeProvider_1.ThemeProvider });
|
|
64
65
|
const rerender = (newProps) => {
|
|
65
|
-
view.rerender(React.createElement(AiChat_1.AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl:
|
|
66
|
+
view.rerender(React.createElement(AiChat_1.AiChat, Object.assign({ "data-testid": "ai-chat", initialMessages: initialMessages, conversationStarters: conversationStarters, requestOpts: { apiUrl: API_URL } }, newProps)));
|
|
66
67
|
};
|
|
67
68
|
return { initialMessages, conversationStarters, rerender };
|
|
68
69
|
};
|
|
@@ -108,11 +109,11 @@ describe("AiChat", () => {
|
|
|
108
109
|
yield whenCount(getMessages, 3);
|
|
109
110
|
}));
|
|
110
111
|
test("transformBody is called before sending requests", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
const mockFetch = jest.spyOn(window, "fetch");
|
|
111
113
|
const fakeBody = { message: en_1.faker.lorem.sentence() };
|
|
112
|
-
const apiUrl = en_1.faker.internet.url();
|
|
113
114
|
const transformBody = jest.fn(() => fakeBody);
|
|
114
115
|
const { initialMessages } = setup({
|
|
115
|
-
requestOpts: { apiUrl, transformBody },
|
|
116
|
+
requestOpts: { apiUrl: API_URL, transformBody },
|
|
116
117
|
});
|
|
117
118
|
yield user_event_1.default.click(react_1.screen.getByPlaceholderText("Type a message..."));
|
|
118
119
|
yield user_event_1.default.paste("User message");
|
|
@@ -122,16 +123,15 @@ describe("AiChat", () => {
|
|
|
122
123
|
expect.objectContaining({ content: "User message", role: "user" }),
|
|
123
124
|
]);
|
|
124
125
|
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
125
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
126
|
+
expect(mockFetch).toHaveBeenCalledWith(API_URL, expect.objectContaining({
|
|
126
127
|
body: JSON.stringify(fakeBody),
|
|
127
128
|
}));
|
|
128
129
|
}));
|
|
129
130
|
test("parseContent is called on the API-received message content", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
130
131
|
const fakeBody = { message: en_1.faker.lorem.sentence() };
|
|
131
|
-
const apiUrl = en_1.faker.internet.url();
|
|
132
132
|
const transformBody = jest.fn(() => fakeBody);
|
|
133
133
|
const { initialMessages, conversationStarters } = setup({
|
|
134
|
-
requestOpts: { apiUrl, transformBody },
|
|
134
|
+
requestOpts: { apiUrl: API_URL, transformBody },
|
|
135
135
|
parseContent: jest.fn((content) => `Parsed: ${content}`),
|
|
136
136
|
});
|
|
137
137
|
yield user_event_1.default.click(getConversationStarters()[0]);
|
|
@@ -151,12 +151,20 @@ describe("AiChat", () => {
|
|
|
151
151
|
}));
|
|
152
152
|
test("Passes extra attributes to root", () => {
|
|
153
153
|
const fakeBody = { message: en_1.faker.lorem.sentence() };
|
|
154
|
-
const apiUrl = en_1.faker.internet.url();
|
|
155
154
|
const transformBody = jest.fn(() => fakeBody);
|
|
156
155
|
setup({
|
|
157
|
-
requestOpts: { apiUrl, transformBody },
|
|
156
|
+
requestOpts: { apiUrl: API_URL, transformBody },
|
|
158
157
|
parseContent: jest.fn((content) => `Parsed: ${content}`),
|
|
159
158
|
});
|
|
160
159
|
expect(react_1.screen.getByTestId("ai-chat")).toBeInTheDocument();
|
|
161
160
|
});
|
|
161
|
+
test("If the API returns an error, an alert is shown", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
162
|
+
setup();
|
|
163
|
+
server.use(msw_1.http.post(API_URL, () => __awaiter(void 0, void 0, void 0, function* () {
|
|
164
|
+
return new msw_1.HttpResponse(null, { status: 500 });
|
|
165
|
+
})));
|
|
166
|
+
yield user_event_1.default.click(getConversationStarters()[0]);
|
|
167
|
+
const alert = yield react_1.screen.findByRole("alert");
|
|
168
|
+
expect(alert).toHaveTextContent("An unexpected error has occurred");
|
|
169
|
+
}));
|
|
162
170
|
});
|
package/dist/{esm/components/AiChat/story-utils.js → cjs/components/AiChat/test-utils/api.js}
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -7,6 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
9
|
});
|
|
9
10
|
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handlers = void 0;
|
|
13
|
+
const msw_1 = require("msw");
|
|
10
14
|
const SAMPLE_RESPONSES = [
|
|
11
15
|
`For exploring AI applications in business, I recommend the following course from MIT:
|
|
12
16
|
|
|
@@ -37,63 +41,85 @@ Here are some courses on linear algebra that you can explore:
|
|
|
37
41
|
These courses provide a comprehensive introduction to linear algebra and its applications across various fields.
|
|
38
42
|
<!-- Comment! -->
|
|
39
43
|
`,
|
|
44
|
+
`Here are some courses on quantum computing that offer certificates:
|
|
45
|
+
|
|
46
|
+
1. [Introduction to Quantum Computing](https://xpro.mit.edu/courses/course-v1:xPRO+QCFx1/)
|
|
47
|
+
- **Description**: This is the first course in the Quantum Computing Fundamentals professional certificate program. You can earn a Professional Certificate and CEUs by completing both courses in the program. Alternatively, you can take this course individually for a certificate of completion and CEUs.
|
|
48
|
+
- **Offered by**: MIT xPRO
|
|
49
|
+
- **Instructors**: Isaac Chuang, William Oliver, Peter Shor, Aram Harrow
|
|
50
|
+
|
|
51
|
+
2. [Practical Realities of Quantum Computation and Quantum Communication](https://xpro.mit.edu/courses/course-v1:xPRO+QCRx1/)
|
|
52
|
+
- **Description**: This course is part of the Quantum Computing Realities professional certificate program. Completing both courses in the program will earn you a Professional Certificate and CEUs. You can also take this course individually for a certificate of completion and CEUs.
|
|
53
|
+
- **Offered by**: MIT xPRO
|
|
54
|
+
- **Instructors**: Isaac Chuang, William Oliver, Peter Shor, Aram Harrow
|
|
55
|
+
|
|
56
|
+
These courses are part of professional certificate programs, and you can choose to complete the entire program or take individual courses for certification.`,
|
|
40
57
|
];
|
|
41
58
|
const rand = (min, max) => {
|
|
42
59
|
// min and max included
|
|
43
60
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
44
61
|
};
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (i === num) {
|
|
72
|
-
controller.close();
|
|
73
|
-
clearInterval(timerId);
|
|
74
|
-
}
|
|
75
|
-
}, 100);
|
|
76
|
-
},
|
|
77
|
-
cancel() {
|
|
78
|
-
if (timerId) {
|
|
62
|
+
const getReadableStream = () => {
|
|
63
|
+
let timerId;
|
|
64
|
+
const response = SAMPLE_RESPONSES[rand(0, SAMPLE_RESPONSES.length - 1)];
|
|
65
|
+
const chunks = response.split(" ").reduce((acc, word) => {
|
|
66
|
+
const last = acc[acc.length - 1];
|
|
67
|
+
if (acc.length === 0) {
|
|
68
|
+
acc.push(word);
|
|
69
|
+
}
|
|
70
|
+
else if (Math.random() < 0.75) {
|
|
71
|
+
acc[acc.length - 1] = `${last} ${word}`;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
acc.push(` ${word}`);
|
|
75
|
+
}
|
|
76
|
+
return acc;
|
|
77
|
+
}, []);
|
|
78
|
+
const num = chunks.length;
|
|
79
|
+
let i = 0;
|
|
80
|
+
return new ReadableStream({
|
|
81
|
+
start(controller) {
|
|
82
|
+
timerId = setInterval(() => {
|
|
83
|
+
const msg = new TextEncoder().encode(chunks[i]);
|
|
84
|
+
controller.enqueue(msg);
|
|
85
|
+
i++;
|
|
86
|
+
if (i === num) {
|
|
87
|
+
controller.close();
|
|
79
88
|
clearInterval(timerId);
|
|
80
89
|
}
|
|
81
|
-
},
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
}
|
|
90
|
+
}, 100);
|
|
91
|
+
},
|
|
92
|
+
cancel() {
|
|
93
|
+
if (timerId) {
|
|
94
|
+
clearInterval(timerId);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
88
97
|
});
|
|
89
98
|
};
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
const handlers = [
|
|
100
|
+
msw_1.http.post("http://localhost:4567/streaming", (_a) => __awaiter(void 0, [_a], void 0, function* ({ request }) {
|
|
101
|
+
yield (0, msw_1.delay)(600);
|
|
102
|
+
const body = getReadableStream();
|
|
103
|
+
const requestBody = yield request.json();
|
|
104
|
+
if (Array.isArray(requestBody)) {
|
|
105
|
+
const last = requestBody[requestBody.length - 1];
|
|
106
|
+
const { content } = last;
|
|
107
|
+
if (content === "error") {
|
|
108
|
+
return new msw_1.HttpResponse("Internal Server Error", {
|
|
109
|
+
status: 500,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return new msw_1.HttpResponse(body, {
|
|
114
|
+
headers: {
|
|
115
|
+
"Content-Type": "text/plain",
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
})),
|
|
119
|
+
msw_1.http.post("http://localhost:4567/json", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
120
|
+
const message = SAMPLE_RESPONSES[rand(0, SAMPLE_RESPONSES.length - 1)];
|
|
121
|
+
yield (0, msw_1.delay)(800);
|
|
122
|
+
return msw_1.HttpResponse.json({ message });
|
|
123
|
+
})),
|
|
124
|
+
];
|
|
125
|
+
exports.handlers = handlers;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { AlertColor } from "@mui/material/Alert";
|
|
3
|
+
type AlertProps = {
|
|
4
|
+
visible?: boolean;
|
|
5
|
+
closable?: boolean;
|
|
6
|
+
className?: string;
|
|
7
|
+
severity?: AlertColor;
|
|
8
|
+
/**
|
|
9
|
+
* Alert Content
|
|
10
|
+
*/
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
declare const Alert: React.FC<AlertProps>;
|
|
14
|
+
export { Alert };
|
|
15
|
+
export type { AlertProps };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Alert = void 0;
|
|
5
|
+
const React = require("react");
|
|
6
|
+
const styled_1 = require("@emotion/styled");
|
|
7
|
+
const Alert_1 = require("@mui/material/Alert");
|
|
8
|
+
const getColor = (theme, severity) => {
|
|
9
|
+
return {
|
|
10
|
+
info: theme.custom.colors.blue,
|
|
11
|
+
success: theme.custom.colors.green,
|
|
12
|
+
warning: theme.custom.colors.orange,
|
|
13
|
+
error: theme.custom.colors.lightRed,
|
|
14
|
+
}[severity];
|
|
15
|
+
};
|
|
16
|
+
const AlertStyled = (0, styled_1.default)(Alert_1.default)(({ theme, severity }) => ({
|
|
17
|
+
padding: "11px 16px",
|
|
18
|
+
borderRadius: 4,
|
|
19
|
+
borderWidth: 2,
|
|
20
|
+
borderStyle: "solid",
|
|
21
|
+
borderColor: getColor(theme, severity),
|
|
22
|
+
background: "#FFF",
|
|
23
|
+
".MuiAlert-message": Object.assign(Object.assign({}, theme.typography.body2), { color: theme.custom.colors.darkGray2, alignSelf: "center" }),
|
|
24
|
+
"> div": {
|
|
25
|
+
paddingTop: 0,
|
|
26
|
+
paddingBottom: 0,
|
|
27
|
+
},
|
|
28
|
+
".MuiAlert-icon": {
|
|
29
|
+
marginRight: 8,
|
|
30
|
+
svg: {
|
|
31
|
+
width: 16,
|
|
32
|
+
fill: getColor(theme, severity),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
button: {
|
|
36
|
+
padding: 0,
|
|
37
|
+
":hover": {
|
|
38
|
+
margin: 0,
|
|
39
|
+
background: "none",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
const Hidden = styled_1.default.span({ display: "none" });
|
|
44
|
+
const Alert = ({ visible = true, severity = "info", closable, children, className, }) => {
|
|
45
|
+
const [_visible, setVisible] = React.useState(visible);
|
|
46
|
+
const id = React.useId();
|
|
47
|
+
const onCloseClick = () => {
|
|
48
|
+
setVisible(false);
|
|
49
|
+
};
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
setVisible(visible);
|
|
52
|
+
}, [visible]);
|
|
53
|
+
if (!_visible) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return (React.createElement(AlertStyled, { severity: severity, onClose: closable ? onCloseClick : undefined, role: "alert", "aria-describedby": id, className: className },
|
|
57
|
+
children,
|
|
58
|
+
React.createElement(Hidden, { id: id },
|
|
59
|
+
severity,
|
|
60
|
+
" message")));
|
|
61
|
+
};
|
|
62
|
+
exports.Alert = Alert;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Alert } from "./Alert";
|
|
3
|
+
declare const meta: Meta<typeof Alert>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof Alert>;
|
|
6
|
+
export declare const Basic: Story;
|
|
7
|
+
export declare const Closable: Story;
|
|
8
|
+
export declare const Variants: Story;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Variants = exports.Closable = exports.Basic = void 0;
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const Alert_1 = require("./Alert");
|
|
6
|
+
const Stack_1 = require("@mui/material/Stack");
|
|
7
|
+
const meta = {
|
|
8
|
+
title: "smoot-design/Alert",
|
|
9
|
+
component: Alert_1.Alert,
|
|
10
|
+
};
|
|
11
|
+
exports.default = meta;
|
|
12
|
+
exports.Basic = {
|
|
13
|
+
args: {
|
|
14
|
+
severity: "info",
|
|
15
|
+
},
|
|
16
|
+
render: (args) => (React.createElement(Alert_1.Alert, Object.assign({}, args),
|
|
17
|
+
"Alert with severity \"",
|
|
18
|
+
args.severity,
|
|
19
|
+
"\"")),
|
|
20
|
+
};
|
|
21
|
+
exports.Closable = {
|
|
22
|
+
args: {
|
|
23
|
+
severity: "warning",
|
|
24
|
+
closable: true,
|
|
25
|
+
},
|
|
26
|
+
render: (args) => (React.createElement(Alert_1.Alert, Object.assign({}, args),
|
|
27
|
+
"Closable alert with severity \"",
|
|
28
|
+
args.severity,
|
|
29
|
+
"\"")),
|
|
30
|
+
};
|
|
31
|
+
exports.Variants = {
|
|
32
|
+
argTypes: {
|
|
33
|
+
severity: {
|
|
34
|
+
table: {
|
|
35
|
+
disable: true,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
closable: {
|
|
39
|
+
table: {
|
|
40
|
+
disable: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
render: (args) => (React.createElement(Stack_1.default, { direction: "column", gap: 2, sx: { my: 2 } },
|
|
45
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { severity: "info" }), "Alert with severity \"info\""),
|
|
46
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { closable: true, severity: "info" }), "Closable alert with severity \"info\""),
|
|
47
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { severity: "success" }), "Alert with severity \"success\""),
|
|
48
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { closable: true, severity: "success" }), "Closable alert with severity \"success\""),
|
|
49
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { severity: "warning" }), "Alert with severity \"warning\""),
|
|
50
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { closable: true, severity: "warning" }), "Closable alert with severity \"warning\""),
|
|
51
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { severity: "error" }), "Alert with severity \"error\""),
|
|
52
|
+
React.createElement(Alert_1.Alert, Object.assign({}, args, { closable: true, severity: "error" }), "Closable alert with severity \"error\""))),
|
|
53
|
+
};
|