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:
Favour Ohanekwu 2023-01-17 08:12:16 +01:00 committed by GitHub
parent dffe5890fb
commit a1b07e21c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 10 deletions

View File

@ -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");
});
});

View File

@ -91,7 +91,10 @@ import { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationR
import { FormEvalActionPayload } from "./FormEvaluationSaga";
import { getSelectedAppTheme } from "selectors/appThemingSelectors";
import { resetWidgetsMetaState, updateMetaState } from "actions/metaActions";
import { getAllActionValidationConfig } from "selectors/entitiesSelector";
import {
getAllActionValidationConfig,
getAllJSActionsData,
} from "selectors/entitiesSelector";
import {
DataTree,
UnEvalTree,
@ -229,6 +232,7 @@ export function* evaluateTreeSaga(
yield call(evalErrorHandler as any, errors, updatedDataTree, evaluationOrder);
if (appMode !== APP_MODE.PUBLISHED) {
const jsData: Record<string, unknown> = yield select(getAllJSActionsData);
yield call(makeUpdateJSCollection, jsUpdates);
yield fork(
logSuccessfulBindings,
@ -243,6 +247,7 @@ export function* evaluateTreeSaga(
updatedDataTree,
unEvalUpdates,
isCreateFirstTree,
jsData,
);
}
yield put(setDependencyMap(dependencies));
@ -566,6 +571,7 @@ function* evaluationChangeListenerSaga(): any {
if (shouldProcessBatchedAction(action)) {
const postEvalActions = getPostEvalActions(action);
yield call(
evaluateTreeSaga,
postEvalActions,

View File

@ -342,6 +342,7 @@ export function* updateTernDefinitions(
dataTree: DataTree,
updates: DataTreeDiff[],
isCreateFirstTree: boolean,
jsData: Record<string, unknown> = {},
) {
const shouldUpdate: boolean =
isCreateFirstTree ||
@ -365,6 +366,7 @@ export function* updateTernDefinitions(
const { def, entityInfo } = dataTreeTypeDefCreator(
treeWithoutPrivateWidgets,
!!featureFlags.JS_EDITOR,
jsData,
);
CodemirrorTernService.updateDef("DATA_TREE", def, entityInfo);
const end = performance.now();

View File

@ -916,3 +916,22 @@ export const selectLibrariesForExplorer = createSelector(
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;
};

View File

@ -25,6 +25,7 @@ import { Variable } from "entities/JSCollection";
export const dataTreeTypeDefCreator = (
dataTree: DataTree,
isJSEditorEnabled: boolean,
jsData: Record<string, unknown> = {},
): { 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
const extraDefsToDefine: Def = {};
@ -67,15 +68,15 @@ export const dataTreeTypeDefCreator = (
const metaObj = entity.meta;
const jsPropertiesDef: Def = {};
for (const key in metaObj) {
// const jsFunctionObj = metaObj[key];
// const { arguments: args } = jsFunctionObj;
// const argsTypeString = getFunctionsArgsType(args);
// As we don't show args we avoid to get args def of function
// we will also need to check performance implications here
const argsTypeString = getFunctionsArgsType([]);
jsPropertiesDef[key] = argsTypeString;
for (const funcName in metaObj) {
const funcTypeDef = generateJSFunctionTypeDef(
jsData,
`${entity.name}.${funcName}`,
extraDefsToDefine,
);
jsPropertiesDef[funcName] = funcTypeDef;
// To also show funcName.data in autocompletion hint, we explictly add it here
jsPropertiesDef[`${funcName}.data`] = funcTypeDef.data;
}
for (let i = 0; i < entity.variables.length; i++) {
@ -197,3 +198,14 @@ export const getFunctionsArgsType = (args: Variable[]): string => {
);
return argsTypeString ? `fn(${argsTypeString})` : `fn()`;
};
export function generateJSFunctionTypeDef(
jsData: Record<string, unknown> = {},
fullFunctionName: string,
extraDefs: ExtraDef,
) {
return {
"!type": getFunctionsArgsType([]),
data: generateTypeDef(jsData[fullFunctionName], extraDefs),
};
}