fix: Show JS Function data in autocompletion hints (#19811)
## Description This PR adds JS function data to autocompletion hints Fixes #15909 <img width="278" alt="Screenshot 2023-01-16 at 20 35 55" src="https://user-images.githubusercontent.com/46670083/212754461-68844350-5d23-4b50-af1f-675b7719dc49.png"> ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? 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: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test
This commit is contained in:
parent
dffe5890fb
commit
a1b07e21c7
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
|
||||||
|
import { WIDGET } from "../../../../locators/WidgetLocators";
|
||||||
|
|
||||||
|
const jsEditor = ObjectsRegistry.JSEditor,
|
||||||
|
ee = ObjectsRegistry.EntityExplorer,
|
||||||
|
agHelper = ObjectsRegistry.AggregateHelper,
|
||||||
|
propPane = ObjectsRegistry.PropertyPane,
|
||||||
|
CommonLocators = ObjectsRegistry.CommonLocators;
|
||||||
|
|
||||||
|
describe("JS Function Execution", function() {
|
||||||
|
before(() => {
|
||||||
|
ee.DragDropWidgetNVerify(WIDGET.BUTTON, 200, 200);
|
||||||
|
});
|
||||||
|
it("1. Shows js function data as part of autocompletion hints", function() {
|
||||||
|
jsEditor.CreateJSObject(
|
||||||
|
`export default {
|
||||||
|
myFun1: ()=>{
|
||||||
|
return "yes"
|
||||||
|
},
|
||||||
|
myFun2:()=>{
|
||||||
|
return [{name: "test"}]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
{
|
||||||
|
paste: true,
|
||||||
|
completeReplace: true,
|
||||||
|
toRun: false,
|
||||||
|
shouldCreateNewJSObj: true,
|
||||||
|
prettify: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ee.SelectEntityByName("Button1", "Widgets");
|
||||||
|
propPane.EnterJSContext("onClick", `{{JSObject1.`, true, false);
|
||||||
|
agHelper.AssertContains("myFun1.data");
|
||||||
|
agHelper.AssertContains("myFun2.data");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -91,7 +91,10 @@ import { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationR
|
||||||
import { FormEvalActionPayload } from "./FormEvaluationSaga";
|
import { FormEvalActionPayload } from "./FormEvaluationSaga";
|
||||||
import { getSelectedAppTheme } from "selectors/appThemingSelectors";
|
import { getSelectedAppTheme } from "selectors/appThemingSelectors";
|
||||||
import { resetWidgetsMetaState, updateMetaState } from "actions/metaActions";
|
import { resetWidgetsMetaState, updateMetaState } from "actions/metaActions";
|
||||||
import { getAllActionValidationConfig } from "selectors/entitiesSelector";
|
import {
|
||||||
|
getAllActionValidationConfig,
|
||||||
|
getAllJSActionsData,
|
||||||
|
} from "selectors/entitiesSelector";
|
||||||
import {
|
import {
|
||||||
DataTree,
|
DataTree,
|
||||||
UnEvalTree,
|
UnEvalTree,
|
||||||
|
|
@ -229,6 +232,7 @@ export function* evaluateTreeSaga(
|
||||||
yield call(evalErrorHandler as any, errors, updatedDataTree, evaluationOrder);
|
yield call(evalErrorHandler as any, errors, updatedDataTree, evaluationOrder);
|
||||||
|
|
||||||
if (appMode !== APP_MODE.PUBLISHED) {
|
if (appMode !== APP_MODE.PUBLISHED) {
|
||||||
|
const jsData: Record<string, unknown> = yield select(getAllJSActionsData);
|
||||||
yield call(makeUpdateJSCollection, jsUpdates);
|
yield call(makeUpdateJSCollection, jsUpdates);
|
||||||
yield fork(
|
yield fork(
|
||||||
logSuccessfulBindings,
|
logSuccessfulBindings,
|
||||||
|
|
@ -243,6 +247,7 @@ export function* evaluateTreeSaga(
|
||||||
updatedDataTree,
|
updatedDataTree,
|
||||||
unEvalUpdates,
|
unEvalUpdates,
|
||||||
isCreateFirstTree,
|
isCreateFirstTree,
|
||||||
|
jsData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
yield put(setDependencyMap(dependencies));
|
yield put(setDependencyMap(dependencies));
|
||||||
|
|
@ -566,6 +571,7 @@ function* evaluationChangeListenerSaga(): any {
|
||||||
|
|
||||||
if (shouldProcessBatchedAction(action)) {
|
if (shouldProcessBatchedAction(action)) {
|
||||||
const postEvalActions = getPostEvalActions(action);
|
const postEvalActions = getPostEvalActions(action);
|
||||||
|
|
||||||
yield call(
|
yield call(
|
||||||
evaluateTreeSaga,
|
evaluateTreeSaga,
|
||||||
postEvalActions,
|
postEvalActions,
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,7 @@ export function* updateTernDefinitions(
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
updates: DataTreeDiff[],
|
updates: DataTreeDiff[],
|
||||||
isCreateFirstTree: boolean,
|
isCreateFirstTree: boolean,
|
||||||
|
jsData: Record<string, unknown> = {},
|
||||||
) {
|
) {
|
||||||
const shouldUpdate: boolean =
|
const shouldUpdate: boolean =
|
||||||
isCreateFirstTree ||
|
isCreateFirstTree ||
|
||||||
|
|
@ -365,6 +366,7 @@ export function* updateTernDefinitions(
|
||||||
const { def, entityInfo } = dataTreeTypeDefCreator(
|
const { def, entityInfo } = dataTreeTypeDefCreator(
|
||||||
treeWithoutPrivateWidgets,
|
treeWithoutPrivateWidgets,
|
||||||
!!featureFlags.JS_EDITOR,
|
!!featureFlags.JS_EDITOR,
|
||||||
|
jsData,
|
||||||
);
|
);
|
||||||
CodemirrorTernService.updateDef("DATA_TREE", def, entityInfo);
|
CodemirrorTernService.updateDef("DATA_TREE", def, entityInfo);
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
|
||||||
|
|
@ -916,3 +916,22 @@ export const selectLibrariesForExplorer = createSelector(
|
||||||
return [...queuedInstalls, ...libs];
|
return [...queuedInstalls, ...libs];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getAllJSActionsData = (state: AppState) => {
|
||||||
|
const jsActionsData: Record<string, unknown> = {};
|
||||||
|
const jsCollections = state.entities.jsActions;
|
||||||
|
jsCollections.forEach((collection) => {
|
||||||
|
if (collection.data) {
|
||||||
|
Object.keys(collection.data).forEach((actionId) => {
|
||||||
|
const jsAction = getJSActions(state, collection.config.id).find(
|
||||||
|
(action) => action.id === actionId,
|
||||||
|
);
|
||||||
|
if (jsAction) {
|
||||||
|
jsActionsData[`${collection.config.name}.${jsAction.name}`] =
|
||||||
|
collection.data?.[actionId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return jsActionsData;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { Variable } from "entities/JSCollection";
|
||||||
export const dataTreeTypeDefCreator = (
|
export const dataTreeTypeDefCreator = (
|
||||||
dataTree: DataTree,
|
dataTree: DataTree,
|
||||||
isJSEditorEnabled: boolean,
|
isJSEditorEnabled: boolean,
|
||||||
|
jsData: Record<string, unknown> = {},
|
||||||
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
): { def: Def; entityInfo: Map<string, DataTreeDefEntityInformation> } => {
|
||||||
// When there is a complex data type, we store it in extra def and refer to it in the def
|
// When there is a complex data type, we store it in extra def and refer to it in the def
|
||||||
const extraDefsToDefine: Def = {};
|
const extraDefsToDefine: Def = {};
|
||||||
|
|
@ -67,15 +68,15 @@ export const dataTreeTypeDefCreator = (
|
||||||
const metaObj = entity.meta;
|
const metaObj = entity.meta;
|
||||||
const jsPropertiesDef: Def = {};
|
const jsPropertiesDef: Def = {};
|
||||||
|
|
||||||
for (const key in metaObj) {
|
for (const funcName in metaObj) {
|
||||||
// const jsFunctionObj = metaObj[key];
|
const funcTypeDef = generateJSFunctionTypeDef(
|
||||||
// const { arguments: args } = jsFunctionObj;
|
jsData,
|
||||||
// const argsTypeString = getFunctionsArgsType(args);
|
`${entity.name}.${funcName}`,
|
||||||
// As we don't show args we avoid to get args def of function
|
extraDefsToDefine,
|
||||||
// we will also need to check performance implications here
|
);
|
||||||
|
jsPropertiesDef[funcName] = funcTypeDef;
|
||||||
const argsTypeString = getFunctionsArgsType([]);
|
// To also show funcName.data in autocompletion hint, we explictly add it here
|
||||||
jsPropertiesDef[key] = argsTypeString;
|
jsPropertiesDef[`${funcName}.data`] = funcTypeDef.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < entity.variables.length; i++) {
|
for (let i = 0; i < entity.variables.length; i++) {
|
||||||
|
|
@ -197,3 +198,14 @@ export const getFunctionsArgsType = (args: Variable[]): string => {
|
||||||
);
|
);
|
||||||
return argsTypeString ? `fn(${argsTypeString})` : `fn()`;
|
return argsTypeString ? `fn(${argsTypeString})` : `fn()`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function generateJSFunctionTypeDef(
|
||||||
|
jsData: Record<string, unknown> = {},
|
||||||
|
fullFunctionName: string,
|
||||||
|
extraDefs: ExtraDef,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
"!type": getFunctionsArgsType([]),
|
||||||
|
data: generateTypeDef(jsData[fullFunctionName], extraDefs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user