diff --git a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/NavigateTo_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/NavigateTo_spec.ts index 39e3947a3e..443512bbd9 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/NavigateTo_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/NavigateTo_spec.ts @@ -23,6 +23,7 @@ describe("Navigate To feature", { tags: ["@tag.JS"] }, () => { EditorNavigation.SelectEntityByName("Button1", EntityType.Widget); propPane.SelectPlatformFunction("onClick", "Navigate to"); dataSources.ValidateNSelectDropdown("Choose page", "Select page", "Page1"); + agHelper.GetNClick(propPane._actionCollapsibleHeader("Query params")); propPane.UpdatePropertyFieldValue( "Query params", `{{ diff --git a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/uiToCode_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/uiToCode_spec.ts index 9f40f4d769..05aaff090f 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/uiToCode_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/uiToCode_spec.ts @@ -368,6 +368,7 @@ describe("UI to Code", { tags: ["@tag.JS"] }, () => { // Edit the success callback of the nested Api2.run propPane.SelectActionByTitleAndValue("Execute a query", "Api2.run"); + agHelper.GetNClick(propPane._actionCollapsibleHeader("Params")); agHelper.EnterActionValue( "Params", `{{{ diff --git a/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js index 5916976ec2..c0b39cdf07 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Binding/ButtonWidgets_NavigateTo_validation_spec.js @@ -21,6 +21,7 @@ describe( agHelper.GetNClick(propPane._navigateToType("URL")); cy.get("label") .contains("Enter URL") + .parent() .siblings("div") .within(() => { cy.get(".t--code-editor-wrapper").type(testdata.externalPage); diff --git a/app/client/cypress/support/Objects/CommonLocators.ts b/app/client/cypress/support/Objects/CommonLocators.ts index 9314f3fcb0..dd3b874456 100644 --- a/app/client/cypress/support/Objects/CommonLocators.ts +++ b/app/client/cypress/support/Objects/CommonLocators.ts @@ -118,7 +118,9 @@ export class CommonLocators { _actionTextArea = (actionName: string) => "//label[text()='" + actionName + - "']/following-sibling::div//div[contains(@class, 'CodeMirror')]//textarea"; + "']/following-sibling::div//div[contains(@class, 'CodeMirror')]//textarea | //label[text()='" + + actionName + + "']/parent::div/following-sibling::div//div[contains(@class, 'CodeMirror')]//textarea"; _existingDefaultTextInput = ".t--property-control-defaulttext .CodeMirror-code"; _widgetPageIcon = (widgetType: string) => @@ -144,7 +146,9 @@ export class CommonLocators { fieldName.replace(/ +/g, "").toLowerCase() + "')] | //label[text()='" + fieldName + - "']/following-sibling::div"; + "']/following-sibling::div | //label[text()='" + + fieldName + + "']/parent::div/following-sibling::div"; _existingFieldValueByName = (fieldName: string) => this._existingFieldTextByName(fieldName) + "//div[contains(@class,'CodeMirror-code')]"; diff --git a/app/client/cypress/support/Pages/PropertyPane.ts b/app/client/cypress/support/Pages/PropertyPane.ts index bed6d64154..daf36e185d 100644 --- a/app/client/cypress/support/Pages/PropertyPane.ts +++ b/app/client/cypress/support/Pages/PropertyPane.ts @@ -66,10 +66,12 @@ export class PropertyPane { _colorInputField = (option: string) => "//h3[text()='" + option + " Color']//parent::div"; _actionSelectorPopup = ".t--action-selector-popup"; + _actionCollapsibleHeader = (label: string) => + `.ads-v2-collapsible__header:has(label[for="${label}"])`; _actionSelectorFieldByLabel = (label: string) => - `.t--action-selector-popup label[for="${label}"] + div .CodeMirror textarea`; + `.t--action-selector-popup label[for="${label}"] + div .CodeMirror textarea, .t--action-selector-popup .ads-v2-collapsible__header:has(label[for="${label}"]) + div .CodeMirror textarea`; _actionSelectorFieldContentByLabel = (label: string) => - `.t--action-selector-popup label[for="${label}"] + div`; + `.t--action-selector-popup label[for="${label}"] + div, .t--action-selector-popup .ads-v2-collapsible__header:has(label[for="${label}"]) + div`; _actionCardByTitle = (title: string) => `[data-testid='action-card-${title}']`; _actionCallbacks = ".t--action-callbacks"; diff --git a/app/client/src/components/editorComponents/ActionCreator/Field/index.tsx b/app/client/src/components/editorComponents/ActionCreator/Field/index.tsx index 2d5d971c92..c6af8d6c70 100644 --- a/app/client/src/components/editorComponents/ActionCreator/Field/index.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/Field/index.tsx @@ -161,8 +161,8 @@ export function Field(props: FieldProps) { }); break; case FieldType.PARAMS_FIELD: - viewElement = (view as (props: SelectorViewProps) => JSX.Element)({ - options: options as TreeDropdownOption[], + viewElement = (view as (props: TextViewProps) => JSX.Element)({ + exampleText: exampleText, label: label, get: (value: string) => getterFunction(value, props.field.position), set: (value: string | DropdownOption) => { @@ -174,7 +174,9 @@ export function Field(props: FieldProps) { props.onValueChange(finalValueToSet, false); }, value: value, - defaultText: defaultText, + isValueChanged: (value: string) => { + return value !== getterFunction(""); + }, }); break; case FieldType.KEY_VALUE_FIELD: @@ -190,12 +192,31 @@ export function Field(props: FieldProps) { defaultText: defaultText, }); break; + case FieldType.QUERY_PARAMS_FIELD: + viewElement = (view as (props: TextViewProps) => JSX.Element)({ + label: label, + toolTip: toolTip, + exampleText: exampleText, + get: getterFunction, + set: (value: string | DropdownOption, isUpdatedViaKeyboard = false) => { + const finalValueToSet = fieldConfig.setter(value, props.value); + props.onValueChange(finalValueToSet, isUpdatedViaKeyboard, true); + }, + value: value, + additionalAutoComplete: props.additionalAutoComplete, + dataTreePath: props.dataTreePath, + isValueChanged: (value: string) => { + // This function checks whether the param value is changed from the default value. + // getterFunction("") -> passing empty string will return the default value + return value !== getterFunction(""); + }, + }); + break; case FieldType.ALERT_TEXT_FIELD: case FieldType.URL_FIELD: case FieldType.KEY_TEXT_FIELD_STORE_VALUE: case FieldType.KEY_TEXT_FIELD_REMOVE_VALUE: case FieldType.VALUE_TEXT_FIELD: - case FieldType.QUERY_PARAMS_FIELD: case FieldType.DOWNLOAD_DATA_FIELD: case FieldType.DOWNLOAD_FILE_NAME_FIELD: case FieldType.COPY_TEXT_FIELD: diff --git a/app/client/src/components/editorComponents/ActionCreator/types.ts b/app/client/src/components/editorComponents/ActionCreator/types.ts index 7eeea55ab1..24240abb9d 100644 --- a/app/client/src/components/editorComponents/ActionCreator/types.ts +++ b/app/client/src/components/editorComponents/ActionCreator/types.ts @@ -61,6 +61,7 @@ export type TextViewProps = ViewProps & { additionalAutoComplete?: AdditionalDynamicDataTree; toolTip?: string; dataTreePath?: string | undefined; + isValueChanged?: (value: string) => boolean; }; export type TabViewProps = Omit & SwitcherProps; diff --git a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/TextView.test.tsx b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/TextView.test.tsx index 023669c0f4..edbcc4a4d7 100644 --- a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/TextView.test.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/TextView.test.tsx @@ -20,4 +20,65 @@ describe("Text view component", () => { render(); expect(screen.getByTestId("text-view-label")).toHaveTextContent("Key"); }); + + const propsQueryParams = { + dataTreePath: "Button1.onClick", + exampleText: "navigateTo('Page1', { a: 1 }, 'SAME_WINDOW')", + get: () => { + return ""; + }, + set: () => { + return 1; + }, + isValueChanged: () => { + return false; + }, + label: "Query params", + value: "{{navigateTo('', {}, 'SAME_WINDOW');}}", + }; + + test("Renders Text view with Query Params field collapsed for navigateTo action", () => { + render(); + const queryParamsBody = + screen.getByText("Query params").parentNode?.nextSibling; + expect(queryParamsBody).toHaveStyle({ display: "none" }); + }); + + test("Renders Text view with Query Params field expanded for navigateTo action", () => { + propsQueryParams.isValueChanged = () => true; + render(); + const queryParamsBodyUpdated = + screen.getByText("Query params").parentNode?.nextSibling; + expect(queryParamsBodyUpdated).toHaveStyle({ display: "flex" }); + }); + + const propsParams = { + dataTreePath: "Button1.onClick", + exampleText: "Api1.run({ a: 1 })", + get: () => { + return ""; + }, + set: () => { + return 1; + }, + isValueChanged: () => { + return false; + }, + label: "Params", + value: "{{Api1.run();}}", + }; + + test("Renders Text view with Params field collapsed for Query action selected", () => { + render(); + const queryParamsBody = screen.getByText("Params").parentNode?.nextSibling; + expect(queryParamsBody).toHaveStyle({ display: "none" }); + }); + test("Renders Text view with Params field expanded for Query action selected", () => { + propsParams.isValueChanged = () => true; + + render(); + const queryParamsBodyUpdated = + screen.getByText("Params").parentNode?.nextSibling; + expect(queryParamsBodyUpdated).toHaveStyle({ display: "flex" }); + }); }); diff --git a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx index f2729680ee..b6b05f00b0 100644 --- a/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/viewComponents/TextView/index.tsx @@ -5,7 +5,7 @@ import { } from "components/propertyControls/StyledControls"; import { InputText } from "components/propertyControls/InputTextControl"; import { AutocompleteDataType } from "utils/autocomplete/AutocompleteDataType"; -import React, { useEffect, useMemo } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { getCodeFromMoustache } from "../../utils"; import { useDispatch, useSelector } from "react-redux"; import { EVAL_WORKER_ACTIONS } from "@appsmith/workers/Evaluation/evalWorkerActions"; @@ -15,10 +15,16 @@ import { evaluateActionSelectorField, } from "actions/actionSelectorActions"; import { isFunctionPresent } from "@shared/ast"; +import { + Collapsible, + CollapsibleContent, + CollapsibleHeader, +} from "design-system"; export function TextView(props: TextViewProps) { const id = useMemo(() => generateReactKey(), []); const textValue = props.get(props.value, props.index, false) as string; + const [isDefaultOpenFlag, setIsDefaultOpenFlag] = useState(true); const dispatch = useDispatch(); @@ -50,9 +56,15 @@ export function TextView(props: TextViewProps) { const value = evaluatedCodeValue?.value || codeWithoutMoustache; + useEffect(() => { + if (typeof props.isValueChanged === "function") { + setIsDefaultOpenFlag(props.isValueChanged(textValue)); + } + }, []); + return ( - - + + {props.label && ( - + + + + + { + if (event.target) { + props.set(event.target.value, true); + } else { + props.set(event, true); + } + }} + value={textValue} + /> + + + + ); }