From f7caac7d00c8538d970b67e98d0d8ca2f34b5e55 Mon Sep 17 00:00:00 2001 From: Uladzimir Havenchyk <125459446+uladzimirdev@users.noreply.github.com> Date: Tue, 30 May 2023 14:13:19 +0300 Subject: [PATCH] Show name in save action form model picker (#30990) --- .../ActionCreator-QueryActions.unit.spec.ts | 58 ++++++++++++++++++- .../containers/ActionCreator/tests/common.tsx | 23 +++++--- .../FormModelPicker/FormModelPicker.tsx | 24 ++++++-- .../__mocks__/NativeQueryEditor.jsx | 3 +- 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/frontend/src/metabase/actions/containers/ActionCreator/tests/ActionCreator-QueryActions.unit.spec.ts b/frontend/src/metabase/actions/containers/ActionCreator/tests/ActionCreator-QueryActions.unit.spec.ts index 7161f857315..231767bf0b5 100644 --- a/frontend/src/metabase/actions/containers/ActionCreator/tests/ActionCreator-QueryActions.unit.spec.ts +++ b/frontend/src/metabase/actions/containers/ActionCreator/tests/ActionCreator-QueryActions.unit.spec.ts @@ -1,14 +1,16 @@ -import { screen, getIcon, queryIcon } from "__support__/ui"; +import userEvent from "@testing-library/user-event"; +import { screen, getIcon, queryIcon, within } from "__support__/ui"; import { createMockActionParameter, + createMockCard, createMockQueryAction, } from "metabase-types/api/mocks"; import { setup } from "./common"; describe("ActionCreator > Query Actions", () => { - describe("new action", () => { + describe("New Action", () => { it("renders correctly", async () => { await setup(); @@ -45,9 +47,59 @@ describe("ActionCreator > Query Actions", () => { screen.getByRole("button", { name: "Action settings" }), ).toBeInTheDocument(); }); + + describe("Save Modal", () => { + it("should show default message in model picker", async () => { + await setup({ model: null }); + + // put query into textbox + const view = screen.getByTestId("mock-native-query-editor"); + userEvent.paste( + within(view).getByRole("textbox"), + "select * from orders where {{paramNane}}", + ); + + userEvent.click(screen.getByRole("button", { name: "Save" })); + + // form is rendered + expect( + screen.getByPlaceholderText("My new fantastic action"), + ).toBeInTheDocument(); + expect(screen.getByTestId("select-button-content")).toHaveTextContent( + "Select a model", + ); + }); + it("should preselect model", async () => { + const MODEL_NAME = "Awesome Model"; + const model = createMockCard({ + dataset: true, + can_write: true, + name: MODEL_NAME, + }); + await setup({ model }); + + // put query into textbox + const view = screen.getByTestId("mock-native-query-editor"); + userEvent.paste( + within(view).getByRole("textbox"), + "select * from orders where {{paramNane}}", + ); + + userEvent.click(screen.getByRole("button", { name: "Save" })); + + // form is rendered + expect( + screen.getByPlaceholderText("My new fantastic action"), + ).toBeInTheDocument(); + // model is preselected + expect(screen.getByTestId("select-button-content")).toHaveTextContent( + MODEL_NAME, + ); + }); + }); }); - describe("editing action", () => { + describe("Editing Action", () => { it("renders correctly", async () => { const action = createMockQueryAction(); await setup({ action }); diff --git a/frontend/src/metabase/actions/containers/ActionCreator/tests/common.tsx b/frontend/src/metabase/actions/containers/ActionCreator/tests/common.tsx index 3fc476b5b52..50ac05982e3 100644 --- a/frontend/src/metabase/actions/containers/ActionCreator/tests/common.tsx +++ b/frontend/src/metabase/actions/containers/ActionCreator/tests/common.tsx @@ -12,7 +12,7 @@ import { setupDatabasesEndpoints, } from "__support__/server-mocks"; -import type { WritebackAction } from "metabase-types/api"; +import type { Card, WritebackAction } from "metabase-types/api"; import { createMockCard, createMockDatabase, @@ -33,6 +33,7 @@ export type SetupOpts = { hasActionsEnabled?: boolean; isAdmin?: boolean; isPublicSharingEnabled?: boolean; + model?: Card | null; }; export async function setup({ @@ -41,17 +42,21 @@ export async function setup({ hasActionsEnabled = true, isAdmin = false, isPublicSharingEnabled = false, + model, }: SetupOpts = {}) { - const model = createMockCard({ - dataset: true, - can_write: canWrite, - }); + if (model === undefined) { + model = createMockCard({ + dataset: true, + can_write: canWrite, + }); + } + const database = createMockDatabase({ settings: { "database-enable-actions": hasActionsEnabled }, }); setupDatabasesEndpoints([database]); - setupCardsEndpoints([model]); + setupCardsEndpoints(model ? [model] : []); if (action) { fetchMock.get(`path:/api/action/${action.id}`, action); @@ -62,7 +67,11 @@ export async function setup({ } renderWithProviders( - <ActionCreator actionId={action?.id} modelId={model.id} />, + <ActionCreator + actionId={action?.id} + modelId={model?.id} + databaseId={database.id} + />, { storeInitialState: createMockState({ currentUser: createMockUser({ diff --git a/frontend/src/metabase/models/containers/FormModelPicker/FormModelPicker.tsx b/frontend/src/metabase/models/containers/FormModelPicker/FormModelPicker.tsx index 7205f98412a..a0c3fd9346a 100644 --- a/frontend/src/metabase/models/containers/FormModelPicker/FormModelPicker.tsx +++ b/frontend/src/metabase/models/containers/FormModelPicker/FormModelPicker.tsx @@ -9,13 +9,10 @@ import { t } from "ttag"; import { useField } from "formik"; import { useUniqueId } from "metabase/hooks/use-unique-id"; - import FormField from "metabase/core/components/FormField"; import SelectButton from "metabase/core/components/SelectButton"; import TippyPopoverWithTrigger from "metabase/components/PopoverWithTrigger/TippyPopoverWithTrigger"; - -import Models from "metabase/entities/questions"; - +import { useQuestionQuery } from "metabase/common/hooks"; import type { CardId } from "metabase-types/api"; import { PopoverItemPicker, MIN_POPOVER_WIDTH } from "./FormModelPicker.styled"; @@ -39,6 +36,11 @@ function FormModelPicker({ const [{ value }, { error, touched }, { setValue }] = useField(name); const formFieldRef = useRef<HTMLDivElement>(null); const [width, setWidth] = useState(MIN_POPOVER_WIDTH); + const isModelSelected = typeof value === "number"; + const { data: model } = useQuestionQuery({ + id: value, + enabled: isModelSelected, + }); useEffect(() => { const { width: formFieldWidth } = @@ -59,11 +61,21 @@ function FormModelPicker({ ref={formFieldRef} > <SelectButton onClick={handleShowPopover}> - {typeof value === "number" ? <Models.Name id={value} /> : placeholder} + {isModelSelected ? model?.displayName() : placeholder} </SelectButton> </FormField> ), - [id, value, title, placeholder, error, touched, className, style], + [ + id, + title, + placeholder, + error, + touched, + className, + style, + model, + isModelSelected, + ], ); const renderContent = useCallback( diff --git a/frontend/src/metabase/query_builder/components/__mocks__/NativeQueryEditor.jsx b/frontend/src/metabase/query_builder/components/__mocks__/NativeQueryEditor.jsx index 7b136984a1a..232ec1014ca 100644 --- a/frontend/src/metabase/query_builder/components/__mocks__/NativeQueryEditor.jsx +++ b/frontend/src/metabase/query_builder/components/__mocks__/NativeQueryEditor.jsx @@ -2,6 +2,7 @@ import React from "react"; import SyncedParametersList from "metabase/parameters/components/SyncedParametersList/SyncedParametersList"; +import { ACE_ELEMENT_ID } from "metabase/query_builder/components/NativeQueryEditor/constants"; const MockNativeQueryEditor = ({ query, setParameterValue, ...props }) => { const onChange = evt => { @@ -9,7 +10,7 @@ const MockNativeQueryEditor = ({ query, setParameterValue, ...props }) => { }; return ( - <div data-testid="mock-native-query-editor"> + <div data-testid="mock-native-query-editor" id={ACE_ELEMENT_ID}> {query.queryText && ( <textarea value={query.queryText()} onChange={onChange} /> )} -- GitLab