Skip to content

Commit

Permalink
Merge branch 'develop' into MAT-7935
Browse files Browse the repository at this point in the history
  • Loading branch information
ethankaplan authored Dec 18, 2024
2 parents cedb73e + 7167993 commit 14ce8d3
Show file tree
Hide file tree
Showing 14 changed files with 795 additions and 44 deletions.
33 changes: 19 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@madie/cql-antlr-parser": "^1.0.8",
"@madie/cql-antlr-parser": "^1.0.9",
"@madie/madie-design-system": "^1.2.39",
"@material-ui/core": "^4.12.4",
"@mui/icons-material": "^5.5.1",
Expand Down
1 change: 1 addition & 0 deletions src/AceEditor/madie-ace-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export interface EditorPropsType {
) => void;
handleDeleteLibrary?: (lib: SelectedLibrary) => void;
handleApplyFunction?: (funct: Funct) => void;
handleFunctionDelete?: (funct: any) => void;
parseDebounceTime?: number;
inboundAnnotations?: Ace.Annotation[];
inboundErrorMarkers?: Ace.MarkerLike[];
Expand Down
3 changes: 3 additions & 0 deletions src/CqlBuilderPanel/CqlBuilderPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default function CqlBuilderPanel({
handleDefinitionEdit,
handleDefinitionDelete,
handleApplyFunction,
handleFunctionDelete,
resetCql,
getCqlDefinitionReturnTypes,
makeExpanded,
Expand Down Expand Up @@ -244,9 +245,11 @@ export default function CqlBuilderPanel({
cqlBuilderLookupsTypes={cqlBuilderLookupsTypes}
canEdit={canEdit}
handleApplyFunction={handleApplyFunction}
handleFunctionDelete={handleFunctionDelete}
loading={loading}
cql={measureStoreCql}
isCQLUnchanged={isCQLUnchanged}
resetCql={resetCql}
/>
)}
</div>
Expand Down
28 changes: 28 additions & 0 deletions src/CqlBuilderPanel/functionsSection/EditFunctionDialog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import { parseArgumentsFromLogicString } from "./EditFunctionDialog";

describe("parseArgumentsFromLogicString", () => {
test("Can parse out empty arguments", () => {
const logicString = `define function "Empty Arguments" (): true`;
const result = parseArgumentsFromLogicString(logicString);
expect(result).toEqual([]);
});
test("Can parse out multiple arguments", () => {
const logicString2 = `define function "Function name here" (arg1 "Integer", arg2 "Integer", arg3 "Date"):\n true`;
const result = parseArgumentsFromLogicString(logicString2);

expect(result).toEqual([
{ argumentName: "arg1", dataType: "Integer" },
{ argumentName: "arg2", dataType: "Integer" },
{ argumentName: "arg3", dataType: "Date" },
]);
});
test("Can parse out arguments with commas embedded in dataType", () => {
const logicString3 = `define fluent function "Numerator Observation"(Encounter "Encounter, Performed" ):
duration in hours of Encounter.relevantPeriod`;
const result = parseArgumentsFromLogicString(logicString3);
expect(result).toEqual([
{ argumentName: "Encounter", dataType: "Encounter, Performed" },
]);
});
});
89 changes: 89 additions & 0 deletions src/CqlBuilderPanel/functionsSection/EditFunctionDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from "react";
import { MadieDialog } from "@madie/madie-design-system/dist/react";
import FunctionBuilder from "./functionBuilder/FunctionBuilder";

interface PropTypes {
open: boolean;
onClose: () => void;
cqlBuilderLookupsTypes?: any;
funct?: any;
setEditFunctionDialogOpen: Function;
handleApplyFunction: Function;
handleFunctionEdit: Function;
}

export const parseArgumentsFromLogicString = (logicString) => {
// `s` flag for multiline content
const argumentListRegex = /\(([^)]*)\)/s;
const match = logicString.match(argumentListRegex);

// Nobody in parenthesis
if (!match) {
return [];
}

const argumentsString = match[1].trim();
// no args
if (!argumentsString) {
return [];
}

// Regex to match argument and data type pairs
const argumentRegex = /([\w]+)\s+"([^"]+)"/g;
const results = [];

let argumentMatch;
while ((argumentMatch = argumentRegex.exec(argumentsString)) !== null) {
const [, argumentName, dataType] = argumentMatch;
results.push({ argumentName, dataType });
}
return results;
};

