diff --git a/app/client/src/components/editorComponents/ActionCreator/Field/FieldConfig.ts b/app/client/src/components/editorComponents/ActionCreator/Field/FieldConfig.ts index 88d9d2247640..5a67e28befe6 100644 --- a/app/client/src/components/editorComponents/ActionCreator/Field/FieldConfig.ts +++ b/app/client/src/components/editorComponents/ActionCreator/Field/FieldConfig.ts @@ -38,6 +38,19 @@ import { FIELD_GROUP_CONFIG } from "../FieldGroup/FieldGroupConfig"; import { getFunctionName, checkIfArgumentExistAtPosition } from "@shared/ast"; export const FIELD_CONFIG: AppsmithFunctionConfigType = { + [FieldType.APP_NAME_FIELD]: { + label: () => "Enter App Name", + defaultText: "", + exampleText: "navigateTo('', {}, 'SAME_WINDOW', 'MyApp')", + options: () => null, + getter: (value: string) => { + return textGetter(value, 3); + }, + setter: (value, currentValue) => { + return textSetter(value, currentValue, 3); + }, + view: ViewTypes.TEXT_VIEW, + }, [FieldType.ACTION_SELECTOR_FIELD]: { label: (props: FieldProps) => props.label || "", options: (props: FieldProps) => props.integrationOptions, diff --git a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.test.ts b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.test.ts index f3e34f2c4422..38a7e263e7e4 100644 --- a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.test.ts +++ b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.test.ts @@ -30,6 +30,7 @@ describe("Test Field Group Config", () => { FieldType.PAGE_SELECTOR_FIELD, FieldType.QUERY_PARAMS_FIELD, FieldType.NAVIGATION_TARGET_FIELD, + FieldType.APP_NAME_FIELD, ], }, { diff --git a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.ts b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.ts index 4344a0486266..7146bde09e7d 100644 --- a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.ts +++ b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/FieldGroupConfig.ts @@ -57,8 +57,9 @@ export const FIELD_GROUP_CONFIG: FieldGroupConfig = { FieldType.PAGE_SELECTOR_FIELD, FieldType.QUERY_PARAMS_FIELD, FieldType.NAVIGATION_TARGET_FIELD, + FieldType.APP_NAME_FIELD, ], - defaultParams: `"", {}, 'SAME_WINDOW'`, + defaultParams: `"", {}, 'SAME_WINDOW', ""`, icon: "page-line", }, [AppsmithFunction.showAlert]: { diff --git a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/index.tsx b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/index.tsx index eadd5fc1d6c0..37afdffd2342 100644 --- a/app/client/src/components/editorComponents/ActionCreator/FieldGroup/index.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/FieldGroup/index.tsx @@ -25,6 +25,13 @@ function FieldGroup(props: FieldGroupProps) { setActiveTabNavigateTo(NAVIGATE_TO_TAB_SWITCHER[1]); }, }, + { + id: "app-name", + text: "App name", + action: () => { + setActiveTabNavigateTo(NAVIGATE_TO_TAB_SWITCHER[2]); + }, + }, ]; const apiAndQueryCallbackTabSwitches: SwitchType[] = [ diff --git a/app/client/src/components/editorComponents/ActionCreator/constants.ts b/app/client/src/components/editorComponents/ActionCreator/constants.ts index 859e75217316..47eb1608cf05 100644 --- a/app/client/src/components/editorComponents/ActionCreator/constants.ts +++ b/app/client/src/components/editorComponents/ActionCreator/constants.ts @@ -82,6 +82,7 @@ export const ViewTypes = { export const NAVIGATE_TO_TAB_OPTIONS = { PAGE_NAME: "page-name", URL: "url", + APP_NAME: "app-name", }; export const NEW_MODAL_LABEL = "New Modal"; @@ -95,6 +96,7 @@ export const EMPTY_BINDING = "{{}}"; export const EMPTY_BINDING_WITH_EMPTY_OBJECT = "{{{}}}"; export enum FieldType { + APP_NAME_FIELD = "APP_NAME_FIELD", PARAMS_FIELD = "PARAMS_FIELD", ACTION_SELECTOR_FIELD = "ACTION_SELECTOR_FIELD", ON_SUCCESS_FIELD = "ON_SUCCESS_FIELD", diff --git a/app/client/src/components/editorComponents/ActionCreator/helpers.tsx b/app/client/src/components/editorComponents/ActionCreator/helpers.tsx index 8648b6e9d3f5..ef87fc968930 100644 --- a/app/client/src/components/editorComponents/ActionCreator/helpers.tsx +++ b/app/client/src/components/editorComponents/ActionCreator/helpers.tsx @@ -364,13 +364,16 @@ function getFieldsForSelectedAction( * if PAGE_NAME then this field will be PAGE_SELECTOR_FIELD (default) * if URL then this field will be URL_FIELD **/ - if ( - functionMatch === APPSMITH_GLOBAL_FUNCTIONS.navigateTo && - activeTabNavigateTo.id === NAVIGATE_TO_TAB_OPTIONS.URL - ) { - fields[2] = { - field: FieldType.URL_FIELD, - }; + if (functionMatch === APPSMITH_GLOBAL_FUNCTIONS.navigateTo) { + if (activeTabNavigateTo.id === NAVIGATE_TO_TAB_OPTIONS.URL) { + fields[2] = { + field: FieldType.URL_FIELD, + }; + } else if (activeTabNavigateTo.id === NAVIGATE_TO_TAB_OPTIONS.APP_NAME) { + fields[2] = { + field: FieldType.APP_NAME_FIELD, + }; + } } return fields; diff --git a/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts b/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts index 7c4df1d906ac..f97f8d8df0c8 100644 --- a/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/NavigateActionSaga.ts @@ -4,18 +4,20 @@ import _ from "lodash"; import { ReduxActionTypes } from "ee/constants/ReduxActionConstants"; import type { Page } from "entities/Page"; import AnalyticsUtil from "ee/utils/AnalyticsUtil"; -import { getAppMode } from "ee/selectors/applicationSelectors"; +import { getAppMode, getApplicationList } from "ee/selectors/applicationSelectors"; import { APP_MODE } from "entities/App"; import { getQueryStringfromObject } from "ee/entities/URLRedirect/URLAssembly"; import history from "utils/history"; import { setDataUrl } from "ee/sagas/PageSagas"; import AppsmithConsole from "utils/AppsmithConsole"; -import { builderURL, viewerURL } from "ee/RouteBuilder"; +import urlBuilder from "ce/entities/URLRedirect/URLAssembly"; import { TriggerFailureError } from "./errorUtils"; import { isValidURL, matchesURLPattern } from "utils/URLUtils"; import type { TNavigateToDescription } from "workers/Evaluation/fns/navigateTo"; import { NavigationTargetType } from "workers/Evaluation/fns/navigateTo"; import type { SourceEntity } from "entities/AppsmithConsole"; +import type { ApplicationPayload } from "entities/Application"; +import type { SagaIterator } from "redux-saga"; export enum NavigationTargetType_Dep { SAME_WINDOW = "SAME_WINDOW", @@ -32,32 +34,68 @@ const isValidPageName = ( export default function* navigateActionSaga( action: TNavigateToDescription, source?: SourceEntity, -) { +): SagaIterator { const { payload } = action; - const pageList: Page[] = yield select(getPageList); - const { pageNameOrUrl, params, target } = payload; + const pageList = (yield select(getPageList)) as Page[]; + const { pageNameOrUrl, params, target, appName } = payload; + + if (appName) { + const applicationList = (yield select(getApplicationList)) as ApplicationPayload[]; + const app = applicationList.find((app) => app.name === appName); + + if (!app) { + throw new TriggerFailureError(`No application found with name: ${appName}`); + } + + const appMode = (yield select(getAppMode)) as APP_MODE; + const path = urlBuilder.build( + { + basePageId: app.defaultPageId, + params, + }, + appMode, + ); + + if (target === NavigationTargetType.SAME_WINDOW) { + window.location.assign(path); + } else if (target === NavigationTargetType.NEW_WINDOW) { + window.open(path, "_blank"); + } + + AppsmithConsole.info({ + source, + text: `navigateTo('${appName}') was triggered`, + state: { + params, + }, + }); + + AnalyticsUtil.logEvent("NAVIGATE", { + appName, + pageParams: params, + }); + + return; + } const page = isValidPageName(pageNameOrUrl, pageList); if (page) { - const currentPageId: string = yield select(getCurrentPageId); + const currentPageId = (yield select(getCurrentPageId)) as string; AnalyticsUtil.logEvent("NAVIGATE", { pageName: pageNameOrUrl, pageParams: params, }); - const appMode: APP_MODE = yield select(getAppMode); - const path = - appMode === APP_MODE.EDIT - ? builderURL({ - basePageId: page.basePageId, - params, - }) - : viewerURL({ - basePageId: page.basePageId, - params, - }); + const appMode = (yield select(getAppMode)) as APP_MODE; + const path = urlBuilder.build( + { + basePageId: page.basePageId, + params, + }, + appMode, + ); if (target === NavigationTargetType.SAME_WINDOW) { history.push(path); diff --git a/app/client/src/workers/Evaluation/__tests__/Actions.test.ts b/app/client/src/workers/Evaluation/__tests__/Actions.test.ts index aafe688b839f..4c5958c14f70 100644 --- a/app/client/src/workers/Evaluation/__tests__/Actions.test.ts +++ b/app/client/src/workers/Evaluation/__tests__/Actions.test.ts @@ -123,6 +123,41 @@ describe("Add functions", () => { ); }); + it("navigateTo with app name works", () => { + const pageNameOrUrl = ""; + const params = "{ param1: value1 }"; + const target = "NEW_WINDOW"; + const appName = "TestApp"; + + expect( + evalContext.navigateTo(pageNameOrUrl, params, target, appName), + ).resolves.toBe({}); + expect(workerEventMock).lastCalledWith( + messageCreator("PROCESS_TRIGGER", { + data: { + enableJSFnPostProcessors: true, + enableJSVarUpdateTracking: true, + trigger: { + type: "NAVIGATE_TO", + payload: { + pageNameOrUrl, + params, + target, + appName, + }, + }, + eventType: undefined, + triggerMeta: { + source: {}, + triggerPropertyName: undefined, + onPageLoad: false, + }, + }, + method: "PROCESS_TRIGGER", + }), + ); + }); + it("showAlert works", () => { const message = "Alert message"; const style = "info"; diff --git a/app/client/src/workers/Evaluation/fns/navigateTo.ts b/app/client/src/workers/Evaluation/fns/navigateTo.ts index cdd4d1568e81..500c1d89715d 100644 --- a/app/client/src/workers/Evaluation/fns/navigateTo.ts +++ b/app/client/src/workers/Evaluation/fns/navigateTo.ts @@ -5,14 +5,22 @@ export enum NavigationTargetType { NEW_WINDOW = "NEW_WINDOW", } +/** + * Navigates to a page, URL, or application + * @param pageNameOrUrl - Name of the page or URL to navigate to + * @param params - Query parameters for the navigation + * @param target - Target window for navigation (same window or new window) + * @param appName - Optional name of the application to navigate to + */ function navigateToFnDescriptor( pageNameOrUrl: string, - params: Record, + params: Record = {}, target = NavigationTargetType.SAME_WINDOW, + appName?: string, ) { return { type: "NAVIGATE_TO" as const, - payload: { pageNameOrUrl, params, target }, + payload: { pageNameOrUrl, params, target, appName }, }; } @@ -20,6 +28,9 @@ export type TNavigateToArgs = Parameters; export type TNavigateToDescription = ReturnType; export type TNavigateToActionType = TNavigateToDescription["type"]; +// Helper type for the payload +export type TNavigateToPayload = TNavigateToDescription["payload"]; + async function navigateTo(...args: Parameters) { return promisify(navigateToFnDescriptor)(...args); }