chore: add new condition for form conditionals (#38556)

## Description
Added an extra condition for form evaluation. This allows server to
render different components for application or any other editors.

Form configs can now use this as a conditional to show or hide
components `editorContextType === "PAGE"`. Valid values are `WORKFLOWS`,
`PAGE` or `MODULE`.

Fixes #37735 

## Automation

/ok-to-test tags="@tag.Sanity, @tag.IDE"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/13294166183>
> Commit: 6ab73336517e1ad6491ae3e63a5387cd65112a7b
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13294166183&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Sanity, @tag.IDE`
> Spec:
> <hr>Wed, 12 Feb 2025 21:15:04 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced context-aware evaluation actions to improve dynamic form
interactions.
  
- **Refactor**
- Streamlined the form evaluation workflow and standardized key
extraction for enhanced consistency and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Ayush Pahwa 2025-02-14 17:02:30 +07:00 committed by GitHub
parent 260765520e
commit 2782994c7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 86 additions and 55 deletions

View File

@ -2,8 +2,6 @@ import type { ReduxAction } from "./ReduxActionTypes";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { intersection } from "lodash";
import type { DependencyMap } from "utils/DynamicBindingUtils";
import type { QueryActionConfig } from "entities/Action";
import type { DatasourceConfiguration } from "entities/Datasource";
import type { DiffWithNewTreeState } from "workers/Evaluation/helpers";
import {
EVALUATE_REDUX_ACTIONS,
@ -81,46 +79,6 @@ export const setDependencyMap = (
};
};
// Called when a form is being setup, for setting up the base condition evaluations for the form
export const initFormEvaluations = (
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
editorConfig: any,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
settingConfig: any,
formId: string,
) => {
return {
type: ReduxActionTypes.INIT_FORM_EVALUATION,
payload: { editorConfig, settingConfig, formId },
};
};
// Called when there is change in the data of the form, re evaluates the whole form
export const startFormEvaluations = (
formId: string,
formData: QueryActionConfig,
datasourceId: string,
pluginId: string,
actionDiffPath?: string,
hasRouteChanged?: boolean,
datasourceConfiguration?: DatasourceConfiguration,
) => {
return {
type: ReduxActionTypes.RUN_FORM_EVALUATION,
payload: {
formId,
actionConfiguration: formData,
datasourceId,
pluginId,
actionDiffPath,
hasRouteChanged,
datasourceConfiguration,
},
};
};
// These actions require the entire tree to be re-evaluated
const FORCE_EVAL_ACTIONS = {
[ReduxActionTypes.INSTALL_LIBRARY_SUCCESS]: true,

View File

@ -0,0 +1,46 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import type { QueryActionConfig } from "entities/Action";
import type { DatasourceConfiguration } from "entities/Datasource";
import type { ActionParentEntityTypeInterface } from "ee/entities/Engine/actionHelpers";
// Called when a form is being setup, for setting up the base condition evaluations for the form
export const initFormEvaluations = (
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
editorConfig: any,
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
settingConfig: any,
formId: string,
) => {
return {
type: ReduxActionTypes.INIT_FORM_EVALUATION,
payload: { editorConfig, settingConfig, formId },
};
};
// Called when there is change in the data of the form, re evaluates the whole form
export const startFormEvaluations = (
formId: string,
formData: QueryActionConfig,
datasourceId: string,
pluginId: string,
editorContextType?: ActionParentEntityTypeInterface,
actionDiffPath?: string,
hasRouteChanged?: boolean,
datasourceConfiguration?: DatasourceConfiguration,
) => {
return {
type: ReduxActionTypes.RUN_FORM_EVALUATION,
payload: {
formId,
actionConfiguration: formData,
datasourceId,
pluginId,
editorContextType,
actionDiffPath,
hasRouteChanged,
datasourceConfiguration,
},
};
};

View File

@ -37,7 +37,10 @@ import {
import type { DatasourceConfiguration } from "entities/Datasource";
import { buffers } from "redux-saga";
import type { Plugin } from "entities/Plugin";
import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
import {
doesPluginRequireDatasource,
type ActionParentEntityTypeInterface,
} from "ee/entities/Engine/actionHelpers";
import { klonaLiteWithTelemetry } from "utils/helpers";
import { objectKeys } from "@appsmith/utils";
@ -45,6 +48,7 @@ export interface FormEvalActionPayload {
formId: string;
datasourceId?: string;
pluginId?: string;
editorContextType: ActionParentEntityTypeInterface;
actionConfiguration?: ActionConfig;
editorConfig?: FormConfigType[];
settingConfig?: FormConfigType[];

View File

@ -49,7 +49,7 @@ import get from "lodash/get";
import {
initFormEvaluations,
startFormEvaluations,
} from "actions/evaluationActions";
} from "actions/formEvaluationActions";
import { updateReplayEntity } from "actions/pageActions";
import { ENTITY_TYPE } from "ee/entities/AppsmithConsole/utils";
import type { EventLocation } from "ee/utils/analyticsUtilTypes";
@ -85,9 +85,13 @@ import {
getCurrentApplicationIdForCreateNewApp,
} from "ee/selectors/applicationSelectors";
import { TEMP_DATASOURCE_ID } from "constants/Datasource";
import { doesPluginRequireDatasource } from "ee/entities/Engine/actionHelpers";
import {
ActionParentEntityType,
doesPluginRequireDatasource,
} from "ee/entities/Engine/actionHelpers";
import { convertToBasePageIdSelector } from "selectors/pageListSelectors";
import { openGeneratePageModalWithSelectedDS } from "../utils/GeneratePageUtils";
import { objectKeys } from "@appsmith/utils";
// Called whenever the query being edited is changed via the URL or query pane
function* changeQuerySaga(actionPayload: ReduxAction<ChangeQueryPayload>) {
@ -187,6 +191,7 @@ function* changeQuerySaga(actionPayload: ReduxAction<ChangeQueryPayload>) {
//@ts-expect-error: id does not exists
action.datasource.id,
pluginId,
action.contextType,
),
);
}
@ -275,7 +280,7 @@ function* formValueChangeSaga(
type: ReduxActionTypes.SET_TRIGGER_VALUES_LOADING,
payload: {
formId: values.id,
keys: Object.keys(allTriggers),
keys: objectKeys(allTriggers),
value: true,
},
});
@ -317,7 +322,7 @@ function* formValueChangeSaga(
"datasourceConfiguration",
)
) {
currentEnvironment = Object.keys(datasourceStorages)[0];
currentEnvironment = objectKeys(datasourceStorages)[0];
}
let dsConfig = {
@ -338,6 +343,7 @@ function* formValueChangeSaga(
values.actionConfiguration,
values.datasource.id,
values.pluginId,
values.contextType || ActionParentEntityType.PAGE,
field,
hasRouteChanged,
dsConfig,

View File

@ -76,12 +76,13 @@ import {
import { AppThemingMode } from "selectors/appThemingSelectors";
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import { startFormEvaluations } from "actions/evaluationActions";
import { startFormEvaluations } from "actions/formEvaluationActions";
import { getUIComponent } from "pages/Editor/QueryEditor/helpers";
import { type Plugin, UIComponentTypes } from "entities/Plugin";
import { getCurrentEnvironmentId } from "ee/selectors/environmentSelectors";
import { updateAndSaveAnvilLayout } from "layoutSystems/anvil/utils/anvilChecksUtils";
import type { ReplayOperation } from "entities/Replay/ReplayEntity/ReplayOperations";
import { objectKeys } from "@appsmith/utils";
export interface UndoRedoPayload {
operation: ReplayOperation;
@ -103,7 +104,7 @@ export default function* undoRedoListenerSaga() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function* openPropertyPaneSaga(replay: any) {
try {
const replayWidgetId = Object.keys(replay.widgets)[0];
const replayWidgetId = objectKeys(replay.widgets)[0] as string;
if (!replayWidgetId || !replay.widgets[replayWidgetId].propertyUpdates)
return;
@ -155,9 +156,9 @@ export function* postUndoRedoSaga(replay: any) {
processUndoRedoToasts(replay.toasts);
}
if (!replay.widgets || Object.keys(replay.widgets).length <= 0) return;
if (!replay.widgets || objectKeys(replay.widgets).length <= 0) return;
const widgetIds = Object.keys(replay.widgets);
const widgetIds = objectKeys(replay.widgets) as string[];
yield put(selectWidgetInitAction(SelectionRequestType.Multiple, widgetIds));
scrollWidgetIntoView(widgetIds[0]);
@ -357,6 +358,7 @@ function* replayActionSaga(
replayEntity.actionConfiguration,
replayEntity.datasource.id || "",
replayEntity.pluginId,
replayEntity.contextType,
u.modifiedProperty,
true,
datasource?.datasourceStorages[currentEnvironment]

View File

@ -15,6 +15,11 @@ import { extractEvalConfigFromFormConfig } from "components/formControls/utils";
import { isDynamicValue } from "utils/DynamicBindingUtils";
import { isTrueObject } from "ee/workers/Evaluation/evaluationUtils";
import type { DatasourceConfiguration } from "entities/Datasource";
import { objectKeys } from "@appsmith/utils";
import {
ActionParentEntityType,
type ActionParentEntityTypeInterface,
} from "ee/entities/Engine/actionHelpers";
export enum ConditionType {
HIDE = "hide", // When set, the component will be shown until condition is true
@ -71,7 +76,7 @@ const generateInitialEvalState = (formConfig: FormConfigType) => {
// Any element is only added to the eval state if they have a conditional statement present, if not they are allowed to be rendered
if ("conditionals" in formConfig && !!formConfig.conditionals) {
const allConditionTypes = Object.keys(formConfig.conditionals);
const allConditionTypes = objectKeys(formConfig.conditionals);
if (
allConditionTypes.includes(ConditionType.HIDE) ||
@ -306,12 +311,15 @@ function evaluateDynamicValuesConfig(
}
function evaluateFormConfigElements(
/* eslint-disable @typescript-eslint/no-unused-vars */
actionConfiguration: ActionConfig,
config: FormConfigEvalObject,
/* eslint-disable @typescript-eslint/no-unused-vars */
editorContextType: ActionParentEntityTypeInterface,
/* eslint-disable @typescript-eslint/no-unused-vars */
datasourceConfiguration?: DatasourceConfiguration,
) {
const paths = Object.keys(config);
const paths = objectKeys(config);
if (paths.length > 0) {
paths.forEach((path) => {
@ -332,17 +340,18 @@ function evaluateFormConfigElements(
function evaluate(
actionConfiguration: ActionConfig,
currentEvalState: FormEvalOutput,
editorContextType: ActionParentEntityTypeInterface,
actionDiffPath?: string,
hasRouteChanged?: boolean,
datasourceConfiguration?: DatasourceConfiguration,
) {
Object.keys(currentEvalState).forEach((key: string) => {
objectKeys(currentEvalState).forEach((key: string) => {
try {
if (currentEvalState[key].hasOwnProperty("conditionals")) {
const conditionBlock = currentEvalState[key].conditionals;
if (!!conditionBlock) {
Object.keys(conditionBlock).forEach((conditionType: string) => {
objectKeys(conditionBlock).forEach((conditionType: string) => {
const output = eval(conditionBlock[conditionType]);
if (conditionType === ConditionType.HIDE) {
@ -431,6 +440,7 @@ function evaluate(
currentEvalState[key]
.evaluateFormConfig as EvaluatedFormConfig
).evaluateFormConfigObject,
editorContextType,
datasourceConfiguration,
);
}
@ -448,6 +458,7 @@ function getFormEvaluation(
formId: string,
actionConfiguration: ActionConfig,
currentEvalState: FormEvaluationState,
editorContextType: ActionParentEntityTypeInterface,
actionDiffPath?: string,
hasRouteChanged?: boolean,
datasourceConfiguration?: DatasourceConfiguration,
@ -502,6 +513,7 @@ function getFormEvaluation(
conditionToBeEvaluated = evaluate(
actionConfiguration,
currentEvalState[formId],
editorContextType,
actionDiffPath,
hasRouteChanged,
datasourceConfiguration,
@ -514,6 +526,7 @@ function getFormEvaluation(
conditionToBeEvaluated = evaluate(
actionConfiguration,
conditionToBeEvaluated,
editorContextType,
actionDiffPath,
hasRouteChanged,
datasourceConfiguration,
@ -572,6 +585,7 @@ export function setFormEvaluationSaga(
actionConfiguration,
actionDiffPath,
datasourceConfiguration,
editorContextType,
formId,
hasRouteChanged,
} = payload;
@ -584,6 +598,7 @@ export function setFormEvaluationSaga(
formId,
actionConfiguration,
currentEvalState,
editorContextType || ActionParentEntityType.PAGE,
actionDiffPath,
hasRouteChanged,
datasourceConfiguration,