fix: reset widget to default value after setter method (#29151)
## Description This PR ensures that widgets are reset to their default value after the `setValue` setter method is used to set its value #### PR fixes following issue(s) Fixes #27119 #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [ ] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Implemented a new reset functionality for widgets, allowing users to revert to default values after changes. - Enhanced widget meta updates with a new reset action. - **Bug Fixes** - Added test cases to ensure widget reset functionality works as expected, even after asynchronous operations. - **Refactor** - Refactored evaluation logic to improve handling of widget meta updates and resets. - Improved action execution logic for resetting widget properties. - **Tests** - Expanded end-to-end regression tests to cover new reset widget functionality. - **Documentation** - Updated internal documentation to reflect new action types and evaluation processes related to widget meta updates. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Druthi Polisetty <druthi@appsmith.com>
This commit is contained in:
parent
40e7e8b535
commit
95fa2328a8
|
|
@ -0,0 +1,83 @@
|
||||||
|
import {
|
||||||
|
entityExplorer,
|
||||||
|
propPane,
|
||||||
|
draggableWidgets,
|
||||||
|
agHelper,
|
||||||
|
deployMode,
|
||||||
|
locators,
|
||||||
|
jsEditor,
|
||||||
|
} from "../../../../support/Objects/ObjectsCore";
|
||||||
|
|
||||||
|
describe("Reset widget action", () => {
|
||||||
|
it("Reset widget to default after setValue has been applied", () => {
|
||||||
|
entityExplorer.DragDropWidgetNVerify(draggableWidgets.INPUT_V2);
|
||||||
|
propPane.UpdatePropertyFieldValue("Default value", "John");
|
||||||
|
|
||||||
|
entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 300, 300);
|
||||||
|
propPane.EnterJSContext("onClick", `{{Input1.setValue('Hello!')}}`);
|
||||||
|
propPane.UpdatePropertyFieldValue("Label", "Set value");
|
||||||
|
|
||||||
|
entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 500, 100);
|
||||||
|
propPane.EnterJSContext("onClick", `{{resetWidget("Input1")}}`);
|
||||||
|
propPane.UpdatePropertyFieldValue("Label", "Reset value");
|
||||||
|
|
||||||
|
deployMode.DeployApp();
|
||||||
|
|
||||||
|
agHelper.ClickButton("Set value");
|
||||||
|
agHelper.AssertText(
|
||||||
|
locators._widgetInDeployed(draggableWidgets.INPUT_V2) + " input",
|
||||||
|
"val",
|
||||||
|
"Hello!",
|
||||||
|
);
|
||||||
|
|
||||||
|
agHelper.ClickButton("Reset value");
|
||||||
|
agHelper.AssertText(
|
||||||
|
locators._widgetInDeployed(draggableWidgets.INPUT_V2) + " input",
|
||||||
|
"val",
|
||||||
|
"John",
|
||||||
|
);
|
||||||
|
|
||||||
|
agHelper.ClearNType(
|
||||||
|
locators._widgetInDeployed(draggableWidgets.INPUT_V2) + " input",
|
||||||
|
"Testing",
|
||||||
|
);
|
||||||
|
agHelper.ClickButton("Reset value");
|
||||||
|
agHelper.AssertText(
|
||||||
|
locators._widgetInDeployed(draggableWidgets.INPUT_V2) + " input",
|
||||||
|
"val",
|
||||||
|
"John",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("Reset value is accessible after 'awaiting'", () => {
|
||||||
|
deployMode.NavigateBacktoEditor();
|
||||||
|
agHelper.ClearNType(locators._input, "Meta Text");
|
||||||
|
|
||||||
|
const JS_OBJECT_BODY = `export default {
|
||||||
|
async resetInputWithoutAwait () {
|
||||||
|
resetWidget('Input1')
|
||||||
|
showAlert(Input1.text)
|
||||||
|
},
|
||||||
|
async resetInputWithAwait () {
|
||||||
|
await resetWidget('Input1')
|
||||||
|
showAlert(Input1.text)
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
// Create js object
|
||||||
|
jsEditor.CreateJSObject(JS_OBJECT_BODY, {
|
||||||
|
paste: true,
|
||||||
|
completeReplace: true,
|
||||||
|
toRun: false,
|
||||||
|
prettify: false,
|
||||||
|
shouldCreateNewJSObj: true,
|
||||||
|
});
|
||||||
|
agHelper.Sleep(4000);
|
||||||
|
jsEditor.SelectFunctionDropdown("resetInputWithoutAwait");
|
||||||
|
agHelper.ClickButton("Run");
|
||||||
|
agHelper.AssertContains("Meta Text");
|
||||||
|
|
||||||
|
jsEditor.SelectFunctionDropdown("resetInputWithAwait");
|
||||||
|
agHelper.ClickButton("Run");
|
||||||
|
agHelper.AssertContains("John");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -55,6 +55,18 @@ export const resetWidgetMetaProperty = (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resetWidgetMetaUpdates = (
|
||||||
|
evalMetaUpdates: EvalMetaUpdates,
|
||||||
|
): BatchAction<ResetWidgetMetaPayload> => {
|
||||||
|
return batchAction({
|
||||||
|
type: ReduxActionTypes.RESET_WIDGET_META_UPDATES,
|
||||||
|
payload: {
|
||||||
|
evalMetaUpdates,
|
||||||
|
},
|
||||||
|
postEvalActions: [{ type: ReduxActionTypes.RESET_WIDGET_META_EVALUATED }],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const resetChildrenMetaProperty = (
|
export const resetChildrenMetaProperty = (
|
||||||
widgetId: string,
|
widgetId: string,
|
||||||
): ReduxAction<{ widgetId: string }> => {
|
): ReduxAction<{ widgetId: string }> => {
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ export const EVALUATE_REDUX_ACTIONS = [
|
||||||
ReduxActionTypes.SET_META_PROP_AND_EVAL,
|
ReduxActionTypes.SET_META_PROP_AND_EVAL,
|
||||||
ReduxActionTypes.META_UPDATE_DEBOUNCED_EVAL,
|
ReduxActionTypes.META_UPDATE_DEBOUNCED_EVAL,
|
||||||
ReduxActionTypes.RESET_WIDGET_META,
|
ReduxActionTypes.RESET_WIDGET_META,
|
||||||
|
ReduxActionTypes.RESET_WIDGET_META_UPDATES,
|
||||||
// Batches
|
// Batches
|
||||||
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
|
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
|
||||||
// App Theme
|
// App Theme
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ const ActionTypes = {
|
||||||
META_UPDATE_DEBOUNCED_EVAL: "META_UPDATE_DEBOUNCED_EVAL",
|
META_UPDATE_DEBOUNCED_EVAL: "META_UPDATE_DEBOUNCED_EVAL",
|
||||||
RESET_CHILDREN_WIDGET_META: "RESET_CHILDREN_WIDGET_META",
|
RESET_CHILDREN_WIDGET_META: "RESET_CHILDREN_WIDGET_META",
|
||||||
RESET_WIDGET_META: "RESET_WIDGET_META",
|
RESET_WIDGET_META: "RESET_WIDGET_META",
|
||||||
|
RESET_WIDGET_META_UPDATES: "RESET_WIDGET_META_UPDATES",
|
||||||
RESET_WIDGET_META_EVALUATED: "RESET_WIDGET_META_EVALUATED",
|
RESET_WIDGET_META_EVALUATED: "RESET_WIDGET_META_EVALUATED",
|
||||||
RESET_WIDGETS_META_STATE: "RESET_WIDGETS_META_STATE",
|
RESET_WIDGETS_META_STATE: "RESET_WIDGETS_META_STATE",
|
||||||
UPDATE_WIDGET_NAME_INIT: "UPDATE_WIDGET_NAME_INIT",
|
UPDATE_WIDGET_NAME_INIT: "UPDATE_WIDGET_NAME_INIT",
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,6 @@ export const generateDataTreeWidget = (
|
||||||
|
|
||||||
// overridingMetaProps maps properties that can be overriden by either default values or meta changes to initial values.
|
// overridingMetaProps maps properties that can be overriden by either default values or meta changes to initial values.
|
||||||
// initial value is set to metaProps value or defaultMetaProps value.
|
// initial value is set to metaProps value or defaultMetaProps value.
|
||||||
|
|
||||||
Object.entries(defaultMetaProps).forEach(([key, value]) => {
|
Object.entries(defaultMetaProps).forEach(([key, value]) => {
|
||||||
if (overridingMetaPropsMap[key]) {
|
if (overridingMetaPropsMap[key]) {
|
||||||
overridingMetaProps[key] =
|
overridingMetaProps[key] =
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,11 @@ import {
|
||||||
} from "@appsmith/constants/ReduxActionConstants";
|
} from "@appsmith/constants/ReduxActionConstants";
|
||||||
import produce from "immer";
|
import produce from "immer";
|
||||||
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
|
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
|
||||||
import { getMetaWidgetResetObj } from "./metaReducerUtils";
|
import {
|
||||||
|
getMetaWidgetResetObj,
|
||||||
|
getNextMetaStateWithUpdates,
|
||||||
|
setMetaValuesOnResetFromEval,
|
||||||
|
} from "./metaReducerUtils";
|
||||||
import type { WidgetEntityConfig } from "@appsmith/entities/DataTree/types";
|
import type { WidgetEntityConfig } from "@appsmith/entities/DataTree/types";
|
||||||
|
|
||||||
export type WidgetMetaState = Record<string, unknown>;
|
export type WidgetMetaState = Record<string, unknown>;
|
||||||
|
|
@ -28,18 +32,7 @@ export const metaReducer = createReducer(initialState, {
|
||||||
evalMetaUpdates: EvalMetaUpdates;
|
evalMetaUpdates: EvalMetaUpdates;
|
||||||
}>,
|
}>,
|
||||||
) => {
|
) => {
|
||||||
const { evalMetaUpdates } = action.payload;
|
return getNextMetaStateWithUpdates(state, action);
|
||||||
|
|
||||||
if (!evalMetaUpdates.length) return state;
|
|
||||||
|
|
||||||
// if metaObject is updated in dataTree we also update meta values, to keep meta state in sync.
|
|
||||||
const newMetaState = produce(state, (draftMetaState) => {
|
|
||||||
evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => {
|
|
||||||
set(draftMetaState, [widgetId, ...metaPropertyPath], value);
|
|
||||||
});
|
|
||||||
return draftMetaState;
|
|
||||||
});
|
|
||||||
return newMetaState;
|
|
||||||
},
|
},
|
||||||
[ReduxActionTypes.SET_META_PROP]: (
|
[ReduxActionTypes.SET_META_PROP]: (
|
||||||
state: MetaState,
|
state: MetaState,
|
||||||
|
|
@ -132,6 +125,14 @@ export const metaReducer = createReducer(initialState, {
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
[ReduxActionTypes.RESET_WIDGET_META_UPDATES]: (
|
||||||
|
state: MetaState,
|
||||||
|
action: ReduxAction<{
|
||||||
|
evalMetaUpdates: EvalMetaUpdates;
|
||||||
|
}>,
|
||||||
|
) => {
|
||||||
|
return setMetaValuesOnResetFromEval(state, action);
|
||||||
|
},
|
||||||
[ReduxActionTypes.RESET_WIDGETS_META_STATE]: (
|
[ReduxActionTypes.RESET_WIDGETS_META_STATE]: (
|
||||||
state: MetaState,
|
state: MetaState,
|
||||||
action: ReduxAction<{ widgetIdsToClear: string[] }>,
|
action: ReduxAction<{ widgetIdsToClear: string[] }>,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@ import type {
|
||||||
PropertyOverrideDependency,
|
PropertyOverrideDependency,
|
||||||
} from "@appsmith/entities/DataTree/types";
|
} from "@appsmith/entities/DataTree/types";
|
||||||
import { klona } from "klona";
|
import { klona } from "klona";
|
||||||
import type { WidgetMetaState } from ".";
|
import type { MetaState, WidgetMetaState } from ".";
|
||||||
|
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
||||||
|
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
|
||||||
|
import produce from "immer";
|
||||||
|
import { set, unset } from "lodash";
|
||||||
|
|
||||||
export function getMetaWidgetResetObj(
|
export function getMetaWidgetResetObj(
|
||||||
evaluatedWidget: WidgetEntity | undefined,
|
evaluatedWidget: WidgetEntity | undefined,
|
||||||
|
|
@ -30,3 +34,50 @@ export function getMetaWidgetResetObj(
|
||||||
}
|
}
|
||||||
return resetMetaObj;
|
return resetMetaObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When resetWidget is called from eval, we update all the meta values and remove those meta values which are undefined
|
||||||
|
*/
|
||||||
|
export function setMetaValuesOnResetFromEval(
|
||||||
|
state: MetaState,
|
||||||
|
action: ReduxAction<{
|
||||||
|
evalMetaUpdates: EvalMetaUpdates;
|
||||||
|
}>,
|
||||||
|
) {
|
||||||
|
const { evalMetaUpdates } = action.payload;
|
||||||
|
|
||||||
|
if (!evalMetaUpdates.length) return state;
|
||||||
|
|
||||||
|
const newMetaState = klona(state);
|
||||||
|
|
||||||
|
evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
unset(newMetaState, `${widgetId}.${metaPropertyPath.join(".")}`);
|
||||||
|
} else {
|
||||||
|
set(newMetaState, [widgetId, ...metaPropertyPath], value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newMetaState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNextMetaStateWithUpdates(
|
||||||
|
state: MetaState,
|
||||||
|
action: ReduxAction<{
|
||||||
|
evalMetaUpdates: EvalMetaUpdates;
|
||||||
|
}>,
|
||||||
|
) {
|
||||||
|
const { evalMetaUpdates } = action.payload;
|
||||||
|
|
||||||
|
if (!evalMetaUpdates.length) return state;
|
||||||
|
|
||||||
|
// if metaObject is updated in dataTree we also update meta values, to keep meta state in sync.
|
||||||
|
const newMetaState = produce(state, (draftMetaState) => {
|
||||||
|
evalMetaUpdates.forEach(({ metaPropertyPath, value, widgetId }) => {
|
||||||
|
set(draftMetaState, [widgetId, ...metaPropertyPath], value);
|
||||||
|
});
|
||||||
|
return draftMetaState;
|
||||||
|
});
|
||||||
|
|
||||||
|
return newMetaState;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import { put, select, take } from "redux-saga/effects";
|
import { put, select, take } from "redux-saga/effects";
|
||||||
import { getWidgetByName } from "sagas/selectors";
|
import { getWidgetByName } from "sagas/selectors";
|
||||||
import {
|
import { resetWidgetMetaUpdates } from "actions/metaActions";
|
||||||
resetChildrenMetaProperty,
|
|
||||||
resetWidgetMetaProperty,
|
|
||||||
} from "actions/metaActions";
|
|
||||||
import AppsmithConsole from "utils/AppsmithConsole";
|
|
||||||
import {
|
import {
|
||||||
ActionValidationError,
|
ActionValidationError,
|
||||||
TriggerFailureError,
|
TriggerFailureError,
|
||||||
|
|
@ -12,20 +9,15 @@ import {
|
||||||
import { getType, Types } from "utils/TypeHelpers";
|
import { getType, Types } from "utils/TypeHelpers";
|
||||||
import type { FlattenedWidgetProps } from "WidgetProvider/constants";
|
import type { FlattenedWidgetProps } from "WidgetProvider/constants";
|
||||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
import { getDataTree, getConfigTree } from "selectors/dataTreeSelectors";
|
|
||||||
import type {
|
|
||||||
WidgetEntity,
|
|
||||||
WidgetEntityConfig,
|
|
||||||
} from "@appsmith/entities/DataTree/types";
|
|
||||||
import type { DataTree, ConfigTree } from "entities/DataTree/dataTreeTypes";
|
|
||||||
import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils";
|
|
||||||
import type { TResetWidgetDescription } from "workers/Evaluation/fns/resetWidget";
|
import type { TResetWidgetDescription } from "workers/Evaluation/fns/resetWidget";
|
||||||
|
import AppsmithConsole from "utils/AppsmithConsole";
|
||||||
|
|
||||||
export default function* resetWidgetActionSaga(
|
export default function* resetWidgetActionSaga(
|
||||||
action: TResetWidgetDescription,
|
action: TResetWidgetDescription,
|
||||||
) {
|
) {
|
||||||
const { payload } = action;
|
const { payload } = action;
|
||||||
const { widgetName } = payload;
|
const { metaUpdates, widgetName } = payload;
|
||||||
|
|
||||||
if (getType(widgetName) !== Types.STRING) {
|
if (getType(widgetName) !== Types.STRING) {
|
||||||
throw new ActionValidationError(
|
throw new ActionValidationError(
|
||||||
"RESET_WIDGET_META_RECURSIVE_BY_NAME",
|
"RESET_WIDGET_META_RECURSIVE_BY_NAME",
|
||||||
|
|
@ -34,8 +26,6 @@ export default function* resetWidgetActionSaga(
|
||||||
getType(widgetName),
|
getType(widgetName),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const dataTree: DataTree = yield select(getDataTree);
|
|
||||||
const configTree: ConfigTree = yield select(getConfigTree);
|
|
||||||
|
|
||||||
const widget: FlattenedWidgetProps | undefined = yield select(
|
const widget: FlattenedWidgetProps | undefined = yield select(
|
||||||
getWidgetByName,
|
getWidgetByName,
|
||||||
|
|
@ -44,23 +34,10 @@ export default function* resetWidgetActionSaga(
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
throw new TriggerFailureError(`Widget ${payload.widgetName} not found`);
|
throw new TriggerFailureError(`Widget ${payload.widgetName} not found`);
|
||||||
}
|
}
|
||||||
const evaluatedEntity = dataTree[widget.widgetName];
|
|
||||||
const evaluatedEntityConfig = configTree[widget.widgetName];
|
yield put(resetWidgetMetaUpdates(metaUpdates));
|
||||||
if (isWidget(evaluatedEntity)) {
|
|
||||||
yield put(
|
|
||||||
resetWidgetMetaProperty(
|
|
||||||
widget.widgetId,
|
|
||||||
evaluatedEntity as WidgetEntity,
|
|
||||||
evaluatedEntityConfig as WidgetEntityConfig,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (payload.resetChildren) {
|
|
||||||
yield put(resetChildrenMetaProperty(widget.widgetId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
yield take(ReduxActionTypes.RESET_WIDGET_META_EVALUATED);
|
yield take(ReduxActionTypes.RESET_WIDGET_META_EVALUATED);
|
||||||
|
|
||||||
AppsmithConsole.info({
|
AppsmithConsole.info({
|
||||||
text: `resetWidget('${payload.widgetName}', ${payload.resetChildren}) was triggered`,
|
text: `resetWidget('${payload.widgetName}', ${payload.resetChildren}) was triggered`,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ const BATCH_PRIORITY = {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
needsSaga: false,
|
needsSaga: false,
|
||||||
},
|
},
|
||||||
|
[ReduxActionTypes.RESET_WIDGET_META_UPDATES]: {
|
||||||
|
priority: 0,
|
||||||
|
needsSaga: false,
|
||||||
|
},
|
||||||
[ReduxActionTypes.UPDATE_WIDGET_PROPERTY]: {
|
[ReduxActionTypes.UPDATE_WIDGET_PROPERTY]: {
|
||||||
priority: 0,
|
priority: 0,
|
||||||
needsSaga: false,
|
needsSaga: false,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import {
|
||||||
getDataTree,
|
getDataTree,
|
||||||
getUnevaluatedDataTree,
|
getUnevaluatedDataTree,
|
||||||
} from "selectors/dataTreeSelectors";
|
} from "selectors/dataTreeSelectors";
|
||||||
import { getMetaWidgets, getWidgets } from "sagas/selectors";
|
import { getMetaWidgets, getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||||
import type { WidgetTypeConfigMap } from "WidgetProvider/factory";
|
import type { WidgetTypeConfigMap } from "WidgetProvider/factory";
|
||||||
import WidgetFactory from "WidgetProvider/factory";
|
import WidgetFactory from "WidgetProvider/factory";
|
||||||
import { GracefulWorkerService } from "utils/WorkerUtil";
|
import { GracefulWorkerService } from "utils/WorkerUtil";
|
||||||
|
|
@ -260,6 +260,8 @@ export function* evaluateTreeSaga(
|
||||||
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
PerformanceTransactionName.DATA_TREE_EVALUATION,
|
||||||
);
|
);
|
||||||
const appMode: ReturnType<typeof getAppMode> = yield select(getAppMode);
|
const appMode: ReturnType<typeof getAppMode> = yield select(getAppMode);
|
||||||
|
const widgetsMeta: ReturnType<typeof getWidgetsMeta> =
|
||||||
|
yield select(getWidgetsMeta);
|
||||||
|
|
||||||
const evalTreeRequestData: EvalTreeRequestData = {
|
const evalTreeRequestData: EvalTreeRequestData = {
|
||||||
unevalTree: unEvalAndConfigTree,
|
unevalTree: unEvalAndConfigTree,
|
||||||
|
|
@ -271,6 +273,7 @@ export function* evaluateTreeSaga(
|
||||||
forceEvaluation,
|
forceEvaluation,
|
||||||
metaWidgets,
|
metaWidgets,
|
||||||
appMode,
|
appMode,
|
||||||
|
widgetsMeta,
|
||||||
};
|
};
|
||||||
|
|
||||||
const workerResponse: EvalTreeResponseData = yield call(
|
const workerResponse: EvalTreeResponseData = yield call(
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,6 @@ describe("Add functions", () => {
|
||||||
it("resetWidget works", () => {
|
it("resetWidget works", () => {
|
||||||
const widgetName = "widget1";
|
const widgetName = "widget1";
|
||||||
const resetChildren = true;
|
const resetChildren = true;
|
||||||
|
|
||||||
expect(evalContext.resetWidget(widgetName, resetChildren)).resolves.toBe(
|
expect(evalContext.resetWidget(widgetName, resetChildren)).resolves.toBe(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
@ -323,6 +322,7 @@ describe("Add functions", () => {
|
||||||
payload: {
|
payload: {
|
||||||
widgetName,
|
widgetName,
|
||||||
resetChildren,
|
resetChildren,
|
||||||
|
metaUpdates: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
eventType: undefined,
|
eventType: undefined,
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,284 @@
|
||||||
import { promisify } from "./utils/Promisify";
|
import { promisify } from "./utils/Promisify";
|
||||||
|
|
||||||
function resetWidgetFnDescriptor(widgetName: string, resetChildren = true) {
|
import type { FlattenedWidgetProps } from "WidgetProvider/constants";
|
||||||
|
import {
|
||||||
|
canvasWidgets,
|
||||||
|
dataTreeEvaluator,
|
||||||
|
canvasWidgetsMeta,
|
||||||
|
} from "../handlers/evalTree";
|
||||||
|
import _ from "lodash";
|
||||||
|
import type {
|
||||||
|
WidgetEntityConfig,
|
||||||
|
WidgetEntity,
|
||||||
|
} from "@appsmith/entities/DataTree/types";
|
||||||
|
import { isWidget } from "@appsmith/workers/Evaluation/evaluationUtils";
|
||||||
|
import { klona } from "klona";
|
||||||
|
import { getDynamicBindings, isDynamicValue } from "utils/DynamicBindingUtils";
|
||||||
|
import evaluateSync from "../evaluate";
|
||||||
|
import type { DescendantWidgetMap } from "sagas/WidgetOperationUtils";
|
||||||
|
import type { MetaState } from "reducers/entityReducers/metaReducer";
|
||||||
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||||
|
import type { EvalMetaUpdates } from "@appsmith/workers/common/DataTreeEvaluator/types";
|
||||||
|
import type { DataTree } from "entities/DataTree/dataTreeTypes";
|
||||||
|
import { validateAndParseWidgetProperty } from "workers/common/DataTreeEvaluator/validationUtils";
|
||||||
|
|
||||||
|
function resetWidgetFnDescriptor(
|
||||||
|
widgetName: string,
|
||||||
|
resetChildren = true,
|
||||||
|
metaUpdates: EvalMetaUpdates,
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
type: "RESET_WIDGET_META_RECURSIVE_BY_NAME" as const,
|
type: "RESET_WIDGET_META_RECURSIVE_BY_NAME" as const,
|
||||||
payload: { widgetName, resetChildren },
|
payload: { widgetName, resetChildren, metaUpdates },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TResetWidgetArgs = Parameters<typeof resetWidgetFnDescriptor>;
|
|
||||||
export type TResetWidgetDescription = ReturnType<
|
export type TResetWidgetDescription = ReturnType<
|
||||||
typeof resetWidgetFnDescriptor
|
typeof resetWidgetFnDescriptor
|
||||||
>;
|
>;
|
||||||
export type TResetWidgetActionType = TResetWidgetDescription["type"];
|
export type TResetWidgetActionType = TResetWidgetDescription["type"];
|
||||||
|
|
||||||
async function resetWidget(
|
async function resetWidget(
|
||||||
...args: Parameters<typeof resetWidgetFnDescriptor>
|
...args: [widgetName: string, resetChildren: boolean]
|
||||||
) {
|
) {
|
||||||
return promisify(resetWidgetFnDescriptor)(...args);
|
const widgetName = args[0];
|
||||||
|
const resetChildren = args[1] || true;
|
||||||
|
const metaUpdates: EvalMetaUpdates = [];
|
||||||
|
const updatedProperties: string[][] = [];
|
||||||
|
|
||||||
|
resetWidgetMetaProperty(
|
||||||
|
widgetName,
|
||||||
|
resetChildren,
|
||||||
|
metaUpdates,
|
||||||
|
updatedProperties,
|
||||||
|
);
|
||||||
|
|
||||||
|
return promisify(resetWidgetFnDescriptor)(
|
||||||
|
widgetName,
|
||||||
|
resetChildren,
|
||||||
|
metaUpdates,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetWidgetMetaProperty(
|
||||||
|
widgetName: string,
|
||||||
|
resetChildren = true,
|
||||||
|
evalMetaUpdates: EvalMetaUpdates,
|
||||||
|
updatedProperties: string[][],
|
||||||
|
) {
|
||||||
|
const widget: FlattenedWidgetProps | undefined = _.find(
|
||||||
|
Object.values(canvasWidgets || {}),
|
||||||
|
(widget) => widget.widgetName === widgetName,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!dataTreeEvaluator || !widget) return;
|
||||||
|
|
||||||
|
const evalTree = dataTreeEvaluator.getEvalTree();
|
||||||
|
const oldUnEvalTree = dataTreeEvaluator.getOldUnevalTree();
|
||||||
|
const configTree = dataTreeEvaluator.getConfigTree();
|
||||||
|
const evalProps = dataTreeEvaluator.getEvalProps();
|
||||||
|
const evalPathsIdenticalToState =
|
||||||
|
dataTreeEvaluator.getEvalPathsIdenticalToState();
|
||||||
|
|
||||||
|
const evaluatedEntity = evalTree[widget.widgetName];
|
||||||
|
const evaluatedEntityConfig = configTree[
|
||||||
|
widget.widgetName
|
||||||
|
] as WidgetEntityConfig;
|
||||||
|
|
||||||
|
if (evaluatedEntity && isWidget(evaluatedEntity)) {
|
||||||
|
const metaObj = evaluatedEntity.meta;
|
||||||
|
const currentMetaProperties = Object.keys(metaObj);
|
||||||
|
const { propertyOverrideDependency } = evaluatedEntityConfig;
|
||||||
|
|
||||||
|
for (const propertyPath of currentMetaProperties) {
|
||||||
|
const defaultPropertyPath =
|
||||||
|
propertyOverrideDependency[propertyPath]?.DEFAULT;
|
||||||
|
if (defaultPropertyPath) {
|
||||||
|
const unEvalEntity = oldUnEvalTree[widget.widgetName] as WidgetEntity;
|
||||||
|
const expressionToEvaluate: string = unEvalEntity[defaultPropertyPath];
|
||||||
|
|
||||||
|
let finalValue: unknown;
|
||||||
|
if (
|
||||||
|
expressionToEvaluate &&
|
||||||
|
typeof expressionToEvaluate === "string" &&
|
||||||
|
isDynamicValue(expressionToEvaluate)
|
||||||
|
) {
|
||||||
|
const { jsSnippets } = getDynamicBindings(expressionToEvaluate);
|
||||||
|
const { result } = evaluateSync(
|
||||||
|
jsSnippets[0] || expressionToEvaluate,
|
||||||
|
evalTree,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
undefined,
|
||||||
|
configTree,
|
||||||
|
);
|
||||||
|
|
||||||
|
finalValue = klona(result);
|
||||||
|
} else {
|
||||||
|
finalValue = klona(expressionToEvaluate);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedValue = validateAndParseWidgetProperty({
|
||||||
|
fullPropertyPath: `${widget.widgetName}.${defaultPropertyPath}`,
|
||||||
|
widget: unEvalEntity,
|
||||||
|
configTree,
|
||||||
|
evalPropertyValue: finalValue,
|
||||||
|
unEvalPropertyValue: expressionToEvaluate,
|
||||||
|
evalProps,
|
||||||
|
evalPathsIdenticalToState,
|
||||||
|
});
|
||||||
|
|
||||||
|
evalMetaUpdates.push({
|
||||||
|
widgetId: evaluatedEntity.widgetId,
|
||||||
|
metaPropertyPath: propertyPath.split("."),
|
||||||
|
value: parsedValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
evalMetaUpdates.push({
|
||||||
|
widgetId: evaluatedEntity.widgetId,
|
||||||
|
metaPropertyPath: propertyPath.split("."),
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resetChildren) {
|
||||||
|
resetChildrenMetaProperty(
|
||||||
|
widget.widgetId,
|
||||||
|
evalTree,
|
||||||
|
evalMetaUpdates,
|
||||||
|
updatedProperties,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortWidgetsMetaByParent(widgetsMeta: MetaState, parentId: string) {
|
||||||
|
return _.reduce(
|
||||||
|
widgetsMeta,
|
||||||
|
function (
|
||||||
|
result: {
|
||||||
|
childrenWidgetsMeta: MetaState;
|
||||||
|
otherWidgetsMeta: MetaState;
|
||||||
|
},
|
||||||
|
currentWidgetMeta,
|
||||||
|
key,
|
||||||
|
) {
|
||||||
|
return key.startsWith(parentId + "_")
|
||||||
|
? {
|
||||||
|
...result,
|
||||||
|
childrenWidgetsMeta: {
|
||||||
|
...result.childrenWidgetsMeta,
|
||||||
|
[key]: currentWidgetMeta,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...result,
|
||||||
|
otherWidgetsMeta: {
|
||||||
|
...result.otherWidgetsMeta,
|
||||||
|
[key]: currentWidgetMeta,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
childrenWidgetsMeta: {},
|
||||||
|
otherWidgetsMeta: {},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWidgetDescendantToReset(
|
||||||
|
canvasWidgets: CanvasWidgetsReduxState,
|
||||||
|
widgetId: string,
|
||||||
|
evaluatedDataTree: DataTree,
|
||||||
|
widgetsMeta: MetaState,
|
||||||
|
): DescendantWidgetMap[] {
|
||||||
|
const descendantList: DescendantWidgetMap[] = [];
|
||||||
|
const widget = _.get(canvasWidgets, widgetId);
|
||||||
|
|
||||||
|
const sortedWidgetsMeta = sortWidgetsMetaByParent(widgetsMeta, widgetId);
|
||||||
|
for (const childMetaWidgetId of Object.keys(
|
||||||
|
sortedWidgetsMeta.childrenWidgetsMeta,
|
||||||
|
)) {
|
||||||
|
const evaluatedChildWidget = _.find(evaluatedDataTree, function (entity) {
|
||||||
|
return isWidget(entity) && entity.widgetId === childMetaWidgetId;
|
||||||
|
}) as WidgetEntity | undefined;
|
||||||
|
descendantList.push({
|
||||||
|
id: childMetaWidgetId,
|
||||||
|
evaluatedWidget: evaluatedChildWidget,
|
||||||
|
});
|
||||||
|
const grandChildren = getWidgetDescendantToReset(
|
||||||
|
canvasWidgets,
|
||||||
|
childMetaWidgetId,
|
||||||
|
evaluatedDataTree,
|
||||||
|
sortedWidgetsMeta.otherWidgetsMeta,
|
||||||
|
);
|
||||||
|
if (grandChildren.length) {
|
||||||
|
descendantList.push(...grandChildren);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget) {
|
||||||
|
const { children = [] } = widget;
|
||||||
|
|
||||||
|
if (children && children.length) {
|
||||||
|
for (const childIndex in children) {
|
||||||
|
if (children.hasOwnProperty(childIndex)) {
|
||||||
|
const childWidgetId = children[childIndex];
|
||||||
|
|
||||||
|
const childCanvasWidget = _.get(canvasWidgets, childWidgetId);
|
||||||
|
const childWidgetName = childCanvasWidget.widgetName;
|
||||||
|
const childWidget = evaluatedDataTree[childWidgetName];
|
||||||
|
if (isWidget(childWidget)) {
|
||||||
|
descendantList.push({
|
||||||
|
id: childWidgetId,
|
||||||
|
evaluatedWidget: childWidget,
|
||||||
|
});
|
||||||
|
const grandChildren = getWidgetDescendantToReset(
|
||||||
|
canvasWidgets,
|
||||||
|
childWidgetId,
|
||||||
|
evaluatedDataTree,
|
||||||
|
sortedWidgetsMeta.otherWidgetsMeta,
|
||||||
|
);
|
||||||
|
if (grandChildren.length) {
|
||||||
|
descendantList.push(...grandChildren);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return descendantList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetChildrenMetaProperty(
|
||||||
|
parentWidgetId: string,
|
||||||
|
evaluatedDataTree: DataTree,
|
||||||
|
evalMetaUpdates: EvalMetaUpdates,
|
||||||
|
updatedProperties: string[][],
|
||||||
|
) {
|
||||||
|
const childrenList = getWidgetDescendantToReset(
|
||||||
|
canvasWidgets,
|
||||||
|
parentWidgetId,
|
||||||
|
evaluatedDataTree,
|
||||||
|
canvasWidgetsMeta,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const childIndex in childrenList) {
|
||||||
|
const { evaluatedWidget: childWidget } = childrenList[childIndex];
|
||||||
|
|
||||||
|
if (!childWidget) continue;
|
||||||
|
|
||||||
|
resetWidgetMetaProperty(
|
||||||
|
childWidget?.widgetName,
|
||||||
|
false,
|
||||||
|
evalMetaUpdates,
|
||||||
|
updatedProperties,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default resetWidget;
|
export default resetWidget;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export function promisify<P extends ReadonlyArray<unknown>>(
|
||||||
return async function (...args: P) {
|
return async function (...args: P) {
|
||||||
const actionDescription = fnDescriptor(...args);
|
const actionDescription = fnDescriptor(...args);
|
||||||
const metaData = ExecutionMetaData.getExecutionMetaData();
|
const metaData = ExecutionMetaData.getExecutionMetaData();
|
||||||
|
|
||||||
const response = await WorkerMessenger.request({
|
const response = await WorkerMessenger.request({
|
||||||
method: MAIN_THREAD_ACTION.PROCESS_TRIGGER,
|
method: MAIN_THREAD_ACTION.PROCESS_TRIGGER,
|
||||||
data: {
|
data: {
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,13 @@ import DataStore from "../dataStore";
|
||||||
import type { TransmissionErrorHandler } from "../fns/utils/Messenger";
|
import type { TransmissionErrorHandler } from "../fns/utils/Messenger";
|
||||||
import { MessageType, sendMessage } from "utils/MessageUtil";
|
import { MessageType, sendMessage } from "utils/MessageUtil";
|
||||||
import { startSpansInAnEvaluation } from "UITelemetry/generateWebWorkerTraces";
|
import { startSpansInAnEvaluation } from "UITelemetry/generateWebWorkerTraces";
|
||||||
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||||
|
|
||||||
export let replayMap: Record<string, ReplayEntity<any>> | undefined;
|
export let replayMap: Record<string, ReplayEntity<any>> | undefined;
|
||||||
export let dataTreeEvaluator: DataTreeEvaluator | undefined;
|
export let dataTreeEvaluator: DataTreeEvaluator | undefined;
|
||||||
export const CANVAS = "canvas";
|
export const CANVAS = "canvas";
|
||||||
|
export let canvasWidgetsMeta: Record<string, any>;
|
||||||
|
export let canvasWidgets: CanvasWidgetsReduxState;
|
||||||
|
|
||||||
export default function (request: EvalWorkerSyncRequest) {
|
export default function (request: EvalWorkerSyncRequest) {
|
||||||
const { data } = request;
|
const { data } = request;
|
||||||
|
|
@ -56,11 +60,15 @@ export default function (request: EvalWorkerSyncRequest) {
|
||||||
theme,
|
theme,
|
||||||
unevalTree: __unevalTree__,
|
unevalTree: __unevalTree__,
|
||||||
widgets,
|
widgets,
|
||||||
|
widgetsMeta,
|
||||||
widgetTypeConfigMap,
|
widgetTypeConfigMap,
|
||||||
} = data as EvalTreeRequestData;
|
} = data as EvalTreeRequestData;
|
||||||
|
|
||||||
const unevalTree = __unevalTree__.unEvalTree;
|
const unevalTree = __unevalTree__.unEvalTree;
|
||||||
configTree = __unevalTree__.configTree as ConfigTree;
|
configTree = __unevalTree__.configTree as ConfigTree;
|
||||||
|
canvasWidgets = widgets;
|
||||||
|
canvasWidgetsMeta = widgetsMeta;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!dataTreeEvaluator) {
|
if (!dataTreeEvaluator) {
|
||||||
isCreateFirstTree = true;
|
isCreateFirstTree = true;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ export interface EvalTreeRequestData {
|
||||||
forceEvaluation: boolean;
|
forceEvaluation: boolean;
|
||||||
metaWidgets: MetaWidgetsReduxState;
|
metaWidgets: MetaWidgetsReduxState;
|
||||||
appMode?: APP_MODE;
|
appMode?: APP_MODE;
|
||||||
|
widgetsMeta: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EvalTreeResponseData {
|
export interface EvalTreeResponseData {
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,10 @@ export default class DataTreeEvaluator {
|
||||||
return this.evalTree;
|
return this.evalTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEvalProps() {
|
||||||
|
return this.evalProps;
|
||||||
|
}
|
||||||
|
|
||||||
setEvalTree(evalTree: DataTree) {
|
setEvalTree(evalTree: DataTree) {
|
||||||
this.evalTree = evalTree;
|
this.evalTree = evalTree;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ export function validateAndParseWidgetProperty({
|
||||||
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
evalPathsIdenticalToState: EvalPathsIdenticalToState;
|
||||||
}): unknown {
|
}): unknown {
|
||||||
const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath);
|
const { propertyPath } = getEntityNameAndPropertyPath(fullPropertyPath);
|
||||||
|
|
||||||
if (isPathDynamicTrigger(widget, propertyPath)) {
|
if (isPathDynamicTrigger(widget, propertyPath)) {
|
||||||
// TODO find a way to validate triggers
|
// TODO find a way to validate triggers
|
||||||
return unEvalPropertyValue;
|
return unEvalPropertyValue;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user