fix: collapse params & query params in action selector if value is not changed (#35323)
This commit is contained in:
parent
5458466a11
commit
a0f2ee10ce
|
|
@ -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",
|
||||
`{{
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
`{{{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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')]";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export type TextViewProps = ViewProps & {
|
|||
additionalAutoComplete?: AdditionalDynamicDataTree;
|
||||
toolTip?: string;
|
||||
dataTreePath?: string | undefined;
|
||||
isValueChanged?: (value: string) => boolean;
|
||||
};
|
||||
|
||||
export type TabViewProps = Omit<ViewProps, "get" | "set"> & SwitcherProps;
|
||||
|
|
|
|||
|
|
@ -20,4 +20,65 @@ describe("Text view component", () => {
|
|||
render(<TextView {...props} />);
|
||||
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(<TextView {...propsQueryParams} />);
|
||||
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(<TextView {...propsQueryParams} />);
|
||||
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(<TextView {...propsParams} />);
|
||||
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(<TextView {...propsParams} />);
|
||||
const queryParamsBodyUpdated =
|
||||
screen.getByText("Params").parentNode?.nextSibling;
|
||||
expect(queryParamsBodyUpdated).toHaveStyle({ display: "flex" });
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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<boolean>(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 (
|
||||
<FieldWrapper className="text-view">
|
||||
<ControlWrapper isAction key={props.label}>
|
||||
<Collapsible isOpen={isDefaultOpenFlag} key={props.label}>
|
||||
<CollapsibleHeader>
|
||||
{props.label && (
|
||||
<label
|
||||
className="!text-gray-600 !text-xs"
|
||||
|
|
@ -62,30 +74,36 @@ export function TextView(props: TextViewProps) {
|
|||
{props.label}
|
||||
</label>
|
||||
)}
|
||||
<InputText
|
||||
additionalAutocomplete={props.additionalAutoComplete}
|
||||
dataTreePath={props.dataTreePath}
|
||||
enableAI={false}
|
||||
evaluatedValue={value}
|
||||
expected={{
|
||||
type: "string",
|
||||
example: props.exampleText,
|
||||
autocompleteDataType: AutocompleteDataType.STRING,
|
||||
openExampleTextByDefault: true,
|
||||
}}
|
||||
label={props.label}
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange={(event: any) => {
|
||||
if (event.target) {
|
||||
props.set(event.target.value, true);
|
||||
} else {
|
||||
props.set(event, true);
|
||||
}
|
||||
}}
|
||||
value={textValue}
|
||||
/>
|
||||
</ControlWrapper>
|
||||
</FieldWrapper>
|
||||
</CollapsibleHeader>
|
||||
<CollapsibleContent>
|
||||
<FieldWrapper className="text-view">
|
||||
<ControlWrapper isAction key={props.label}>
|
||||
<InputText
|
||||
additionalAutocomplete={props.additionalAutoComplete}
|
||||
dataTreePath={props.dataTreePath}
|
||||
enableAI={false}
|
||||
evaluatedValue={value}
|
||||
expected={{
|
||||
type: "string",
|
||||
example: props.exampleText,
|
||||
autocompleteDataType: AutocompleteDataType.STRING,
|
||||
openExampleTextByDefault: true,
|
||||
}}
|
||||
label={props.label}
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange={(event: any) => {
|
||||
if (event.target) {
|
||||
props.set(event.target.value, true);
|
||||
} else {
|
||||
props.set(event, true);
|
||||
}
|
||||
}}
|
||||
value={textValue}
|
||||
/>
|
||||
</ControlWrapper>
|
||||
</FieldWrapper>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user