const EditFunctionDialog = ({
funct,
handleFunctionEdit,
onClose,
open,
setEditFunctionDialogOpen,
cqlBuilderLookupsTypes,
handleApplyFunction,
}: PropTypes) => {
// a property is passed called argument names that does not seem to work for anything with commas.
// the following is regex to grab arguments and dataTypes using a matcher and add them to the function to edit.

const updatedFunction = {
...funct,
fluentFunction: funct?.isFluent === true ? true : false,
functionsArguments: parseArgumentsFromLogicString(
funct?.logic ? funct?.logic : ""
),
expressionEditorValue: funct?.logic,
};

return (
<MadieDialog
title="Edit"
dialogProps={{
open,
onClose: onClose,
fullWidth: true,
maxWidth: "md",
"data-testid": "edit-parameter-dialog",
}}
>
<FunctionBuilder
cqlBuilderLookupsTypes={cqlBuilderLookupsTypes}
canEdit={true}
handleApplyFunction={handleApplyFunction}
funct={updatedFunction}
operation="edit"
handleFunctionEdit={handleFunctionEdit}
onClose={onClose}
setEditFunctionDialogOpen={setEditFunctionDialogOpen}
/>
</MadieDialog>
);
};

export default EditFunctionDialog;
115 changes: 115 additions & 0 deletions src/CqlBuilderPanel/functionsSection/FunctionsSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import userEvent from "@testing-library/user-event";
import { mockMeasureStoreCql } from "../__mocks__/MockMeasureStoreCql";
import { cqlBuilderLookup } from "../__mocks__/MockCqlBuilderLookupsTypes";

const resetCql = jest.fn();

const props = {
canEdit: true,
loading: false,
handleApplyFunction: jest.fn(),
cql: mockMeasureStoreCql,
isCQLUnchanged: false,
cqlBuilderLookupsTypes: cqlBuilderLookup,
resetCql,
};

describe("FunctionsSection", () => {
Expand Down Expand Up @@ -62,4 +65,116 @@ describe("FunctionsSection", () => {
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
});

it("Should open a confirmation dialog on click", async () => {
render(<FunctionsSection {...props} isCQLUnchanged={false} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (2)");
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
await waitFor(() => {
expect(funct).toHaveAttribute("aria-selected", "true");
});
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "false");
});
userEvent.click(savedfunctions);
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "true");
});
const editButon0 = screen.getByTestId("edit-button-0");
userEvent.click(editButon0);
expect(screen.getByTestId("discard-dialog")).toBeInTheDocument();
expect(screen.getByText("You have unsaved changes.")).toBeInTheDocument();
const cancelBtn = screen.getByTestId("discard-dialog-cancel-button");
const discardBtn = screen.getByTestId("discard-dialog-continue-button");
expect(cancelBtn).toBeInTheDocument();
expect(discardBtn).toBeInTheDocument();

userEvent.click(discardBtn);
await waitFor(() => {
expect(screen.getByText("Edit")).toBeInTheDocument();
});
});

it("Should open edit dialog on click", async () => {
render(<FunctionsSection {...props} isCQLUnchanged={true} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (2)");
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
await waitFor(() => {
expect(funct).toHaveAttribute("aria-selected", "true");
});
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "false");
});
userEvent.click(savedfunctions);
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "true");
});
const editButon0 = screen.getByTestId("edit-button-0");
userEvent.click(editButon0);
await waitFor(() => {
expect(screen.getByText("Edit")).toBeInTheDocument();
});
});

it("Should close discard dialog on click", async () => {
render(<FunctionsSection {...props} isCQLUnchanged={false} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (2)");
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
await waitFor(() => {
expect(funct).toHaveAttribute("aria-selected", "true");
});
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "false");
});
userEvent.click(savedfunctions);
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "true");
});
const editButon0 = screen.getByTestId("edit-button-0");
userEvent.click(editButon0);
expect(screen.getByTestId("discard-dialog")).toBeInTheDocument();
expect(screen.getByText("You have unsaved changes.")).toBeInTheDocument();
const cancelBtn = screen.getByTestId("discard-dialog-cancel-button");
expect(cancelBtn).toBeInTheDocument();
userEvent.click(cancelBtn);

await waitFor(() => {
expect(screen.queryByTestId("discard-dialog")).not.toBeInTheDocument();
});
});

it("Should close edit dialog on click", async () => {
render(<FunctionsSection {...props} isCQLUnchanged={true} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (2)");
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
await waitFor(() => {
expect(funct).toHaveAttribute("aria-selected", "true");
});
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "false");
});
userEvent.click(savedfunctions);
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "true");
});
const editButon0 = screen.getByTestId("edit-button-0");
userEvent.click(editButon0);
await waitFor(() => {
expect(screen.getByText("Edit")).toBeInTheDocument();
});
const closeButton = screen.getByRole("button", { name: "Close" });
expect(closeButton).toBeInTheDocument();
userEvent.click(closeButton);
await waitFor(() => {
expect(screen.queryByTestId("discard-dialog")).not.toBeInTheDocument();
});
});
});
Loading

0 comments on commit 14ce8d3

Please sign in to comment.