diff --git a/src/AceEditor/madie-ace-editor.tsx b/src/AceEditor/madie-ace-editor.tsx index 2eb17a9c..a81a9078 100644 --- a/src/AceEditor/madie-ace-editor.tsx +++ b/src/AceEditor/madie-ace-editor.tsx @@ -20,6 +20,7 @@ import "./madie-custom.css"; import { ParsedCql, Statement } from "../model/ParsedCql"; import { CqlMetaData, + Parameter, ValueSetForSearch, } from "../api/useTerminologyServiceApi"; import { Definition } from "../CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder"; @@ -29,6 +30,7 @@ export interface EditorPropsType { value: string; onChange?: (value: string) => void; handleApplyCode?: (code: string) => void; + handleApplyParameter?: (parameter: Parameter) => void; handleApplyValueSet?: (vs: ValueSetForSearch) => void; handleApplyDefinition?: (def: Definition) => void; handleDefinitionEdit?: (lib: SelectedLibrary, def: Definition) => void; diff --git a/src/CqlBuilderPanel/CqlBuilderPanel.test.tsx b/src/CqlBuilderPanel/CqlBuilderPanel.test.tsx index 3f219d53..8bb107c0 100644 --- a/src/CqlBuilderPanel/CqlBuilderPanel.test.tsx +++ b/src/CqlBuilderPanel/CqlBuilderPanel.test.tsx @@ -662,7 +662,7 @@ describe("CqlBuilderPanel", () => { expect(parameterTab).not.toBeEnabled(); }); - it("Parameters works", async () => { + it("Parameters clear works", async () => { useFeatureFlags.mockImplementationOnce(() => ({ CQLBuilderIncludes: true, QDMValueSetSearch: true, @@ -725,4 +725,146 @@ describe("CqlBuilderPanel", () => { expect(aceEditor.value).toContain(""); }); }); + it("Parameters apply works and clears input fields", async () => { + useFeatureFlags.mockImplementationOnce(() => ({ + CQLBuilderIncludes: true, + QDMValueSetSearch: true, + CQLBuilderDefinitions: true, + qdmCodeSearch: true, + CQLBuilderParameters: true, + })); + mockedAxios.put.mockResolvedValue({ + data: mockCqlBuilderLookUpData, + }); + + const applyParameter = jest.fn(); + applyParameter.mockReturnValue("success"); + const copiedProps = { ...props, handleApplyParameter: applyParameter }; + let result = render(); + const parameterTab = await screen.queryByText("Parameters"); + expect(parameterTab).toBeInTheDocument(); + userEvent.click(screen.getByRole("tab", { name: "Parameters" })); + expect(screen.getByTestId("cql-editor-parameters")).toBeInTheDocument(); + + await userEvent.click(screen.getByTestId("saved-parameters-tab")); + + await waitFor(() => { + expect(screen.getByTestId("saved-parameters-tab")).toHaveAttribute( + "aria-selected", + "true" + ); + }); + expect(screen.getByTestId("saved-parameters")).toBeInTheDocument(); + // switch back + await userEvent.click(screen.getByTestId("parameter-tab")); + await waitFor(() => { + expect(screen.getByTestId("parameter-tab")).toHaveAttribute( + "aria-selected", + "true" + ); + }); + // write some stuff in parameter name + const parameterInput = screen.getByTestId( + "parameter-name-input" + ) as HTMLInputElement; + userEvent.type(parameterInput, "SomeText"); + expect(parameterInput.value).toBe("SomeText"); + // we now see expression edtior + const editor = screen.getByTestId( + "terminology-section-sub-header-content-Expression Editor" + ); + expect(editor).toBeVisible(); + + //paste into editor, check it's there + const editorValue = "Some more Text"; + let aceEditor: any = await result.container.querySelector( + "#ace-editor-wrapper textarea" + ); + userEvent.paste(aceEditor, editorValue); + aceEditor = await result.container.querySelector( + "#ace-editor-wrapper textarea" + ); + expect(aceEditor.value).toContain(editorValue); + // check that clear does anything + userEvent.click(getByTestId("apply-parameter-btn")); + await waitFor(() => { + expect(applyParameter).toHaveBeenCalledWith({ + expression: "Some more Text", + parameterName: "SomeText", + }); + expect(parameterInput.value).toBe(""); + expect(aceEditor.value).toContain(""); + }); + }); + it("Parameters apply did not work, fields did not get updated", async () => { + useFeatureFlags.mockImplementationOnce(() => ({ + CQLBuilderIncludes: true, + QDMValueSetSearch: true, + CQLBuilderDefinitions: true, + qdmCodeSearch: true, + CQLBuilderParameters: true, + })); + mockedAxios.put.mockResolvedValue({ + data: mockCqlBuilderLookUpData, + }); + + const applyParameter = jest.fn(); + applyParameter.mockReturnValue("failure"); + const copiedProps = { ...props, handleApplyParameter: applyParameter }; + let result = render(); + const parameterTab = await screen.queryByText("Parameters"); + expect(parameterTab).toBeInTheDocument(); + userEvent.click(screen.getByRole("tab", { name: "Parameters" })); + expect(screen.getByTestId("cql-editor-parameters")).toBeInTheDocument(); + + await userEvent.click(screen.getByTestId("saved-parameters-tab")); + + await waitFor(() => { + expect(screen.getByTestId("saved-parameters-tab")).toHaveAttribute( + "aria-selected", + "true" + ); + }); + expect(screen.getByTestId("saved-parameters")).toBeInTheDocument(); + // switch back + await userEvent.click(screen.getByTestId("parameter-tab")); + await waitFor(() => { + expect(screen.getByTestId("parameter-tab")).toHaveAttribute( + "aria-selected", + "true" + ); + }); + // write some stuff in parameter name + const parameterInput = screen.getByTestId( + "parameter-name-input" + ) as HTMLInputElement; + userEvent.type(parameterInput, "SomeText"); + expect(parameterInput.value).toBe("SomeText"); + // we now see expression edtior + const editor = screen.getByTestId( + "terminology-section-sub-header-content-Expression Editor" + ); + expect(editor).toBeVisible(); + + //paste into editor, check it's there + const editorValue = "Some more Text"; + let aceEditor: any = await result.container.querySelector( + "#ace-editor-wrapper textarea" + ); + userEvent.paste(aceEditor, editorValue); + aceEditor = await result.container.querySelector( + "#ace-editor-wrapper textarea" + ); + expect(aceEditor.value).toContain(editorValue); + // check that clear does anything + userEvent.click(getByTestId("apply-parameter-btn")); + await waitFor(() => { + expect(applyParameter).toHaveBeenCalledWith({ + expression: "Some more Text", + parameterName: "SomeText", + }); + expect(parameterInput.value).toContain("SomeText"); + expect(aceEditor.value).toContain("Some more Text"); + }); + }); }); diff --git a/src/CqlBuilderPanel/CqlBuilderPanel.tsx b/src/CqlBuilderPanel/CqlBuilderPanel.tsx index a607881e..c4a08356 100644 --- a/src/CqlBuilderPanel/CqlBuilderPanel.tsx +++ b/src/CqlBuilderPanel/CqlBuilderPanel.tsx @@ -27,6 +27,7 @@ export default function CqlBuilderPanel({ handleDeleteLibrary, handleEditLibrary, handleApplyCode, + handleApplyParameter, handleApplyValueSet, handleApplyDefinition, handleDefinitionEdit, @@ -219,7 +220,9 @@ export default function CqlBuilderPanel({ handleApplyCode={handleApplyCode} /> )} - {activeTab === "parameters" && } + {activeTab === "parameters" && ( + + )} {activeTab === "definitions" && ( {}, + onSubmit: async (values) => { + const result = handleApplyParameter(values); + if (result === "success") { + formik.resetForm(); + } + }, }); const { resetForm } = formik; // adjusting the height of the editor based on the inserted text @@ -36,7 +41,7 @@ export default function ParameterPane() { const newHeight = Math.max(lineCount * 20, 100) + "px"; setEditorHeight(newHeight); } - }, [formik.values.expressionEditorValue]); + }, [formik.values.expression]); return ( <>
@@ -65,9 +70,9 @@ export default function ParameterPane() { mode="sql" ref={textAreaRef} theme="monokai" - value={formik.values.expressionEditorValue} + value={formik.values.expression} onChange={(value) => { - formik.setFieldValue("expressionEditorValue", value); + formik.setFieldValue("expression", value); }} onLoad={(aceEditor) => { // On load we want to tell the ace editor that it's inside of a scrollabel page @@ -93,8 +98,9 @@ export default function ParameterPane() { Clear diff --git a/src/CqlBuilderPanel/Parameters/Parameters.tsx b/src/CqlBuilderPanel/Parameters/Parameters.tsx index 56b9c672..58c03e12 100644 --- a/src/CqlBuilderPanel/Parameters/Parameters.tsx +++ b/src/CqlBuilderPanel/Parameters/Parameters.tsx @@ -3,14 +3,16 @@ import ParametersNavTabs from "./ParamatersNavTabs"; import ParameterPane from "./ParameterPane"; import "./Parameters.scss"; -export default function Parameters() { +export default function Parameters({ handleApplyParameter }) { const [activeTab, setActiveTab] = useState("parameters"); return (
- {activeTab === "parameters" && } + {activeTab === "parameters" && ( + + )} {activeTab === "savedParameters" && (
)} diff --git a/src/api/useTerminologyServiceApi.ts b/src/api/useTerminologyServiceApi.ts index d4fcab6a..f542ad56 100644 --- a/src/api/useTerminologyServiceApi.ts +++ b/src/api/useTerminologyServiceApi.ts @@ -24,6 +24,11 @@ export type ValueSet = { errorMsg: string; }; +export type Parameter = { + parameterName?: string; + expression?: string; +}; + export interface CodeSystem { id: string; lastUpdated: string; diff --git a/src/cqlEditorWithTerminology/CqlEditorWithTerminology.tsx b/src/cqlEditorWithTerminology/CqlEditorWithTerminology.tsx index 78d517ee..73d883de 100644 --- a/src/cqlEditorWithTerminology/CqlEditorWithTerminology.tsx +++ b/src/cqlEditorWithTerminology/CqlEditorWithTerminology.tsx @@ -14,6 +14,7 @@ const CqlEditorWithTerminology = ({ handleCodeDelete, handleDefinitionDelete, handleApplyCode, + handleApplyParameter, handleApplyValueSet, handleApplyDefinition, handleApplyLibrary, @@ -106,6 +107,7 @@ const CqlEditorWithTerminology = ({ setIsCQLUnchanged={setIsCQLUnchanged} isCQLUnchanged={isCQLUnchanged} handleApplyCode={handleApplyCode} + handleApplyParameter={handleApplyParameter} handleApplyValueSet={handleApplyValueSet} handleApplyDefinition={handleApplyDefinition} handleDefinitionEdit={handleDefinitionEdit}