- {props.dataType === "object" && props.data !== null && (
+ {(dataType === "object" || dataType === "array") && jsData !== null && (
-
+
)}
- {props.dataType === "function" && (
-
{(props.data as any).toString()}
- )}
- {props.dataType === "boolean" && (
-
{(props.data as any).toString()}
- )}
- {props.dataType === "string" && (
-
{(props.data as any).toString()}
- )}
- {props.dataType === "number" && (
-
{(props.data as any).toString()}
- )}
- {((props.dataType !== "object" &&
- props.dataType !== "function" &&
- props.dataType !== "boolean" &&
- props.dataType !== "string" &&
- props.dataType !== "number") ||
- props.data === null) && (
+ {dataType === "function" &&
{(jsData as any).toString()}
}
+ {dataType === "boolean" &&
{(jsData as any).toString()}
}
+ {dataType === "string" &&
{(jsData as any).toString()}
}
+ {dataType === "number" &&
{(jsData as any).toString()}
}
+ {((dataType !== "object" &&
+ dataType !== "function" &&
+ dataType !== "boolean" &&
+ dataType !== "string" &&
+ dataType !== "array" &&
+ dataType !== "number") ||
+ jsData === null) && (
- {(props.data as any)?.toString() ??
- props.data ??
- props.data === undefined
+ {(jsData as any)?.toString() ?? jsData ?? jsData === undefined
? "undefined"
: "null"}
diff --git a/app/client/src/components/editorComponents/CodeEditor/index.tsx b/app/client/src/components/editorComponents/CodeEditor/index.tsx
index 8107351506..5f9cd01dfa 100644
--- a/app/client/src/components/editorComponents/CodeEditor/index.tsx
+++ b/app/client/src/components/editorComponents/CodeEditor/index.tsx
@@ -60,16 +60,12 @@ import {
DynamicAutocompleteInputWrapper,
EditorWrapper,
IconContainer,
+ PEEK_STYLE_PERSIST_CLASS,
} from "components/editorComponents/CodeEditor/styledComponents";
import { bindingMarker } from "components/editorComponents/CodeEditor/MarkHelpers/bindingMarker";
import {
entityMarker,
NAVIGATE_TO_ATTRIBUTE,
- PEEKABLE_ATTRIBUTE,
- PEEKABLE_CH_END,
- PEEKABLE_CH_START,
- PEEKABLE_LINE,
- PEEK_STYLE_PERSIST_CLASS,
} from "components/editorComponents/CodeEditor/MarkHelpers/entityMarker";
import {
bindingHint,
@@ -153,7 +149,14 @@ import {
APPSMITH_AI,
askAIEnabled,
} from "@appsmith/components/editorComponents/GPT/trigger";
-import { getAllDatasourceTableKeys } from "selectors/entitiesSelector";
+import {
+ getAllDatasourceTableKeys,
+ selectInstalledLibraries,
+} from "selectors/entitiesSelector";
+import { debug } from "loglevel";
+import { PeekOverlayExpressionIdentifier, SourceType } from "@shared/ast";
+import type { MultiplexingModeConfig } from "components/editorComponents/CodeEditor/modes";
+import { MULTIPLEXING_MODE_CONFIGS } from "components/editorComponents/CodeEditor/modes";
type ReduxStateProps = ReturnType
;
type ReduxDispatchProps = ReturnType;
@@ -229,6 +232,7 @@ export type EditorProps = EditorStyleProps &
isReadOnly?: boolean;
isRawView?: boolean;
isJSObject?: boolean;
+ jsObjectName?: string;
containerHeight?: number;
// Custom gutter
customGutter?: CodeEditorGutter;
@@ -253,7 +257,7 @@ type State = {
ctrlPressed: boolean;
peekOverlayProps:
| (PeekOverlayStateProps & {
- marker?: CodeMirror.TextMarker;
+ tokenElement: Element;
})
| undefined;
isDynamic: boolean;
@@ -278,9 +282,11 @@ class CodeEditor extends Component {
hinters: Hinter[] = [];
annotations: Annotation[] = [];
updateLintingCallback: UpdateLintingCallback | undefined;
+ private peekOverlayExpressionIdentifier: PeekOverlayExpressionIdentifier;
private editorWrapperRef = React.createRef();
currentLineNumber: number | null = null;
AIEnabled = false;
+ private multiplexConfig?: MultiplexingModeConfig;
constructor(props: Props) {
super(props);
@@ -296,6 +302,18 @@ class CodeEditor extends Component {
showAIWindow: false,
};
this.updatePropertyValue = this.updatePropertyValue.bind(this);
+ this.peekOverlayExpressionIdentifier = new PeekOverlayExpressionIdentifier(
+ props.isJSObject
+ ? {
+ sourceType: SourceType.module,
+ thisExpressionReplacement: props.jsObjectName,
+ }
+ : {
+ sourceType: SourceType.script,
+ },
+ props.input.value,
+ );
+ this.multiplexConfig = MULTIPLEXING_MODE_CONFIGS[this.props.mode];
}
componentDidMount(): void {
@@ -577,48 +595,40 @@ class CodeEditor extends Component {
};
showPeekOverlay = (
- peekableAttribute: string,
+ expression: string,
+ paths: string[],
tokenElement: Element,
- tokenElementPosition: DOMRect,
- dataToShow: unknown,
) => {
- const line = tokenElement.getAttribute(PEEKABLE_LINE),
- chStart = tokenElement.getAttribute(PEEKABLE_CH_START),
- chEnd = tokenElement.getAttribute(PEEKABLE_CH_END);
-
- this.state.peekOverlayProps?.marker?.clear();
- let marker: CodeMirror.TextMarker | undefined;
- if (line && chStart && chEnd) {
- marker = this.editor.markText(
- { ch: Number(chStart), line: Number(line) },
- { ch: Number(chEnd), line: Number(line) },
- {
- className: PEEK_STYLE_PERSIST_CLASS,
- },
- );
+ const tokenElementPosition = tokenElement.getBoundingClientRect();
+ if (this.state.peekOverlayProps) {
+ if (tokenElement === this.state.peekOverlayProps.tokenElement) return;
+ this.hidePeekOverlay();
}
-
+ tokenElement.classList.add(PEEK_STYLE_PERSIST_CLASS);
this.setState({
peekOverlayProps: {
- name: peekableAttribute,
+ objectName: paths[0],
+ propertyPath: paths.slice(1),
position: tokenElementPosition,
+ tokenElement,
textWidth: tokenElementPosition.width,
- marker,
- data: dataToShow,
- dataType: typeof dataToShow,
},
});
AnalyticsUtil.logEvent("PEEK_OVERLAY_OPENED", {
- property: peekableAttribute,
+ property: expression,
});
};
hidePeekOverlay = () => {
- this.state.peekOverlayProps?.marker?.clear();
- this.setState({
- peekOverlayProps: undefined,
- });
+ if (this.state.peekOverlayProps) {
+ this.state.peekOverlayProps.tokenElement.classList.remove(
+ PEEK_STYLE_PERSIST_CLASS,
+ );
+ this.setState({
+ peekOverlayProps: undefined,
+ });
+ }
};
debounceHandleMouseOver = debounce(
@@ -648,36 +658,109 @@ class CodeEditor extends Component {
setTimeout(delayedWork, 0);
};
- handleMouseOver = (event: MouseEvent) => {
+ isPeekableElement = (element: Element) => {
if (
- event.target instanceof Element &&
- event.target.hasAttribute(PEEKABLE_ATTRIBUTE)
+ !element.classList.contains("cm-m-javascript") ||
+ element.classList.contains("binding-brackets")
+ )
+ return false;
+ if (
+ // global variables and functions
+ // JsObject1, storeValue()
+ element.classList.contains("cm-variable") ||
+ // properties and function calls
+ // JsObject.myFun(), Api1.data
+ element.classList.contains("cm-property") ||
+ // array indices - [0]
+ element.classList.contains("cm-number") ||
+ // string accessor - ["x"]
+ element.classList.contains("cm-string")
) {
- const tokenElement = event.target;
- const tokenElementPosition = tokenElement.getBoundingClientRect();
- const peekableAttribute = tokenElement.getAttribute(PEEKABLE_ATTRIBUTE);
- if (peekableAttribute) {
- // don't retrigger if hovering over the same token
- if (
- this.state.peekOverlayProps?.name === peekableAttribute &&
- this.state.peekOverlayProps?.position.top ===
- tokenElementPosition.top &&
- this.state.peekOverlayProps?.position.left ===
- tokenElementPosition.left
- ) {
- return;
- }
- const paths = peekableAttribute.split(".");
- if (paths.length) {
- paths.splice(1, 0, "peekData");
- this.showPeekOverlay(
- peekableAttribute,
- tokenElement,
- tokenElementPosition,
- _.get(this.props.entitiesForNavigation, paths),
- );
- }
+ return true;
+ } else if (element.classList.contains("cm-keyword")) {
+ // this keyword for jsObjects
+ if (this.props.isJSObject && element.innerHTML === "this") {
+ return true;
}
+ }
+ };
+
+ getBindingSnippetAtPos = (
+ multiPlexConfig: MultiplexingModeConfig,
+ pos: number,
+ ) => {
+ return multiPlexConfig.innerModes.map((innerMode) => {
+ const doc = this.editor.getValue();
+ const openPos =
+ doc.lastIndexOf(innerMode.open, pos) + innerMode.open.length;
+ const closePos = doc.indexOf(innerMode.close, pos);
+ return {
+ value: doc.slice(openPos, closePos),
+ offset: openPos,
+ };
+ });
+ };
+
+ updateScriptForPeekOverlay = (chIndex: number) => {
+ if (
+ !this.peekOverlayExpressionIdentifier.hasParsedScript() ||
+ this.multiplexConfig
+ ) {
+ if (this.multiplexConfig) {
+ const bindingSnippetsByInnerMode = this.getBindingSnippetAtPos(
+ this.multiplexConfig,
+ chIndex,
+ );
+ for (const snippet of bindingSnippetsByInnerMode) {
+ if (snippet.value) {
+ this.peekOverlayExpressionIdentifier.updateScript(snippet.value);
+ chIndex -= snippet.offset;
+ break;
+ }
+ }
+ } else {
+ this.peekOverlayExpressionIdentifier.updateScript(
+ this.editor.getValue(),
+ );
+ }
+ }
+ return chIndex;
+ };
+
+ isPathLibrary = (paths: string[]) => {
+ return !!this.props.installedLibraries.find((installedLib) =>
+ installedLib.accessor.find((accessor) => accessor === paths[0]),
+ );
+ };
+
+ handleMouseOver = (event: MouseEvent) => {
+ const tokenElement = event.target;
+ if (
+ tokenElement instanceof Element &&
+ this.isPeekableElement(tokenElement)
+ ) {
+ const tokenPos = this.editor.coordsChar({
+ left: event.clientX,
+ top: event.clientY,
+ });
+ const chIndex = this.updateScriptForPeekOverlay(
+ this.editor.indexFromPos(tokenPos),
+ );
+
+ this.peekOverlayExpressionIdentifier
+ .extractExpressionAtPosition(chIndex)
+ .then((lineExpression: string) => {
+ const paths = _.toPath(lineExpression);
+ if (!this.isPathLibrary(paths)) {
+ this.showPeekOverlay(lineExpression, paths, tokenElement);
+ } else {
+ this.hidePeekOverlay();
+ }
+ })
+ .catch((e) => {
+ this.hidePeekOverlay();
+ debug(e);
+ });
} else {
this.hidePeekOverlay();
}
@@ -1086,6 +1169,8 @@ class CodeEditor extends Component {
changeObj.to,
);
}
+
+ this.peekOverlayExpressionIdentifier.clearScript();
};
handleDebouncedChange = _.debounce(this.handleChange, 600);
@@ -1568,6 +1653,7 @@ const mapStateToProps = (state: AppState, props: EditorProps) => ({
),
featureFlags: selectFeatureFlags(state),
datasourceTableKeys: getAllDatasourceTableKeys(state, props.dataTreePath),
+ installedLibraries: selectInstalledLibraries(state),
});
const mapDispatchToProps = (dispatch: any) => ({
diff --git a/app/client/src/components/editorComponents/CodeEditor/modes.ts b/app/client/src/components/editorComponents/CodeEditor/modes.ts
index 5a5a5cf00c..476e60a8ca 100644
--- a/app/client/src/components/editorComponents/CodeEditor/modes.ts
+++ b/app/client/src/components/editorComponents/CodeEditor/modes.ts
@@ -1,73 +1,96 @@
import CodeMirror from "codemirror";
+import type { TEditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
import { EditorModes } from "components/editorComponents/CodeEditor/EditorConfig";
import "codemirror/addon/mode/multiplex";
import "codemirror/mode/javascript/javascript";
import "codemirror/mode/sql/sql";
import "codemirror/addon/hint/sql-hint";
+import type { TEditorSqlModes } from "./sql/config";
import { sqlModesConfig } from "./sql/config";
-CodeMirror.defineMode(EditorModes.TEXT_WITH_BINDING, function (config) {
- // @ts-expect-error: Types are not available
- return CodeMirror.multiplexingMode(
- CodeMirror.getMode(config, EditorModes.TEXT),
- {
- open: "{{",
- close: "}}",
- mode: CodeMirror.getMode(config, {
- name: "javascript",
- }),
- },
- );
-});
+export const BINDING_OPEN = "{{",
+ BINDING_CLOSE = "}}";
-CodeMirror.defineMode(EditorModes.JSON_WITH_BINDING, function (config) {
- // @ts-expect-error: Types are not available
- return CodeMirror.multiplexingMode(
- CodeMirror.getMode(config, { name: "javascript", json: true }),
- {
- open: "{{",
- close: "}}",
- mode: CodeMirror.getMode(config, {
- name: "javascript",
- }),
- },
- );
-});
+export interface MultiplexingModeConfig {
+ outerMode: string | { name: string; json?: boolean };
+ innerModes: {
+ open: string;
+ close: string;
+ }[];
+}
-CodeMirror.defineMode(EditorModes.GRAPHQL_WITH_BINDING, function (config) {
- // @ts-expect-error: Types are not available
- return CodeMirror.multiplexingMode(
- CodeMirror.getMode(config, EditorModes.GRAPHQL),
- {
- open: "{{",
- close: "}}",
- mode: CodeMirror.getMode(config, {
- name: "javascript",
- }),
- },
- {
- open: '"{{',
- close: '}}"',
- mode: CodeMirror.getMode(config, {
- name: "javascript",
- }),
- },
- );
-});
+export type MultiplexingModeConfigs = Record<
+ TEditorModes,
+ MultiplexingModeConfig | undefined
+>;
-for (const sqlModeConfig of Object.values(sqlModesConfig)) {
- if (!sqlModeConfig.isMultiplex) continue;
- CodeMirror.defineMode(sqlModeConfig.mode, function (config) {
+export const MULTIPLEXING_MODE_CONFIGS: MultiplexingModeConfigs = {
+ [EditorModes.TEXT_WITH_BINDING]: {
+ outerMode: EditorModes.TEXT,
+ innerModes: [
+ {
+ open: BINDING_OPEN,
+ close: BINDING_CLOSE,
+ },
+ ],
+ },
+ [EditorModes.JSON_WITH_BINDING]: {
+ outerMode: { name: "javascript", json: true },
+ innerModes: [
+ {
+ open: BINDING_OPEN,
+ close: BINDING_CLOSE,
+ },
+ ],
+ },
+ [EditorModes.GRAPHQL_WITH_BINDING]: {
+ outerMode: EditorModes.GRAPHQL,
+ innerModes: [
+ {
+ open: BINDING_OPEN,
+ close: BINDING_CLOSE,
+ },
+ {
+ // https://github.com/appsmithorg/appsmith/issues/16702
+ open: '"{{',
+ close: '}}"',
+ },
+ ],
+ },
+ ...Object.values(sqlModesConfig)
+ .filter((config) => config.isMultiplex)
+ .reduce((prev, current) => {
+ prev[current.mode] = {
+ outerMode: current.mime,
+ innerModes: [
+ {
+ open: BINDING_OPEN,
+ close: BINDING_CLOSE,
+ },
+ ],
+ };
+ return prev;
+ }, {} as Record),
+ "text/plain": undefined,
+ "application/json": undefined,
+ javascript: undefined,
+ graphql: undefined,
+};
+
+Object.keys(MULTIPLEXING_MODE_CONFIGS).forEach((key) => {
+ const multiplexConfig = MULTIPLEXING_MODE_CONFIGS[key as TEditorModes];
+ if (!multiplexConfig) return;
+ CodeMirror.defineMode(key, function (config) {
// @ts-expect-error: Types are not available
return CodeMirror.multiplexingMode(
- CodeMirror.getMode(config, sqlModeConfig.mime),
- {
- open: "{{",
- close: "}}",
+ CodeMirror.getMode(config, multiplexConfig.outerMode),
+ ...multiplexConfig.innerModes.map((innerMode) => ({
+ open: innerMode.open,
+ close: innerMode.close,
mode: CodeMirror.getMode(config, {
name: "javascript",
}),
- },
+ })),
);
});
-}
+});
diff --git a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
index 9d37d2dd52..0c6f856375 100644
--- a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
+++ b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts
@@ -7,11 +7,9 @@ import {
import type { Theme } from "constants/DefaultTheme";
import { Skin } from "constants/DefaultTheme";
import { Colors } from "constants/Colors";
-import {
- NAVIGATION_CLASSNAME,
- PEEKABLE_CLASSNAME,
- PEEK_STYLE_PERSIST_CLASS,
-} from "./MarkHelpers/entityMarker";
+import { NAVIGATION_CLASSNAME } from "./MarkHelpers/entityMarker";
+
+export const PEEK_STYLE_PERSIST_CLASS = "peek-style-persist";
const getBorderStyle = (
props: { theme: Theme } & {
@@ -263,7 +261,7 @@ export const EditorWrapper = styled.div<{
font-weight: 700;
}
- .${PEEKABLE_CLASSNAME}:hover, .${PEEK_STYLE_PERSIST_CLASS} {
+ .${PEEK_STYLE_PERSIST_CLASS} {
border-color: var(--ads-v2-color-border-emphasis);
background-color: #ededed;
}
diff --git a/app/client/src/pages/Editor/JSEditor/Form.tsx b/app/client/src/pages/Editor/JSEditor/Form.tsx
index 9076d769e8..5b10d496c2 100644
--- a/app/client/src/pages/Editor/JSEditor/Form.tsx
+++ b/app/client/src/pages/Editor/JSEditor/Form.tsx
@@ -404,6 +404,7 @@ function JSEditorForm({ jsCollection: currentJSCollection }: Props) {
onChange: handleEditorChange,
}}
isJSObject
+ jsObjectName={currentJSCollection.name}
mode={EditorModes.JAVASCRIPT}
placeholder="Let's write some code!"
showLightningMenu={false}
diff --git a/app/client/src/selectors/navigationSelectors.ts b/app/client/src/selectors/navigationSelectors.ts
index fb9d8ec155..9492e370b2 100644
--- a/app/client/src/selectors/navigationSelectors.ts
+++ b/app/client/src/selectors/navigationSelectors.ts
@@ -1,7 +1,4 @@
-import type {
- DataTree,
- AppsmithEntity,
-} from "entities/DataTree/dataTreeFactory";
+import type { DataTree } from "entities/DataTree/dataTreeFactory";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
import { createSelector } from "reselect";
import {
@@ -15,11 +12,9 @@ import { getCurrentPageId } from "selectors/editorSelectors";
import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers";
import { jsCollectionIdURL, widgetURL } from "RouteBuilder";
import { getDataTree } from "selectors/dataTreeSelectors";
-import { getActionChildrenNavData } from "utils/NavigationSelector/ActionChildren";
import { createNavData } from "utils/NavigationSelector/common";
import { getWidgetChildrenNavData } from "utils/NavigationSelector/WidgetChildren";
import { getJsChildrenNavData } from "utils/NavigationSelector/JsChildren";
-import { getAppsmithNavData } from "utils/NavigationSelector/AppsmithNavData";
import {
getEntityNameAndPropertyPath,
isJSAction,
@@ -36,8 +31,6 @@ export type NavigationData = {
url: string | undefined;
navigable: boolean;
children: EntityNavigationData;
- peekable: boolean;
- peekData?: unknown;
key?: string;
pluginName?: string;
isMock?: boolean;
@@ -79,8 +72,6 @@ export const getEntitiesForNavigation = createSelector(
(datasource) => datasource.id === datasourceId,
);
const config = getActionConfig(action.config.pluginType);
- // dataTree used to get entityDefinitions and peekData
- const result = getActionChildrenNavData(action, dataTree);
if (!config) return;
navigationData[action.config.name] = createNavData({
id: action.config.id,
@@ -92,9 +83,7 @@ export const getEntitiesForNavigation = createSelector(
action.config.pluginType,
plugin,
),
- peekable: true,
- peekData: result?.peekData,
- children: result?.childNavData || {},
+ children: {},
// Adding below data as it is required for analytical events
pluginName: plugin?.name,
datasourceId: datasource?.id,
@@ -105,36 +94,33 @@ export const getEntitiesForNavigation = createSelector(
});
jsActions.forEach((jsAction) => {
- // dataTree for null check and peekData
+ // dataTree for null check
const result = getJsChildrenNavData(jsAction, pageId, dataTree);
navigationData[jsAction.config.name] = createNavData({
id: jsAction.config.id,
name: jsAction.config.name,
type: ENTITY_TYPE.JSACTION,
url: jsCollectionIdURL({ pageId, collectionId: jsAction.config.id }),
- peekable: true,
- peekData: result?.peekData,
children: result?.childNavData || {},
});
});
Object.values(widgets).forEach((widget) => {
- // dataTree to get entityDefinitions, for url (can use getWidgetByName?) and peekData
- const result = getWidgetChildrenNavData(widget, dataTree, pageId);
+ // dataTree to get entityDefinitions, for url (can use getWidgetByName?)
+ const result = getWidgetChildrenNavData(
+ widget.widgetName,
+ widget.type,
+ dataTree,
+ pageId,
+ );
navigationData[widget.widgetName] = createNavData({
id: widget.widgetId,
name: widget.widgetName,
type: ENTITY_TYPE.WIDGET,
url: widgetURL({ pageId, selectedWidgets: [widget.widgetId] }),
- peekable: true,
- peekData: result?.peekData,
children: result?.childNavData || {},
});
});
- // dataTree to get entity definitions and peekData
- navigationData["appsmith"] = getAppsmithNavData(
- dataTree.appsmith as AppsmithEntity,
- );
if (
entityName &&
isJSAction(dataTree[entityName]) &&
diff --git a/app/client/src/utils/FilterInternalProperties/Action.ts b/app/client/src/utils/FilterInternalProperties/Action.ts
new file mode 100644
index 0000000000..2339fdcce3
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/Action.ts
@@ -0,0 +1,26 @@
+import { entityDefinitions } from "@appsmith/utils/autocomplete/EntityDefinitions";
+import type { DataTree } from "entities/DataTree/dataTreeFactory";
+import type { ActionEntity } from "entities/DataTree/types";
+
+export const getActionChildrenPeekData = (
+ actionName: string,
+ dataTree: DataTree,
+) => {
+ const dataTreeAction = dataTree[actionName] as ActionEntity;
+ if (dataTreeAction) {
+ const definitions = entityDefinitions.ACTION(dataTreeAction, {});
+ const peekData: Record = {};
+ Object.keys(definitions).forEach((key) => {
+ if (key.indexOf("!") === -1) {
+ if (key === "data" || key === "isLoading" || key === "responseMeta") {
+ peekData[key] = dataTreeAction[key];
+ } else if (key === "run" || key === "clear") {
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ peekData[key] = function () {}; // tern inference required here
+ }
+ }
+ });
+
+ return { peekData };
+ }
+};
diff --git a/app/client/src/utils/FilterInternalProperties/Appsmith.ts b/app/client/src/utils/FilterInternalProperties/Appsmith.ts
new file mode 100644
index 0000000000..25c5ebbc55
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/Appsmith.ts
@@ -0,0 +1,14 @@
+import { entityDefinitions } from "@appsmith/utils/autocomplete/EntityDefinitions";
+import type {
+ AppsmithEntity,
+ DataTree,
+} from "entities/DataTree/dataTreeFactory";
+import { createObjectPeekData } from "./Common";
+
+export const getAppsmithPeekData = (dataTree: DataTree) => {
+ const defs: any = entityDefinitions.APPSMITH(
+ dataTree.appsmith as AppsmithEntity,
+ {},
+ );
+ return createObjectPeekData(defs, dataTree.appsmith, {}, "appsmith");
+};
diff --git a/app/client/src/utils/FilterInternalProperties/Common.ts b/app/client/src/utils/FilterInternalProperties/Common.ts
new file mode 100644
index 0000000000..e57a878d99
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/Common.ts
@@ -0,0 +1,40 @@
+import _ from "lodash";
+
+export const isTernFunctionDef = (data: any) =>
+ typeof data === "string" && /^fn\((?:[\w,: \(\)->])*\) -> [\w]*$/.test(data);
+
+export const createObjectPeekData = (
+ defs: any,
+ data: any,
+ peekData: any,
+ parentKey: string,
+) => {
+ Object.keys(defs).forEach((key: string) => {
+ if (key.indexOf("!") === -1) {
+ const childKeyPathArray = [parentKey, key];
+ if (isObject(defs[key])) {
+ if (Object.keys(defs[key]).length > 0) {
+ peekData[key] = {};
+ const result = createObjectPeekData(
+ defs[key],
+ data[key],
+ peekData[key],
+ key,
+ );
+ _.set(peekData, childKeyPathArray, result.peekData);
+ } else {
+ peekData[key] = data[key];
+ }
+ } else {
+ peekData[key] = isTernFunctionDef(defs[key])
+ ? // eslint-disable-next-line @typescript-eslint/no-empty-function
+ function () {} // tern inference required here
+ : data[key];
+ }
+ }
+ });
+ return { peekData };
+};
+
+const isObject = (data: any) =>
+ typeof data === "object" && !Array.isArray(data) && data !== null;
diff --git a/app/client/src/utils/FilterInternalProperties/JsAction.ts b/app/client/src/utils/FilterInternalProperties/JsAction.ts
new file mode 100644
index 0000000000..8b59ca3cdd
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/JsAction.ts
@@ -0,0 +1,29 @@
+import type { DataTree } from "entities/DataTree/dataTreeFactory";
+import type { JSCollectionData } from "reducers/entityReducers/jsActionsReducer";
+import type { JSActionEntity } from "entities/DataTree/types";
+
+export const getJsActionPeekData = (
+ jsAction: JSCollectionData,
+ dataTree: DataTree,
+) => {
+ const peekData: Record = {};
+
+ const dataTreeAction = dataTree[jsAction.config.name] as JSActionEntity;
+
+ if (dataTreeAction) {
+ jsAction.config.actions.forEach((jsChild) => {
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ peekData[jsChild.name] = function () {};
+
+ if (jsAction.data?.[jsChild.id] && jsChild.executeOnLoad) {
+ (peekData[jsChild.name] as any).data = jsAction.data[jsChild.id];
+ }
+ });
+
+ jsAction.config.variables.forEach((jsChild) => {
+ if (dataTreeAction) peekData[jsChild.name] = dataTreeAction[jsChild.name];
+ });
+
+ return { peekData };
+ }
+};
diff --git a/app/client/src/utils/FilterInternalProperties/Widget.ts b/app/client/src/utils/FilterInternalProperties/Widget.ts
new file mode 100644
index 0000000000..f08fb159fc
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/Widget.ts
@@ -0,0 +1,34 @@
+import type { EntityDefinitionsOptions } from "ce/utils/autocomplete/EntityDefinitions";
+import type { DataTree, WidgetEntity } from "entities/DataTree/dataTreeFactory";
+import { isFunction } from "lodash";
+import WidgetFactory from "utils/WidgetFactory";
+
+export const getWidgetChildrenPeekData = (
+ widgetName: string,
+ widgetType: string,
+ dataTree: DataTree,
+) => {
+ const peekData: Record = {};
+ const dataTreeWidget: WidgetEntity = dataTree[widgetName] as WidgetEntity;
+ if (widgetType !== "FORM_WIDGET" && dataTreeWidget) {
+ const type: Exclude<
+ EntityDefinitionsOptions,
+ | "CANVAS_WIDGET"
+ | "ICON_WIDGET"
+ | "SKELETON_WIDGET"
+ | "TABS_MIGRATOR_WIDGET"
+ > = dataTreeWidget.type as any;
+ let config: any = WidgetFactory.getAutocompleteDefinitions(type);
+ if (config) {
+ if (isFunction(config)) config = config(dataTreeWidget);
+ const widgetProps = Object.keys(config).filter(
+ (k) => k.indexOf("!") === -1,
+ );
+ widgetProps.forEach((prop) => {
+ const data = dataTreeWidget[prop];
+ peekData[prop] = data;
+ });
+ }
+ }
+ return { peekData };
+};
diff --git a/app/client/src/utils/FilterInternalProperties/index.ts b/app/client/src/utils/FilterInternalProperties/index.ts
new file mode 100644
index 0000000000..1b9f9adbd2
--- /dev/null
+++ b/app/client/src/utils/FilterInternalProperties/index.ts
@@ -0,0 +1,42 @@
+import { getActionChildrenPeekData } from "./Action";
+import type {
+ DataTree,
+ DataTreeEntity,
+} from "entities/DataTree/dataTreeFactory";
+import type { JSCollectionDataState } from "reducers/entityReducers/jsActionsReducer";
+import { getWidgetChildrenPeekData } from "./Widget";
+import { getJsActionPeekData } from "./JsAction";
+import { getAppsmithPeekData } from "./Appsmith";
+import {
+ isActionEntity,
+ isWidgetEntity,
+} from "components/editorComponents/CodeEditor/codeEditorUtils";
+import {
+ isAppsmithEntity,
+ isJSAction,
+} from "ce/workers/Evaluation/evaluationUtils";
+
+export const filterInternalProperties = (
+ objectName: string,
+ dataTreeEntity: DataTreeEntity,
+ jsActions: JSCollectionDataState,
+ dataTree: DataTree,
+) => {
+ if (!dataTreeEntity) return;
+ if (isActionEntity(dataTreeEntity)) {
+ return getActionChildrenPeekData(objectName, dataTree)?.peekData;
+ } else if (isAppsmithEntity(dataTreeEntity)) {
+ return getAppsmithPeekData(dataTree).peekData;
+ } else if (isJSAction(dataTreeEntity)) {
+ const jsAction = jsActions.find(
+ (jsAction) => jsAction.config.id === dataTreeEntity.actionId,
+ );
+ return jsAction
+ ? getJsActionPeekData(jsAction, dataTree)?.peekData
+ : dataTreeEntity;
+ } else if (isWidgetEntity(dataTreeEntity)) {
+ return getWidgetChildrenPeekData(objectName, dataTreeEntity.type, dataTree)
+ ?.peekData;
+ }
+ return dataTreeEntity;
+};
diff --git a/app/client/src/utils/NavigationSelector/ActionChildren.ts b/app/client/src/utils/NavigationSelector/ActionChildren.ts
deleted file mode 100644
index 3dde65b44f..0000000000
--- a/app/client/src/utils/NavigationSelector/ActionChildren.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { entityDefinitions } from "@appsmith/utils/autocomplete/EntityDefinitions";
-import type { DataTree } from "entities/DataTree/dataTreeFactory";
-import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
-import type { ActionData } from "reducers/entityReducers/actionsReducer";
-import type { EntityNavigationData } from "selectors/navigationSelectors";
-import { createNavData } from "./common";
-import type { ActionEntity } from "entities/DataTree/types";
-
-export const getActionChildrenNavData = (
- action: ActionData,
- dataTree: DataTree,
-) => {
- const dataTreeAction = dataTree[action.config.name] as ActionEntity;
- if (dataTreeAction) {
- const definitions = entityDefinitions.ACTION(dataTreeAction, {});
- const peekData: Record = {};
- const childNavData: EntityNavigationData = {};
- Object.keys(definitions).forEach((key) => {
- if (key.indexOf("!") === -1) {
- if (key === "data" || key === "isLoading" || key === "responseMeta") {
- peekData[key] = dataTreeAction[key];
- childNavData[key] = createNavData({
- id: `${action.config.name}.${key}`,
- name: `${action.config.name}.${key}`,
- type: ENTITY_TYPE.ACTION,
- url: undefined,
- peekable: true,
- peekData: undefined,
- children: {},
- });
- } else if (key === "run" || key === "clear") {
- // eslint-disable-next-line @typescript-eslint/no-empty-function
- peekData[key] = function () {}; // tern inference required here
- childNavData[key] = createNavData({
- id: `${action.config.name}.${key}`,
- name: `${action.config.name}.${key}`,
- type: ENTITY_TYPE.ACTION,
- url: undefined,
- peekable: true,
- peekData: undefined,
- children: {},
- });
- }
- }
- });
-
- return { peekData, childNavData };
- }
-};
diff --git a/app/client/src/utils/NavigationSelector/AppsmithNavData.ts b/app/client/src/utils/NavigationSelector/AppsmithNavData.ts
deleted file mode 100644
index ee284c1509..0000000000
--- a/app/client/src/utils/NavigationSelector/AppsmithNavData.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { entityDefinitions } from "@appsmith/utils/autocomplete/EntityDefinitions";
-import type { AppsmithEntity } from "entities/DataTree/dataTreeFactory";
-import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
-import { createNavData, createObjectNavData } from "./common";
-
-export const getAppsmithNavData = (dataTree: AppsmithEntity) => {
- const defs: any = entityDefinitions.APPSMITH(dataTree, {});
-
- const result = createObjectNavData(
- defs,
- dataTree,
- "appsmith",
- {},
- {
- // restricting peek after appsmith.store because it can contain user data
- // which if large will slow down nav data generation
- "appsmith.store": true,
- },
- );
-
- return createNavData({
- id: "appsmith",
- name: "appsmith",
- type: ENTITY_TYPE.APPSMITH,
- url: undefined,
- peekable: true,
- peekData: result.peekData,
- children: result.entityNavigationData,
- });
-};
diff --git a/app/client/src/utils/NavigationSelector/JsChildren.ts b/app/client/src/utils/NavigationSelector/JsChildren.ts
index 8d95161a25..550c547948 100644
--- a/app/client/src/utils/NavigationSelector/JsChildren.ts
+++ b/app/client/src/utils/NavigationSelector/JsChildren.ts
@@ -15,31 +15,12 @@ export const getJsChildrenNavData = (
pageId: string,
dataTree: DataTree,
) => {
- const peekData: Record = {};
let childNavData: EntityNavigationData = {};
const dataTreeAction = dataTree[jsAction.config.name] as JSActionEntity;
if (dataTreeAction) {
let children: NavigationData[] = jsAction.config.actions.map((jsChild) => {
- // eslint-disable-next-line @typescript-eslint/no-empty-function
- peekData[jsChild.name] = function () {}; // can use new Function to parse string
-
- const children: EntityNavigationData = {};
- if (jsAction.data?.[jsChild.id] && jsChild.executeOnLoad) {
- (peekData[jsChild.name] as any).data = jsAction.data[jsChild.id];
- children.data = createNavData({
- id: `${jsAction.config.name}.${jsChild.name}.data`,
- name: `${jsAction.config.name}.${jsChild.name}.data`,
- type: ENTITY_TYPE.JSACTION,
- url: undefined,
- peekable: true,
- peekData: undefined,
- children: {},
- key: jsChild.name + ".data",
- });
- }
-
return createNavData({
id: `${jsAction.config.name}.${jsChild.name}`,
name: `${jsAction.config.name}.${jsChild.name}`,
@@ -49,17 +30,13 @@ export const getJsChildrenNavData = (
collectionId: jsAction.config.id,
functionName: jsChild.name,
}),
- peekable: true,
- peekData: undefined,
- children,
+ children: {},
key: jsChild.name,
});
});
const variableChildren: NavigationData[] = jsAction.config.variables.map(
(jsChild) => {
- if (dataTreeAction)
- peekData[jsChild.name] = dataTreeAction[jsChild.name];
return createNavData({
id: `${jsAction.config.name}.${jsChild.name}`,
name: `${jsAction.config.name}.${jsChild.name}`,
@@ -69,8 +46,6 @@ export const getJsChildrenNavData = (
collectionId: jsAction.config.id,
functionName: jsChild.name,
}),
- peekable: true,
- peekData: undefined,
children: {},
key: jsChild.name,
});
@@ -84,6 +59,6 @@ export const getJsChildrenNavData = (
NavigationData
>;
- return { childNavData, peekData };
+ return { childNavData };
}
};
diff --git a/app/client/src/utils/NavigationSelector/WidgetChildren.ts b/app/client/src/utils/NavigationSelector/WidgetChildren.ts
index 0040da5949..aa4fdbd40a 100644
--- a/app/client/src/utils/NavigationSelector/WidgetChildren.ts
+++ b/app/client/src/utils/NavigationSelector/WidgetChildren.ts
@@ -1,80 +1,33 @@
-import type { EntityDefinitionsOptions } from "@appsmith/utils/autocomplete/EntityDefinitions";
import type { DataTree, WidgetEntity } from "entities/DataTree/dataTreeFactory";
import { ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
-import { isFunction } from "lodash";
-import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import { builderURL } from "RouteBuilder";
import type { EntityNavigationData } from "selectors/navigationSelectors";
import { createNavData } from "./common";
-import WidgetFactory from "utils/WidgetFactory";
export const getWidgetChildrenNavData = (
- widget: FlattenedWidgetProps,
+ widgetName: string,
+ widgetType: string,
dataTree: DataTree,
pageId: string,
) => {
- const peekData: Record = {};
- const childNavData: EntityNavigationData = {};
- const dataTreeWidget: WidgetEntity = dataTree[
- widget.widgetName
- ] as WidgetEntity;
- if (widget.type === "FORM_WIDGET") {
+ const dataTreeWidget: WidgetEntity = dataTree[widgetName] as WidgetEntity;
+ if (widgetType === "FORM_WIDGET") {
const children: EntityNavigationData = {};
const formChildren: EntityNavigationData = {};
if (dataTreeWidget) {
- Object.keys(dataTreeWidget.data || {}).forEach((widgetName) => {
- const childWidgetId = (dataTree[widgetName] as WidgetEntity).widgetId;
- formChildren[widgetName] = createNavData({
- id: `${widget.widgetName}.data.${widgetName}`,
- name: widgetName,
+ Object.keys(dataTreeWidget.data || {}).forEach((childWidgetName) => {
+ const childWidgetId = (dataTree[childWidgetName] as WidgetEntity)
+ .widgetId;
+ formChildren[childWidgetName] = createNavData({
+ id: `${widgetName}.data.${childWidgetName}`,
+ name: childWidgetName,
type: ENTITY_TYPE.WIDGET,
url: builderURL({ pageId, hash: childWidgetId }),
- peekable: false,
- peekData: undefined,
children: {},
});
});
}
- children.data = createNavData({
- id: `${widget.widgetName}.data`,
- name: "data",
- type: ENTITY_TYPE.WIDGET,
- url: undefined,
- peekable: false,
- peekData: undefined,
- children: formChildren,
- });
- return { childNavData: children, peekData };
- }
- if (dataTreeWidget) {
- const type: Exclude<
- EntityDefinitionsOptions,
- | "CANVAS_WIDGET"
- | "ICON_WIDGET"
- | "SKELETON_WIDGET"
- | "TABS_MIGRATOR_WIDGET"
- > = dataTreeWidget.type as any;
- let config: any = WidgetFactory.getAutocompleteDefinitions(type);
- if (config) {
- if (isFunction(config)) config = config(dataTreeWidget);
- const widgetProps = Object.keys(config).filter(
- (k) => k.indexOf("!") === -1,
- );
- widgetProps.forEach((prop) => {
- const data = dataTreeWidget[prop];
- peekData[prop] = data;
- childNavData[prop] = createNavData({
- id: `${widget.widgetName}.${prop}`,
- name: `${widget.widgetName}.${prop}`,
- type: ENTITY_TYPE.WIDGET,
- url: undefined,
- peekable: true,
- peekData: undefined,
- children: {},
- });
- });
- }
- return { childNavData, peekData };
+ return { childNavData: children };
}
};
diff --git a/app/client/src/utils/NavigationSelector/common.ts b/app/client/src/utils/NavigationSelector/common.ts
index 986e7b134c..9b26d349ea 100644
--- a/app/client/src/utils/NavigationSelector/common.ts
+++ b/app/client/src/utils/NavigationSelector/common.ts
@@ -1,4 +1,4 @@
-import { ENTITY_TYPE } from "entities/DataTree/types";
+import type { ENTITY_TYPE } from "entities/DataTree/types";
import type {
EntityNavigationData,
NavigationData,
@@ -11,8 +11,6 @@ export const createNavData = (general: {
children: EntityNavigationData;
key?: string;
url: string | undefined;
- peekable: boolean;
- peekData: unknown;
pluginName?: string;
datasourceId?: string;
isMock?: boolean;
@@ -26,80 +24,9 @@ export const createNavData = (general: {
key: general.key,
url: general.url,
navigable: !!general.url,
- peekable: general.peekable,
- peekData: general.peekData,
pluginName: general.pluginName,
datasourceId: general.datasourceId,
isMock: general.isMock,
actionType: general.actionType,
};
};
-
-export const isTernFunctionDef = (data: any) =>
- typeof data === "string" && /^fn\((?:[\w,: \(\)->])*\) -> [\w]*$/.test(data);
-
-export const createObjectNavData = (
- defs: any,
- data: any,
- parentKey: string,
- peekData: any,
- restrictKeysFrom: Record,
-) => {
- const entityNavigationData: EntityNavigationData = {};
- Object.keys(defs).forEach((key: string) => {
- if (key.indexOf("!") === -1) {
- const childKey = parentKey + "." + key;
- if (isObject(defs[key])) {
- if (Object.keys(defs[key]).length > 0 && !restrictKeysFrom[childKey]) {
- peekData[key] = {};
- const result = createObjectNavData(
- defs[key],
- data[key],
- childKey,
- peekData[key],
- restrictKeysFrom,
- );
- peekData[key] = result.peekData;
- entityNavigationData[key] = createNavData({
- id: childKey,
- name: childKey,
- type: ENTITY_TYPE.APPSMITH,
- children: result.entityNavigationData,
- url: undefined,
- peekable: true,
- peekData: undefined,
- });
- } else {
- peekData[key] = data[key];
- entityNavigationData[key] = createNavData({
- id: childKey,
- name: childKey,
- type: ENTITY_TYPE.APPSMITH,
- children: {},
- url: undefined,
- peekable: true,
- peekData: undefined,
- });
- }
- } else {
- peekData[key] = isTernFunctionDef(defs[key])
- ? // eslint-disable-next-line @typescript-eslint/no-empty-function
- function () {} // tern inference required here
- : data[key];
- entityNavigationData[key] = createNavData({
- id: childKey,
- name: childKey,
- type: ENTITY_TYPE.APPSMITH,
- children: {},
- url: undefined,
- peekable: true,
- peekData: undefined,
- });
- }
- }
- });
- return { peekData, entityNavigationData };
-};
-
-const isObject = (data: any) =>
- typeof data === "object" && !Array.isArray(data) && data !== null;
diff --git a/app/client/yarn.lock b/app/client/yarn.lock
index f2b14c1095..eb7c2e008b 100644
--- a/app/client/yarn.lock
+++ b/app/client/yarn.lock
@@ -9576,10 +9576,10 @@ __metadata:
cypress-multi-reporters: ^1.2.4
cypress-network-idle: ^1.14.2
cypress-plugin-tab: ^1.0.5
- cypress-real-events: ^1.7.1
+ cypress-real-events: ^1.8.1
cypress-tags: ^1.1.2
cypress-wait-until: ^1.7.2
- cypress-xpath: ^1.4.0
+ cypress-xpath: ^1.6.0
dayjs: ^1.10.6
deep-diff: ^1.0.2
design-system: "npm:@appsmithorg/design-system@2.1.9"
@@ -13203,12 +13203,12 @@ __metadata:
languageName: node
linkType: hard
-"cypress-real-events@npm:^1.7.1":
- version: 1.7.1
- resolution: "cypress-real-events@npm:1.7.1"
+"cypress-real-events@npm:^1.8.1":
+ version: 1.8.1
+ resolution: "cypress-real-events@npm:1.8.1"
peerDependencies:
- cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x
- checksum: b31c2facfa03e01e298926cd0925260b12474770fc1a3ce8998da21818db7e6d9fc2f9eb60d1771aa4ce3c29aca63d04da21e1a63e3bb117b1506a72ab0c3eb1
+ cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x
+ checksum: 48f229335f26f8c225428563e94ca091ab7a00e943a4c953e062135b1d37672edec9cb7667452d894b43528b52924cd1b6061df044ccb0665b9c0965a0fe214f
languageName: node
linkType: hard
@@ -13232,10 +13232,10 @@ __metadata:
languageName: node
linkType: hard
-"cypress-xpath@npm:^1.4.0":
- version: 1.6.0
- resolution: "cypress-xpath@npm:1.6.0"
- checksum: 67cc5613d70a090b1a162d31d0ef11831576592c87ca07d242e2c07a6df2fca413fc81ea7df148a0d0c888025bb7ec0dad34684f2a322eeaa2863b8f3b65dd32
+"cypress-xpath@npm:^1.6.0":
+ version: 1.8.0
+ resolution: "cypress-xpath@npm:1.8.0"
+ checksum: 11792ec46b898f2e00ced81dedcdca8e30d5c232778780210423d1ec0b07cd8b2a29a475d38eadaf9fd7e962efec7f5cee17f92884c308636cfa506efe6017e6
languageName: node
linkType: hard