Skip to content

Commit 69d915e

Browse files
authored
fix: prevent modalContext override in API response handler (#972)
prevent modalContext override in API response handler
1 parent ed2f3a0 commit 69d915e

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed

.changeset/brave-geckos-deny.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ensembleui/react-runtime": patch
3+
---
4+
5+
Fix prevent modalContext override in API response handler

packages/runtime/src/runtime/hooks/__tests__/useInvokeApi.test.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const frameworkActual = jest.requireActual("@ensembleui/react-framework");
55

66
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
77
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8+
import "@testing-library/jest-dom";
89
import { type ReactNode } from "react";
910
import "../../../widgets";
1011
import { BrowserRouter } from "react-router-dom";
@@ -202,3 +203,70 @@ test("fetch API without cache", async () => {
202203
expect(fetchMock).toHaveBeenCalledTimes(2);
203204
});
204205
});
206+
207+
test("after API response modal should close", async () => {
208+
fetchMock.mockResolvedValue({ body: { data: "foobar" } });
209+
210+
render(
211+
<EnsembleScreen
212+
screen={{
213+
name: "test_cache",
214+
id: "test_cache",
215+
body: {
216+
name: "Button",
217+
properties: {
218+
label: "Show Dialog",
219+
onTap: {
220+
showDialog: {
221+
widget: {
222+
Column: {
223+
children: [
224+
{
225+
Text: {
226+
text: "This is modal",
227+
},
228+
},
229+
{
230+
Button: {
231+
label: "Trigger API",
232+
onTap: {
233+
invokeAPI: {
234+
name: "testCache",
235+
onResponse: {
236+
executeCode: "ensemble.closeAllDialogs()",
237+
},
238+
},
239+
},
240+
},
241+
},
242+
],
243+
},
244+
},
245+
},
246+
},
247+
},
248+
},
249+
apis: [{ name: "testCache", method: "GET" }],
250+
}}
251+
/>,
252+
{ wrapper: BrowserRouterWrapper },
253+
);
254+
255+
const showDialogButton = screen.getByText("Show Dialog");
256+
fireEvent.click(showDialogButton);
257+
258+
const modalTitle = screen.getByText("This is modal");
259+
const triggerAPIButton = screen.getByText("Trigger API");
260+
261+
await waitFor(() => {
262+
expect(modalTitle).toBeInTheDocument();
263+
expect(triggerAPIButton).toBeInTheDocument();
264+
});
265+
266+
fireEvent.click(triggerAPIButton);
267+
268+
await waitFor(() => {
269+
expect(modalTitle).not.toBeInTheDocument();
270+
expect(triggerAPIButton).not.toBeInTheDocument();
271+
});
272+
});

packages/runtime/src/runtime/hooks/useEnsembleAction.tsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,12 @@ export const useInvokeAPI: EnsembleActionHook<InvokeAPIAction> = (action) => {
175175
const onAPIErrorAction = useEnsembleAction(currentApi?.onError);
176176

177177
const invokeCommand = useCommandCallback(
178-
async (evalContext, ...args) => {
178+
async (evalContext, ...args: unknown[]) => {
179179
if (!action?.name || !currentApi) return;
180180

181-
const context = merge({}, evalContext, args[0]);
181+
const context = merge({}, evalContext, args[0]) as {
182+
[key: string]: unknown;
183+
};
182184

183185
if (action.name !== currentApi.name) return;
184186

@@ -242,8 +244,14 @@ export const useInvokeAPI: EnsembleActionHook<InvokeAPIAction> = (action) => {
242244
setData(action.id, response);
243245
}
244246

245-
onAPIResponseAction?.callback({ ...context, response });
246-
onInvokeAPIResponseAction?.callback({ ...context, response });
247+
onAPIResponseAction?.callback({
248+
...(args[0] as { [key: string]: unknown }),
249+
response,
250+
});
251+
onInvokeAPIResponseAction?.callback({
252+
...(args[0] as { [key: string]: unknown }),
253+
response,
254+
});
247255
} catch (e) {
248256
logError(e);
249257

@@ -261,22 +269,18 @@ export const useInvokeAPI: EnsembleActionHook<InvokeAPIAction> = (action) => {
261269
});
262270
}
263271

264-
onAPIErrorAction?.callback({ ...context, error: e });
265-
onInvokeAPIErrorAction?.callback({ ...context, error: e });
272+
onAPIErrorAction?.callback({
273+
...(args[0] as { [key: string]: unknown }),
274+
error: e,
275+
});
276+
onInvokeAPIErrorAction?.callback({
277+
...(args[0] as { [key: string]: unknown }),
278+
error: e,
279+
});
266280
}
267281
},
268282
{ navigate },
269-
[
270-
action,
271-
currentApi,
272-
screenModel,
273-
appContext,
274-
queryClient,
275-
onAPIResponseAction,
276-
onAPIErrorAction,
277-
onInvokeAPIResponseAction,
278-
onInvokeAPIErrorAction,
279-
],
283+
[action, currentApi, screenModel, appContext, queryClient],
280284
);
281285

282286
return { callback: invokeCommand };

0 commit comments

Comments
 (0)