From e99c67a1532b34f0396c5e9f4a7cc30c1c731858 Mon Sep 17 00:00:00 2001 From: Phoomparin Mano <poom@metabase.com> Date: Fri, 25 Oct 2024 03:49:38 +0700 Subject: [PATCH] fix(sdk): ability to save questions in interactive question (#48866) * implement saving questions in interactive question * close modal on create question * add e2e test for saving questions --- .../interactive-question.cy.spec.ts | 26 ++++++++++++++++ .../InteractiveQuestionResult.tsx | 31 +++++++++++++++++-- .../InteractiveQuestion.stories.tsx | 1 + .../SaveQuestionModal/SaveQuestionModal.tsx | 8 ++++- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/e2e/test/scenarios/embedding-sdk/interactive-question.cy.spec.ts b/e2e/test/scenarios/embedding-sdk/interactive-question.cy.spec.ts index 21590930a2d..0d81769206e 100644 --- a/e2e/test/scenarios/embedding-sdk/interactive-question.cy.spec.ts +++ b/e2e/test/scenarios/embedding-sdk/interactive-question.cy.spec.ts @@ -1,6 +1,7 @@ import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database"; import { createQuestion, + modal, popover, restore, setTokenFeatures, @@ -98,4 +99,29 @@ describeSDK("scenarios > embedding-sdk > interactive-question", () => { cy.icon("warning").should("not.exist"); }); + + it("can save a question", () => { + cy.intercept("POST", "/api/card").as("createCard"); + + cy.findAllByTestId("cell-data").last().click(); + + popover().findByText("See these Orders").click(); + + cy.findByRole("button", { name: "Save" }).click(); + + modal().within(() => { + cy.findByRole("radiogroup").findByText("Save as new question").click(); + + cy.findByPlaceholderText("What is the name of your question?") + .clear() + .type("Foo Bar Orders"); + + cy.findByRole("button", { name: "Save" }).click(); + }); + + cy.wait("@createCard").then(({ response }) => { + expect(response?.statusCode).to.equal(200); + expect(response?.body.name).to.equal("Foo Bar Orders"); + }); + }); }); diff --git a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx index da87f63870e..1d41e25b804 100644 --- a/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx +++ b/enterprise/frontend/src/embedding-sdk/components/private/InteractiveQuestionResult/InteractiveQuestionResult.tsx @@ -8,6 +8,7 @@ import { SdkError, SdkLoader, } from "embedding-sdk/components/private/PublicComponentWrapper"; +import { SaveQuestionModal } from "metabase/containers/SaveQuestionModal"; import { Box, Button, Group, Icon } from "metabase/ui"; import { InteractiveQuestion } from "../../public/InteractiveQuestion"; @@ -58,12 +59,22 @@ export const InteractiveQuestionResult = ({ const [questionView, setQuestionView] = useState<QuestionView>("visualization"); - const { question, queryResults, isQuestionLoading } = - useInteractiveQuestionContext(); + const { + question, + queryResults, + isQuestionLoading, + isSaveEnabled, + originalQuestion, + onCreate, + onSave, + } = useInteractiveQuestionContext(); const [isChartSelectorOpen, { toggle: toggleChartTypeSelector }] = useDisclosure(false); + const [isSaveModalOpen, { open: openSaveModal, close: closeSaveModal }] = + useDisclosure(false); + if (isQuestionLoading) { return <SdkLoader />; } @@ -104,6 +115,9 @@ export const InteractiveQuestionResult = ({ ) } /> + {isSaveEnabled && !isSaveModalOpen && ( + <InteractiveQuestion.SaveButton onClick={openSaveModal} /> + )} </Group> </Group> @@ -147,6 +161,19 @@ export const InteractiveQuestionResult = ({ /> </Box> </Box> + + {/* Refer to the SaveQuestionProvider for context on why we have to do it like this */} + {isSaveModalOpen && question && ( + <SaveQuestionModal + question={question} + originalQuestion={originalQuestion ?? null} + opened + closeOnSuccess + onClose={closeSaveModal} + onCreate={onCreate} + onSave={onSave} + /> + )} </FlexibleSizeComponent> ); }; diff --git a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.stories.tsx b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.stories.tsx index a9c05f1ea0e..fd466954d9d 100644 --- a/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.stories.tsx +++ b/enterprise/frontend/src/embedding-sdk/components/public/InteractiveQuestion/InteractiveQuestion.stories.tsx @@ -29,5 +29,6 @@ export const Default = { args: { questionId: QUESTION_ID, + isSaveEnabled: true, }, }; diff --git a/frontend/src/metabase/containers/SaveQuestionModal/SaveQuestionModal.tsx b/frontend/src/metabase/containers/SaveQuestionModal/SaveQuestionModal.tsx index 56ad5a0e72f..b7ce3a25273 100644 --- a/frontend/src/metabase/containers/SaveQuestionModal/SaveQuestionModal.tsx +++ b/frontend/src/metabase/containers/SaveQuestionModal/SaveQuestionModal.tsx @@ -20,7 +20,13 @@ export const SaveQuestionModal = ({ <SaveQuestionProvider question={question} originalQuestion={originalQuestion} - onCreate={onCreate} + onCreate={async question => { + await onCreate(question); + + if (closeOnSuccess) { + modalProps.onClose(); + } + }} onSave={onSave} multiStep={multiStep} initialCollectionId={initialCollectionId} -- GitLab