Skip to content

Commit

Permalink
MAT-7790: add function tab to cql builder (#378)
Browse files Browse the repository at this point in the history
* MAT-7790: add function tab to cql builder

* MAT-7790: add test for function tab
  • Loading branch information
chubert-sb authored Nov 20, 2024
1 parent 473f55f commit 5ac33f6
Show file tree
Hide file tree
Showing 14 changed files with 586 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/AceEditor/madie-ace-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from "../api/useTerminologyServiceApi";
import { Definition } from "../CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder";
import { SelectedLibrary } from "../CqlBuilderPanel/Includes/CqlLibraryDetailsDialog";
import { Funct } from "../CqlBuilderPanel/functionsSection/functionBuilder/FunctionBuilder";

export interface EditorPropsType {
value: string;
Expand All @@ -45,6 +46,7 @@ export interface EditorPropsType {
editedLib: SelectedLibrary
) => void;
handleDeleteLibrary?: (lib: SelectedLibrary) => void;
handleApplyFunction?: (funct: Funct) => void;
parseDebounceTime?: number;
inboundAnnotations?: Ace.Annotation[];
inboundErrorMarkers?: Ace.MarkerLike[];
Expand Down
40 changes: 40 additions & 0 deletions src/CqlBuilderPanel/CqlBuilderPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ jest.mock("@madie/madie-util", () => ({
useFeatureFlags: jest.fn().mockReturnValue({
CQLBuilderIncludes: true,
CQLBuilderDefinitions: true,
CQLBuilderFunctions: true,
QDMValueSetSearch: true,
qdmCodeSearch: true,
}),
Expand All @@ -121,6 +122,9 @@ const props = {
handleDeleteLibrary: jest.fn(),
handleEditLibrary: jest.fn(),
handleDefinitionEdit: jest.fn(),
handleApplyFunction: jest.fn(),
handleParameterEdit: jest.fn(),
handleApplyParameter: jest.fn(),
resetCql: jest.fn(),
getCqlDefinitionReturnTypes: jest.fn(),
makeExpanded: jest.fn(),
Expand Down Expand Up @@ -867,4 +871,40 @@ describe("CqlBuilderPanel", () => {
expect(aceEditor.value).toContain("Some more Text");
});
});

it("Functions tab exists and it's enabled", async () => {
useFeatureFlags.mockImplementationOnce(() => ({
CQLBuilderIncludes: true,
QDMValueSetSearch: true,
CQLBuilderDefinitions: true,
qdmCodeSearch: true,
CQLBuilderParameters: true,
CQLBuilderFunctions: true,
}));
const newProps = { ...props, canEdit: false };
mockedAxios.put.mockResolvedValue({
data: mockCqlBuilderLookUpData,
});
render(<CqlBuilderPanel {...newProps} />);
const parameterTab = await screen.queryByText("Functions");
expect(parameterTab).toBeInTheDocument();
expect(parameterTab).toBeEnabled();
});

it("Functions tab does not exist", async () => {
useFeatureFlags.mockImplementationOnce(() => ({
CQLBuilderIncludes: true,
QDMValueSetSearch: true,
CQLBuilderDefinitions: true,
qdmCodeSearch: true,
CQLBuilderParameters: true,
CQLBuilderFunctions: false,
}));
mockedAxios.put.mockResolvedValue({
data: mockCqlBuilderLookUpData,
});
render(<CqlBuilderPanel {...props} />);
const parameterTab = await screen.queryByText("Functions");
expect(parameterTab).not.toBeInTheDocument();
});
});
19 changes: 17 additions & 2 deletions src/CqlBuilderPanel/CqlBuilderPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import CqlBuilderSectionPanelNavTabs from "./CqlBuilderSectionPanelNavTabs";
import ValueSetsSection from "./ValueSets/ValueSets";
import CodesSection from "./codesSection/CodesSection";
import DefinitionsSection from "./definitionsSection/DefinitionsSection";
import FunctionsSection from "./functionsSection/FunctionsSection";
import { useFeatureFlags } from "@madie/madie-util";
import IncludesTabSection from "./Includes/Includes";
import Parameters from "./Parameters/Parameters";
Expand Down Expand Up @@ -34,13 +35,18 @@ export default function CqlBuilderPanel({
handleApplyDefinition,
handleDefinitionEdit,
handleDefinitionDelete,
handleApplyFunction,
resetCql,
getCqlDefinitionReturnTypes,
makeExpanded,
}) {
const featureFlags = useFeatureFlags();
const { CQLBuilderDefinitions, CQLBuilderIncludes, CQLBuilderParameters } =
featureFlags;
const {
CQLBuilderDefinitions,
CQLBuilderIncludes,
CQLBuilderParameters,
CQLBuilderFunctions,
} = featureFlags;
// we have multiple flags and need to select a starting value based off of what's available and canEdit.
const getStartingPage = (() => {
// if cqlBuilderIncludes -> includes
Expand Down Expand Up @@ -143,6 +149,7 @@ export default function CqlBuilderPanel({
isQDM={measureModel?.includes("QDM")}
CQLBuilderParameters={CQLBuilderParameters}
CQLBuilderIncludes={CQLBuilderIncludes}
CQLBuilderFunctions={CQLBuilderFunctions}
/>
<div
style={{
Expand Down Expand Up @@ -244,6 +251,14 @@ export default function CqlBuilderPanel({
loading={loading}
/>
)}

{activeTab === "functions" && (
<FunctionsSection
canEdit={canEdit}
handleApplyFunction={handleApplyFunction}
loading={loading}
/>
)}
</div>
</div>
</div>
Expand Down
12 changes: 12 additions & 0 deletions src/CqlBuilderPanel/CqlBuilderSectionPanelNavTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface NavTabProps {
CQLBuilderIncludes: boolean;
CQLBuilderParameters: boolean;
CQLBuilderDefinitions: boolean;
CQLBuilderFunctions: boolean;
isQDM: boolean;
}

Expand All @@ -16,6 +17,7 @@ export default function CqlBuilderSectionPanelNavTabs(props: NavTabProps) {
CQLBuilderDefinitions,
CQLBuilderIncludes,
CQLBuilderParameters,
CQLBuilderFunctions,
isQDM,
} = props;

Expand Down Expand Up @@ -78,6 +80,16 @@ export default function CqlBuilderSectionPanelNavTabs(props: NavTabProps) {
data-testid="definitions-tab"
/>
)}
{CQLBuilderFunctions && (
<Tab
tabIndex={0}
aria-label="Functions"
type="D"
value="functions"
label="Functions"
data-testid="functions-tab"
/>
)}
</Tabs>
);
}
39 changes: 39 additions & 0 deletions src/CqlBuilderPanel/common/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import { MadieDialog } from "@madie/madie-design-system/dist/react";
import ErrorIcon from "@mui/icons-material/Error";

const ConfirmationDialog = ({ open, onClose, onSubmit }) => {
return (
<MadieDialog
title="Are you sure?"
dialogProps={{
onClose,
open,
}}
cancelButtonProps={{
variant: "secondary",
cancelText: "Cancel",
"data-testid": "confirmation-cancel-button",
}}
continueButtonProps={{
variant: "cyan",
type: "submit",
"data-testid": "confirmation-clear-button",
continueText: "Clear",
onClick: onSubmit,
}}
>
<div id="discard-changes-dialog-body">
<section className="dialog-warning-body">
<p>You are about to clear this function, including all arguments.</p>
</section>
<section className="dialog-warning-action">
<ErrorIcon />
<p>This action cannot be undone!</p>
</section>
</div>
</MadieDialog>
);
};

export default ConfirmationDialog;
46 changes: 46 additions & 0 deletions src/CqlBuilderPanel/functionsSection/FunctionSectionNavTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react";
import { Tabs, Tab } from "@madie/madie-design-system/dist/react";

export interface NavTabProps {
activeTab: string;
setActiveTab: (value: string) => void;
functionCount: number;
loading: boolean;
}

export default function FunctionSectionNavTabs(props: NavTabProps) {
const { activeTab, setActiveTab, functionCount, loading } = props;

return (
<div style={{ borderBottom: "1px solid #b0b0b0" }} tw="flex flex-row">
<Tabs
id="function-tabs"
value={activeTab}
onChange={(e, v) => {
setActiveTab(v);
}}
type="B"
visibleScrollbar
variant="scrollable"
scrollButtons={false}
>
<Tab
tabIndex={0}
aria-label="Function"
type="B"
label="Function"
data-testid="function-tab"
value="function"
/>
<Tab
tabIndex={0}
aria-label="Saved Functions"
type="B"
label={`Saved Functions ${!loading ? "(" + functionCount + ")" : ""}`}
data-testid="saved-functions-tab"
value="saved-functions"
/>
</Tabs>
</div>
);
}
86 changes: 86 additions & 0 deletions src/CqlBuilderPanel/functionsSection/Functions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#function-form {
font-family: Rubik, sans-serif;
grid-column: span 5 / span 14;
display: flex;
flex-direction: column;
padding-top: 32px;
textArea {
box-sizing: border-box;
border-radius: 3px;
border: solid 1px #8c8c8c;
background-color: #fff;
opacity: 1;
height: 100%;
font-size: 14px;
font-weight: 400;
resize: both;
color: #333;
&::placeholder {
color: #717171;
opacity: 1;
}
}
> .content {
display: flex;
flex-direction: column;
flex-grow: 1;
position: relative;
.subTitle {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
padding-bottom: 8px;
margin-bottom: 33px;
border-bottom: solid 1px;
position: relative;
h2 {
font-size: 24px;
border: none;
margin: 0;
padding: 0;
}
}
h3 {
font-weight: 400;
font-size: 32px;
line-height: 48px;
display: flex;
flex-direction: row;
padding-bottom: 8px;
border-bottom: solid 1px #8c8c8c;
}
label:not(.Mui-disabled) {
font-weight: 500;
font-size: 14px;
line-height: 17px;
color: #333333;
text-transform: none;
margin-bottom: 7px;
}
}
.form-actions {
display: flex;
flex-direction: row;
min-width: 100%;
justify-content: end;
> .cancel-button {
margin-top: 1rem;
margin-right: 32px;
}
> button[type="submit"] {
margin-top: 0;
}
}
.left-box {
width: 40%;
float: left;
}
.right-box {
width: 55%;
font-family: Rubik, sans-serif;
margin-left: 30px;
height: 80%;
float: right;
}
}
44 changes: 44 additions & 0 deletions src/CqlBuilderPanel/functionsSection/FunctionsSection.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as React from "react";
import { render, screen, waitFor, act } from "@testing-library/react";
import FunctionsSection from "./FunctionsSection";
import userEvent from "@testing-library/user-event";

const props = {
canEdit: true,
loading: false,
handleApplyFunction: jest.fn(),
};

describe("FunctionsSection", () => {
it("Should display function section", async () => {
render(<FunctionsSection {...props} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (0)");
expect(funct).toBeInTheDocument();
expect(savedfunctions).toBeInTheDocument();
await waitFor(() => {
expect(funct).toHaveAttribute("aria-selected", "true");
});
await waitFor(() => {
expect(savedfunctions).toHaveAttribute("aria-selected", "false");
});
});

it("Should display saved function section", async () => {
render(<FunctionsSection {...props} />);
const funct = await screen.findByTestId("function-tab");
const savedfunctions = await screen.findByText("Saved Functions (0)");
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");
});
});
});
Loading

0 comments on commit 5ac33f6

Please sign in to comment.