diff --git a/src/CqlBuilderPanel/CqlBuilderPanel.tsx b/src/CqlBuilderPanel/CqlBuilderPanel.tsx index 3511c188..9c82fc26 100644 --- a/src/CqlBuilderPanel/CqlBuilderPanel.tsx +++ b/src/CqlBuilderPanel/CqlBuilderPanel.tsx @@ -256,12 +256,12 @@ export default function CqlBuilderPanel({ {activeTab === "functions" && ( )} diff --git a/src/CqlBuilderPanel/common/utils.tsx b/src/CqlBuilderPanel/common/utils.tsx new file mode 100644 index 00000000..5c9a0040 --- /dev/null +++ b/src/CqlBuilderPanel/common/utils.tsx @@ -0,0 +1,47 @@ +export const formatExpressionName = (values) => { + return values?.type !== "Timing" && values?.type !== "Pre-Defined Functions" + ? values?.type === "Functions" || values?.type === "Fluent Functions" + ? values?.name?.replace(/([\w\s]+)\(\)/g, '"$1"()') + : values?.name.includes(".") + ? values?.name.replace(/(.*\.)(.*)/, '$1"$2"') + : `"${values?.name}"` + : values?.name; +}; + +export const getNewExpressionsAndLines = ( + values: any, + cursorPosition: any, + expressionEditorValue: any, + autoInsert: boolean +) => { + const formattedExpression = formatExpressionName(values); + let editorExpressionValue = expressionEditorValue; + let newCursorPosition = cursorPosition; + + if (cursorPosition && !autoInsert) { + // Insert at cursor position + const { row, column } = cursorPosition; + const lines = expressionEditorValue.split("\n"); + const currentLine = lines[row]; + lines[row] = + currentLine.slice(0, column) + + formattedExpression + + currentLine.slice(column); + editorExpressionValue = lines.join("\n"); + newCursorPosition = { + row, + column: column + formattedExpression.length, + } as unknown; + } else { + // Append to a new line + const lines = editorExpressionValue.split("\n"); + const newLineIndex = lines.length; + editorExpressionValue += + (editorExpressionValue ? "\n" : "") + formattedExpression; + newCursorPosition = { + row: newLineIndex, + column: formattedExpression.length, + }; + } + return [editorExpressionValue, newCursorPosition]; +}; diff --git a/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.test.tsx b/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.test.tsx index 04759c27..0f1c9b34 100644 --- a/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.test.tsx +++ b/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.test.tsx @@ -3,7 +3,8 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { describe, it } from "@jest/globals"; import "@testing-library/jest-dom"; import { within } from "@testing-library/dom"; -import DefinitionBuilder, { formatExpressionName } from "./DefinitionBuilder"; +import DefinitionBuilder from "./DefinitionBuilder"; +import { formatExpressionName } from "../../common/utils"; import { cqlBuilderLookup } from "../../__mocks__/MockCqlBuilderLookupsTypes"; describe("CQL Definition Builder Tests", () => { diff --git a/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.tsx b/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.tsx index a8d47649..250fa29c 100644 --- a/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.tsx +++ b/src/CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder.tsx @@ -11,6 +11,7 @@ import "../Definitions.scss"; import { DefinitionSectionSchemaValidator } from "../../../validations/DefinitionSectionSchemaValidator"; import ExpressionEditor from "../expressionSection/ExpressionEditor"; import { CqlBuilderLookup } from "../../../model/CqlBuilderLookup"; +import { getNewExpressionsAndLines } from "../../common/utils"; export interface Definition { definitionName?: string; @@ -29,16 +30,6 @@ export interface DefinitionProps { onClose?: Function; } -export const formatExpressionName = (values) => { - return values?.type !== "Timing" && values?.type !== "Pre-Defined Functions" - ? values?.type === "Functions" || values?.type === "Fluent Functions" - ? values?.name?.replace(/([\w\s]+)\(\)/g, '"$1"()') - : values?.name.includes(".") - ? values?.name.replace(/(.*\.)(.*)/, '$1"$2"') - : `"${values?.name}"` - : values?.name; -}; - export default function DefinitionBuilder({ canEdit, handleApplyDefinition, @@ -56,58 +47,6 @@ export default function DefinitionBuilder({ ); const [cursorPosition, setCursorPosition] = useState(null); const [autoInsert, setAutoInsert] = useState(false); - - const handleExpressionEditorInsert = (values) => { - const formattedExpression = formatExpressionName(values); - let editorExpressionValue = expressionEditorValue; - let newCursorPosition = cursorPosition; - - if (cursorPosition && !autoInsert) { - // Insert at cursor position - const { row, column } = cursorPosition; - const lines = expressionEditorValue.split("\n"); - const currentLine = lines[row]; - lines[row] = - currentLine.slice(0, column) + - formattedExpression + - currentLine.slice(column); - editorExpressionValue = lines.join("\n"); - newCursorPosition = { - row, - column: column + formattedExpression.length, - } as unknown; - } else { - // Append to a new line - const lines = editorExpressionValue.split("\n"); - const newLineIndex = lines.length; - editorExpressionValue += - (editorExpressionValue ? "\n" : "") + formattedExpression; - newCursorPosition = { - row: newLineIndex, - column: formattedExpression.length, - }; - } - - setExpressionEditorValue(editorExpressionValue); - formik.setFieldValue("type", ""); - formik.setFieldValue("name", ""); - - textAreaRef.current.editor.setValue(editorExpressionValue, 1); - - // set the cursor to the end of the inserted text - textAreaRef.current.editor.moveCursorTo( - newCursorPosition.row, - newCursorPosition.column - ); - textAreaRef.current.editor.clearSelection(); - - // set autoInsert to true for next insertion - setAutoInsert(true); - - // clear cursor position to allow the next item to auto-insert at the end - setCursorPosition(null); - }; - const formik = useFormik({ initialValues: { definitionName: definition?.definitionName || "", @@ -118,9 +57,33 @@ export default function DefinitionBuilder({ validationSchema: DefinitionSectionSchemaValidator, enableReinitialize: true, onSubmit: (values) => { - handleExpressionEditorInsert(values); + const newValues = getNewExpressionsAndLines( + values, + cursorPosition, + expressionEditorValue, + autoInsert + ); + updateExpressionAndLines(newValues[0], newValues[1]); }, }); + + // update formik, and expressionEditor, cursor, lines + const updateExpressionAndLines = ( + newEditorExpressionValue, + newCursorPosition + ) => { + setExpressionEditorValue(newEditorExpressionValue); + formik.setFieldValue("type", ""); + formik.setFieldValue("name", ""); + textAreaRef.current.editor.setValue(newEditorExpressionValue, 1); + textAreaRef.current.editor.moveCursorTo( + newCursorPosition.row, + newCursorPosition.column + ); + textAreaRef.current.editor.clearSelection(); + setAutoInsert(true); + setCursorPosition(null); + }; const { resetForm } = formik; useEffect(() => { diff --git a/src/CqlBuilderPanel/definitionsSection/expressionSection/ExpressionEditor.tsx b/src/CqlBuilderPanel/definitionsSection/expressionSection/ExpressionEditor.tsx index 54d19a34..c841a795 100644 --- a/src/CqlBuilderPanel/definitionsSection/expressionSection/ExpressionEditor.tsx +++ b/src/CqlBuilderPanel/definitionsSection/expressionSection/ExpressionEditor.tsx @@ -33,6 +33,15 @@ interface ExpressionsProps { setAutoInsert: Function; } +export const availableTypes = [ + "Parameters", + "Definitions", + "Functions", + "Fluent Functions", + "Timing", + "Pre-Defined Functions", +]; + export default function ExpressionEditor(props: ExpressionsProps) { const { canEdit, @@ -45,14 +54,6 @@ export default function ExpressionEditor(props: ExpressionsProps) { setAutoInsert, } = props; const [namesOptions, setNamesOptions] = useState([]); - const availableTypes = [ - "Parameters", - "Definitions", - "Functions", - "Fluent Functions", - "Timing", - "Pre-Defined Functions", - ]; const [editorHeight, setEditorHeight] = useState("100px"); const formik: any = useFormikContext(); @@ -95,7 +96,14 @@ export default function ExpressionEditor(props: ExpressionsProps) { useEffect(() => { if (textAreaRef.current) { const lineCount = textAreaRef.current.editor.session.getLength(); - const newHeight = Math.max(lineCount * 20, 100) + "px"; + // newNeight should not exceed 180 + /* + Text entry control (with line numbers, but only starting with 1 line, expandable as more text is added to it, max height is 11 lines and then it scrolls) + https://jira.cms.gov/browse/MAT-7792 + */ + const maxHeight = 180; + const proposedNewHeight = Math.max(lineCount * 20, 100); + const newHeight = Math.min(maxHeight, proposedNewHeight) + "px"; setEditorHeight(newHeight); } }, [expressionEditorValue]); diff --git a/src/CqlBuilderPanel/functionsSection/FunctionsSection.tsx b/src/CqlBuilderPanel/functionsSection/FunctionsSection.tsx index 602df0f3..995a428b 100644 --- a/src/CqlBuilderPanel/functionsSection/FunctionsSection.tsx +++ b/src/CqlBuilderPanel/functionsSection/FunctionsSection.tsx @@ -84,6 +84,7 @@ export default function FunctionsSection({ )} {activeTab === "saved-functions" && ( diff --git a/src/CqlBuilderPanel/functionsSection/argumentSection/ArgumentSection.tsx b/src/CqlBuilderPanel/functionsSection/argumentSection/ArgumentSection.tsx index 04bedf46..f084adf3 100644 --- a/src/CqlBuilderPanel/functionsSection/argumentSection/ArgumentSection.tsx +++ b/src/CqlBuilderPanel/functionsSection/argumentSection/ArgumentSection.tsx @@ -95,11 +95,11 @@ export default function ArgumentSection(props: ArgumentsProps) {