Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test-storybook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ jobs:

- name: Storybook Tests
working-directory: ./app/client/packages/storybook
env:
# The Storybook test-runner (Jest + Playwright over every story) exceeds
# Node's ~2GB default old-space heap on the runner and OOMs (exit 129).
# Raise the limit; the runner has ample RAM.
NODE_OPTIONS: --max-old-space-size=8192
run: |
yarn build-storybook
yarn test-storybook:ci
54 changes: 40 additions & 14 deletions app/client/cypress/support/Objects/FeatureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,50 @@ const defaultFlags = {
export const featureFlagIntercept = (
flags: Record<string, boolean> = {},
reload = true,
// When true, the requested flags are OVERLAID on the real feature flags from the
// backend instead of REPLACING them. The replace behavior drops every flag the test
// didn't list — including editor-infrastructure flags (e.g. release_app_sidebar_enabled)
// that the IDE needs to render at all. Tests that open the full editor (Anvil/AI) must
// preserve those, or the editor never mounts (.t--sidebar-Editor never appears).
preserveOtherFlags = false,
) => {
getConsolidatedDataApi({ ...flags, ...defaultFlags }, false);
const response = {
responseMeta: {
status: 200,
success: true,
},
data: {
...flags,
...defaultFlags,
},
errorDisplay: "",
};
cy.intercept("GET", "/api/v1/users/features", response);
getConsolidatedDataApi(
{ ...flags, ...defaultFlags },
false,
preserveOtherFlags,
);
if (preserveOtherFlags) {
cy.intercept("GET", "/api/v1/users/features", (req) => {
req.reply((res: any) => {
const original = res?.body?.data ?? {};
res.send({
responseMeta: { status: 200, success: true },
data: { ...original, ...flags, ...defaultFlags },
errorDisplay: "",
});
});
});
} else {
const response = {
responseMeta: {
status: 200,
success: true,
},
data: {
...flags,
...defaultFlags,
},
errorDisplay: "",
};
cy.intercept("GET", "/api/v1/users/features", response);
}
if (reload) ObjectsRegistry.AggregateHelper.CypressReload();
};

export const getConsolidatedDataApi = (
flags: Record<string, boolean> = {},
reload = true,
preserveOtherFlags = false,
) => {
cy.intercept("GET", "/api/v1/consolidated-api/*?*", (req) => {
delete req.headers["if-none-match"];
Expand All @@ -43,7 +67,9 @@ export const getConsolidatedDataApi = (
const originalResponse = res?.body;
try {
const updatedResponse = JSON.parse(JSON.stringify(originalResponse));
updatedResponse.data.featureFlags.data = { ...flags };
updatedResponse.data.featureFlags.data = preserveOtherFlags
? { ...updatedResponse.data.featureFlags.data, ...flags }
: { ...flags };
return res.send(updatedResponse);
} catch (e) {
cy.log(`Featureflags.ts error `, e);
Expand Down
12 changes: 10 additions & 2 deletions app/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,14 @@
"fast-csv": "4.3.6",
"json-schema": "0.4.0",
"node-fetch": "2.6.7",
"minimatch": "^5.1.8",
"minimatch@^3.0.3": "^5.1.8",
"minimatch@^3.0.4": "^5.1.8",
"minimatch@^3.0.5": "^5.1.8",
"minimatch@^3.1.1": "^5.1.8",
"minimatch@^3.1.2": "^5.1.8",
"minimatch@~3.0.2": "^5.1.8",
"minimatch@~3.0.4": "^5.1.8",
"minimatch@3.0.4": "^5.1.8",
"loader-utils": "^2.0.4",
"json5": "2.2.3",
"dns-packet": "5.4.0",
Expand Down Expand Up @@ -480,6 +487,7 @@
"joi": "^18.2.1",
"ws@^6.1.0": "6.2.4",
"launch-editor": "2.14.1",
"@babel/core": "7.29.6"
"@babel/core": "7.29.6",
"http-proxy-middleware": "2.0.10"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ function getWidgetProps(
parentRowSpace: 10,
};
case "TABLE_WIDGET_V2":
case "WDS_TABLE_WIDGET":
return {
type: "TABLE_WIDGET_V2",
type: suggestedWidget.type,
props: {
[fieldName]: `{{${actionName}.${suggestedWidget.bindingQuery}}}`,
dynamicBindingPathList: [{ key: "tableData" }],
Expand Down Expand Up @@ -276,11 +277,16 @@ function BindDataButton(props: BindDataButtonProps) {
const isAnvilLayout = useSelector(getIsAnvilLayout);
// The purpose of this filter is to make sure that if Anvil is enabled
// only those widgets which have an alternative in Anvil are listed
// for selection for adding a new suggested widget
// for selection for adding a new suggested widget. A suggestion survives
// the filter if it is a legacy type with a WDS counterpart (a map key,
// e.g. TABLE_WIDGET_V2) or already a WDS type (a map value, e.g.
// WDS_TABLE_WIDGET).
const filteredSuggestedWidgets =
isAnvilLayout && suggestedWidgets
? suggestedWidgets.filter((each) =>
Object.keys(WDS_V2_WIDGET_MAP).includes(each.type),
? suggestedWidgets.filter(
(each) =>
Object.keys(WDS_V2_WIDGET_MAP).includes(each.type) ||
Object.values(WDS_V2_WIDGET_MAP).includes(each.type),
)
: suggestedWidgets;

Expand Down
34 changes: 34 additions & 0 deletions app/client/src/actions/copyToAppActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import type {
CopyEntityToAppPayload,
CopyToAppModalEntity,
} from "pages/Editor/Explorer/CopyToApp/types";

export const openCopyToAppModal = (payload: CopyToAppModalEntity) => ({
type: ReduxActionTypes.OPEN_COPY_ENTITY_TO_APP_MODAL,
payload,
});

export const closeCopyToAppModal = () => ({
type: ReduxActionTypes.CLOSE_COPY_ENTITY_TO_APP_MODAL,
});

export const copyActionToApp = (payload: CopyEntityToAppPayload) => ({
type: ReduxActionTypes.COPY_ACTION_TO_APP_INIT,
payload,
});

export const copyJSActionToApp = (payload: CopyEntityToAppPayload) => ({
type: ReduxActionTypes.COPY_JS_ACTION_TO_APP_INIT,
payload,
});

export const fetchAppsForCopyTarget = (workspaceId: string) => ({
type: ReduxActionTypes.FETCH_COPY_TARGET_APPLICATIONS_INIT,
payload: { workspaceId },
});

export const fetchPagesForCopyTarget = (applicationId: string) => ({
type: ReduxActionTypes.FETCH_COPY_TARGET_PAGES_INIT,
payload: { applicationId },
});
15 changes: 15 additions & 0 deletions app/client/src/ce/constants/ReduxActionConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,17 @@ const ImportExportActionTypes = {
PARTIAL_EXPORT_MODAL_OPEN: "PARTIAL_EXPORT_MODAL_OPEN",
PARTIAL_EXPORT_INIT: "PARTIAL_EXPORT_INIT",
PARTIAL_EXPORT_SUCCESS: "PARTIAL_EXPORT_SUCCESS",
OPEN_COPY_ENTITY_TO_APP_MODAL: "OPEN_COPY_ENTITY_TO_APP_MODAL",
CLOSE_COPY_ENTITY_TO_APP_MODAL: "CLOSE_COPY_ENTITY_TO_APP_MODAL",
COPY_ACTION_TO_APP_INIT: "COPY_ACTION_TO_APP_INIT",
COPY_ACTION_TO_APP_SUCCESS: "COPY_ACTION_TO_APP_SUCCESS",
COPY_JS_ACTION_TO_APP_INIT: "COPY_JS_ACTION_TO_APP_INIT",
COPY_JS_ACTION_TO_APP_SUCCESS: "COPY_JS_ACTION_TO_APP_SUCCESS",
FETCH_COPY_TARGET_APPLICATIONS_INIT: "FETCH_COPY_TARGET_APPLICATIONS_INIT",
FETCH_COPY_TARGET_APPLICATIONS_SUCCESS:
"FETCH_COPY_TARGET_APPLICATIONS_SUCCESS",
FETCH_COPY_TARGET_PAGES_INIT: "FETCH_COPY_TARGET_PAGES_INIT",
FETCH_COPY_TARGET_PAGES_SUCCESS: "FETCH_COPY_TARGET_PAGES_SUCCESS",
IMPORT_APPLICATION_INIT: "IMPORT_APPLICATION_INIT",
IMPORT_APPLICATION_FROM_GIT_INIT: "IMPORT_APPLICATION_FROM_GIT_INIT",
IMPORT_APPLICATION_SUCCESS: "IMPORT_APPLICATION_SUCCESS",
Expand All @@ -233,6 +244,10 @@ const ImportExportActionErrorTypes = {
IMPORT_APPLICATION_ERROR: "IMPORT_APPLICATION_ERROR",
PARTIAL_IMPORT_ERROR: "PARTIAL_IMPORT_ERROR",
PARTIAL_EXPORT_ERROR: "PARTIAL_EXPORT_ERROR",
COPY_ACTION_TO_APP_ERROR: "COPY_ACTION_TO_APP_ERROR",
COPY_JS_ACTION_TO_APP_ERROR: "COPY_JS_ACTION_TO_APP_ERROR",
FETCH_COPY_TARGET_APPLICATIONS_ERROR: "FETCH_COPY_TARGET_APPLICATIONS_ERROR",
FETCH_COPY_TARGET_PAGES_ERROR: "FETCH_COPY_TARGET_PAGES_ERROR",
};

const ImportGitActionTypes = {
Expand Down
31 changes: 31 additions & 0 deletions app/client/src/ce/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ export const FIXED_APPLICATIONS = () => `Classic Applications`;
export const AI_AGENTS_APPLICATIONS = () => `AI Agents`;
export const AI_APPLICATION_CARD_LIST_ZERO_STATE = () =>
`There are no AI Agents in this workspace.`;
export const ANVIL_APPLICATIONS = () => `Anvil apps`;
export const ANVIL_APPLICATION_CARD_LIST_ZERO_STATE = () =>
`There are no Anvil apps in this workspace yet.`;
export const NEW_ANVIL_APP = () => `Anvil app`;
export const AI_AGENT_AUTH_SUBTITLE = () =>
`Sign up with any Google account.\n Support for email will be available soon.`;

Expand Down Expand Up @@ -1841,6 +1845,7 @@ export const CONTEXT_RENAME = () => "Rename";
export const CONTEXT_SHOW_BINDING = () => "Show bindings";
export const CONTEXT_MOVE = () => "Move to page";
export const CONTEXT_COPY = () => "Copy to page";
export const CONTEXT_COPY_TO_APP = () => "Copy to application";
export const CONTEXT_DUPLICATE = () => "Duplicate";
export const CONTEXT_DELETE = () => "Delete";
export const CONFIRM_CONTEXT_DELETE = () => "Are you sure?";
Expand Down Expand Up @@ -1924,6 +1929,32 @@ export const FORK_APP_MODAL_SUCCESS_TITLE = () =>
"Choose where to fork the app";
export const FORK = () => `Fork`;

export const COPY_ENTITY_TO_APP_MODAL_TITLE = () => "Copy to application";
export const COPY_ENTITY_TO_APP_WORKSPACE_LABEL = () => "Workspace";
export const COPY_ENTITY_TO_APP_APPLICATION_LABEL = () => "Application";
export const COPY_ENTITY_TO_APP_PAGE_LABEL = () => "Page";
export const COPY_ENTITY_TO_APP_WORKSPACE_PLACEHOLDER = () =>
"Select a workspace";
export const COPY_ENTITY_TO_APP_APPLICATION_PLACEHOLDER = () =>
"Select an application";
export const COPY_ENTITY_TO_APP_PAGE_PLACEHOLDER = () => "Select a page";
export const COPY_ENTITY_TO_APP_CONFIRM = () => "Copy";
export const COPY_ENTITY_TO_APP_NO_WORKSPACES = () =>
"No workspaces available to copy to";
export const COPY_ENTITY_TO_APP_NO_APPS = () =>
"No applications available in this workspace";
export const COPY_ENTITY_TO_APP_NO_PAGES = () =>
"No pages available in this application";
export const COPY_ENTITY_TO_APP_SUCCESS = (
entityName: string,
appName: string,
pageName: string,
) => `Copied ${entityName} to ${appName} / ${pageName}`;
export const COPY_ENTITY_TO_APP_ERROR = () =>
"Failed to copy to the selected application";
export const COPY_ENTITY_TO_APP_NOTE = () =>
"Datasource credentials and any referenced queries aren't copied. Datasources are set up during import, but you may need to re-enter credentials and recreate any referenced queries before the copied item will work in the target application.";

export const CLEAN_URL_UPDATE = {
name: () => "Update URLs",
shortDesc: () =>
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const FEATURE_FLAG = {
"release_show_publish_app_to_community_enabled",
license_gac_enabled: "license_gac_enabled",
release_anvil_enabled: "release_anvil_enabled",
license_anvil_enabled: "license_anvil_enabled",
license_ai_agent_enabled: "license_ai_agent_enabled",
license_git_branch_protection_enabled:
"license_git_branch_protection_enabled",
Expand Down Expand Up @@ -87,6 +88,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_show_publish_app_to_community_enabled: false,
license_gac_enabled: false,
release_anvil_enabled: false,
license_anvil_enabled: false,
license_ai_agent_enabled: false,
release_drag_drop_building_blocks_enabled: false,
license_git_branch_protection_enabled: false,
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/pages/AppIDE/components/AppIDEModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { PartialExportModal } from "components/editorComponents/PartialImportExp
import { PartialImportModal } from "components/editorComponents/PartialImportExport/PartialImportModal";
import { AppCURLImportModal } from "ee/pages/Editor/CurlImport";
import GeneratePageModal from "pages/Editor/GeneratePage";
import CopyEntityToAppModal from "pages/Editor/Explorer/CopyToApp/CopyEntityToAppModal";

export function AppIDEModals() {
return (
Expand All @@ -21,6 +22,7 @@ export function AppIDEModals() {
<PartialImportModal />
<AppCURLImportModal />
<GeneratePageModal />
<CopyEntityToAppModal />
</>
);
}
6 changes: 5 additions & 1 deletion app/client/src/ce/pages/Applications/WorkspaceAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
createMessage,
} from "ee/constants/messages";
import type { Workspace } from "ee/constants/workspaceConstants";
import type { LayoutSystemTypes } from "layoutSystems/types";
import { getIsCreatingApplicationByWorkspaceId } from "ee/selectors/applicationSelectors";
import { getIsFetchingApplications } from "ee/selectors/selectedWorkspaceSelectors";
import { hasCreateNewAppPermission } from "ee/utils/permissionHelpers";
Expand All @@ -27,7 +28,10 @@ export interface WorkspaceActionProps {
isMobile: boolean;
enableImportExport: boolean;
workspaceId: string;
onCreateNewApplication: (workspaceId: string) => void;
onCreateNewApplication: (
workspaceId: string,
layoutSystemType?: LayoutSystemTypes,
) => void;
onStartFromTemplate: (workspaceId: string) => void;
setSelectedWorkspaceIdForImportApplication: (workspaceId?: string) => void;
}
Expand Down
Loading
Loading