chore: decouple widget-config.json from main chunk (#36924)
## Description We decoupled the widget-config.json file (177KB when zipped) from the main bundle and implemented lazy loading during DSL migrations. This optimisation reduces the size of the main chunk by 10%, which should lead to better performance, specifically improving FCP and LCP, as the main chunk's fetch and evaluation times are reduced Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/11384816747> > Commit: ae14b3f40c4c1ef77250157e56aed97acae5cbaf > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11384816747&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Thu, 17 Oct 2024 15:04:34 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced migration logic for DSL widgets with asynchronous operations. - Improved performance with lazy loading of widget configurations. - Added optional properties to page response data structures. - **Bug Fixes** - Resolved issues with dynamic binding path migrations. - **Tests** - Updated test cases to handle asynchronous operations for better reliability and accuracy. - **Chores** - Improved type safety and error handling in widget factory utilities and mock functions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
ebec99c407
commit
b122c8195f
|
|
@ -146,7 +146,7 @@ const migrateUnversionedDSL = (currentDSL: DSLWidget) => {
|
|||
// A rudimentary transform function which updates the DSL based on its version.
|
||||
// A more modular approach needs to be designed.
|
||||
// This needs the widget config to be already built to migrate correctly
|
||||
const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => {
|
||||
const migrateVersionedDSL = async (currentDSL: DSLWidget, newPage = false) => {
|
||||
if (currentDSL.version === 1) {
|
||||
if (currentDSL.children && currentDSL.children.length > 0)
|
||||
currentDSL.children = currentDSL.children.map(updateContainers);
|
||||
|
|
@ -205,7 +205,7 @@ const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => {
|
|||
}
|
||||
|
||||
if (currentDSL.version === 12) {
|
||||
currentDSL = migrateIncorrectDynamicBindingPathLists(currentDSL);
|
||||
currentDSL = await migrateIncorrectDynamicBindingPathLists(currentDSL);
|
||||
currentDSL.version = 13;
|
||||
}
|
||||
|
||||
|
|
@ -619,15 +619,15 @@ const migrateVersionedDSL = (currentDSL: DSLWidget, newPage = false) => {
|
|||
return currentDSL;
|
||||
};
|
||||
|
||||
export const migrateDSL = (
|
||||
export const migrateDSL = async (
|
||||
currentDSL: DSLWidget,
|
||||
newPage = false,
|
||||
): DSLWidget => {
|
||||
): Promise<DSLWidget> => {
|
||||
if (currentDSL.version === undefined) {
|
||||
const initialDSL = migrateUnversionedDSL(currentDSL);
|
||||
|
||||
return migrateVersionedDSL(initialDSL, newPage) as DSLWidget;
|
||||
return (await migrateVersionedDSL(initialDSL, newPage)) as DSLWidget;
|
||||
} else {
|
||||
return migrateVersionedDSL(currentDSL, newPage) as DSLWidget;
|
||||
return (await migrateVersionedDSL(currentDSL, newPage)) as DSLWidget;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import isString from "lodash/isString";
|
|||
import memoize from "micro-memoize";
|
||||
import { isObject, isUndefined } from "lodash";
|
||||
import { generateReactKey, isDynamicValue } from "../utils";
|
||||
import widgetConfigs from "../helpers/widget-configs.json";
|
||||
|
||||
export const WidgetHeightLimits = {
|
||||
MAX_HEIGHT_IN_ROWS: 9000,
|
||||
|
|
@ -511,8 +510,33 @@ export function addSearchConfigToPanelConfig(config: readonly any[]) {
|
|||
return configItem;
|
||||
});
|
||||
}
|
||||
// Cache for lazy-loaded widget configurations
|
||||
let cachedWidgetConfigs: any | null = null;
|
||||
|
||||
const getWidgetPropertyPaneContentConfig = (type: string): readonly any[] => {
|
||||
/*
|
||||
Lazily load this file since it is very large and used in migrations for certain DSL versions. By lazily loading
|
||||
this large file it can be reduce the main chunk only be loaded for certain limited conditions.
|
||||
*/
|
||||
const loadWidgetConfig = async () => {
|
||||
if (!cachedWidgetConfigs) {
|
||||
try {
|
||||
const { default: widgetConfigs } = await import(
|
||||
"../helpers/widget-configs.json"
|
||||
);
|
||||
|
||||
cachedWidgetConfigs = widgetConfigs; // Cache the module for future use
|
||||
} catch (e) {
|
||||
log.error("Error loading WidgetConfig", e);
|
||||
}
|
||||
}
|
||||
|
||||
return cachedWidgetConfigs;
|
||||
};
|
||||
|
||||
const getWidgetPropertyPaneContentConfig = async (
|
||||
type: string,
|
||||
): Promise<readonly any[]> => {
|
||||
const widgetConfigs = await loadWidgetConfig();
|
||||
const propertyPaneContentConfig = (widgetConfigs as any)[type]
|
||||
.propertyPaneContentConfig;
|
||||
|
||||
|
|
@ -540,7 +564,11 @@ const getWidgetPropertyPaneContentConfig = (type: string): readonly any[] => {
|
|||
}
|
||||
};
|
||||
|
||||
const getWidgetPropertyPaneStyleConfig = (type: string): readonly any[] => {
|
||||
const getWidgetPropertyPaneStyleConfig = async (
|
||||
type: string,
|
||||
): Promise<readonly any[]> => {
|
||||
const widgetConfigs = await loadWidgetConfig();
|
||||
|
||||
const propertyPaneStyleConfig = (widgetConfigs as any)[type]
|
||||
.propertyPaneStyleConfig;
|
||||
|
||||
|
|
@ -567,14 +595,20 @@ const getWidgetPropertyPaneStyleConfig = (type: string): readonly any[] => {
|
|||
}
|
||||
};
|
||||
|
||||
const getWidgetPropertyPaneCombinedConfig = (type: string): readonly any[] => {
|
||||
const contentConfig = getWidgetPropertyPaneContentConfig(type);
|
||||
const styleConfig = getWidgetPropertyPaneStyleConfig(type);
|
||||
const getWidgetPropertyPaneCombinedConfig = async (
|
||||
type: string,
|
||||
): Promise<readonly any[]> => {
|
||||
const contentConfig = await getWidgetPropertyPaneContentConfig(type);
|
||||
const styleConfig = await getWidgetPropertyPaneStyleConfig(type);
|
||||
|
||||
return [...contentConfig, ...styleConfig];
|
||||
};
|
||||
|
||||
const getWidgetPropertyPaneConfig = (type: string): readonly any[] => {
|
||||
const getWidgetPropertyPaneConfig = async (
|
||||
type: string,
|
||||
): Promise<readonly any[]> => {
|
||||
const widgetConfigs = await loadWidgetConfig();
|
||||
|
||||
const propertyPaneConfig = (widgetConfigs as any)[type].propertyPaneConfig;
|
||||
|
||||
const features = (widgetConfigs as any)[type].features;
|
||||
|
|
@ -590,7 +624,7 @@ const getWidgetPropertyPaneConfig = (type: string): readonly any[] => {
|
|||
|
||||
return enhancedPropertyPaneConfig;
|
||||
} else {
|
||||
const config = getWidgetPropertyPaneCombinedConfig(type);
|
||||
const config = await getWidgetPropertyPaneCombinedConfig(type);
|
||||
|
||||
if (config === undefined) {
|
||||
log.error("Widget property pane config not defined", type);
|
||||
|
|
@ -957,14 +991,14 @@ const getAllPathsFromPropertyConfig = memoize(
|
|||
{ maxSize: 1000 },
|
||||
);
|
||||
|
||||
export const migrateIncorrectDynamicBindingPathLists = (
|
||||
export const migrateIncorrectDynamicBindingPathLists = async (
|
||||
currentDSL: Readonly<DSLWidget>,
|
||||
): DSLWidget => {
|
||||
): Promise<DSLWidget> => {
|
||||
const migratedDsl = {
|
||||
...currentDSL,
|
||||
};
|
||||
const dynamicBindingPathList: any[] = [];
|
||||
const propertyPaneConfig = getWidgetPropertyPaneConfig(currentDSL.type);
|
||||
const propertyPaneConfig = await getWidgetPropertyPaneConfig(currentDSL.type);
|
||||
const { bindingPaths } = getAllPathsFromPropertyConfig(
|
||||
currentDSL,
|
||||
propertyPaneConfig,
|
||||
|
|
@ -984,8 +1018,10 @@ export const migrateIncorrectDynamicBindingPathLists = (
|
|||
migratedDsl.dynamicBindingPathList = dynamicBindingPathList;
|
||||
|
||||
if (currentDSL.children) {
|
||||
migratedDsl.children = currentDSL.children.map(
|
||||
migrateIncorrectDynamicBindingPathLists,
|
||||
migratedDsl.children = await Promise.all(
|
||||
currentDSL.children.map(async (value) =>
|
||||
migrateIncorrectDynamicBindingPathLists(value),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -938,85 +938,82 @@ describe("Test all the migrations are running", () => {
|
|||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
migrations.forEach((migration: Migration) => {
|
||||
/**
|
||||
* Generates mock fucntion for each migration function.
|
||||
* Mocks the implementation
|
||||
*/
|
||||
const version = migration.version ?? 0;
|
||||
test("assert migration functions being called when migrate dsl has been called ", async () => {
|
||||
migrations.forEach((migration: Migration) => {
|
||||
/**
|
||||
* Generates mock fucntion for each migration function.
|
||||
* Mocks the implementation
|
||||
*/
|
||||
const version = migration.version ?? 0;
|
||||
|
||||
mockFnObj[version] = [];
|
||||
mockFnObj[version] = [];
|
||||
|
||||
migration.functionLookup.forEach((lookup) => {
|
||||
const { functionName, moduleObj } = lookup;
|
||||
|
||||
if (moduleObj) {
|
||||
mockFnObj[version].push({
|
||||
spyOnFunc: jest
|
||||
.spyOn(moduleObj, functionName)
|
||||
.mockImplementation((dsl: any) => {
|
||||
/**
|
||||
* We need to delete the children property on the first migration(calculateDynamicHeight),
|
||||
* to avoid the recursion in the second migration(updateContainers)
|
||||
*/
|
||||
dsl && delete dsl.children;
|
||||
|
||||
return {
|
||||
version: dsl?.version,
|
||||
validationFuncName: functionName,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Runs all the migrations
|
||||
DSLMigrations.migrateDSL(originalDSLForDSLMigrations as unknown as DSLWidget);
|
||||
|
||||
migrations.forEach((item: any, testIdx: number) => {
|
||||
const { functionLookup, version } = item;
|
||||
const dslVersion = version ?? 0;
|
||||
|
||||
functionLookup.forEach(
|
||||
(lookup: { moduleObj: any; functionName: string }, index: number) => {
|
||||
migration.functionLookup.forEach((lookup) => {
|
||||
const { functionName, moduleObj } = lookup;
|
||||
|
||||
if (moduleObj) {
|
||||
const mockObj = mockFnObj[dslVersion][index].spyOnFunc;
|
||||
const calls = mockObj.mock?.calls;
|
||||
const results = mockObj.mock?.results;
|
||||
const resultsLastIdx = mockObj.mock.results.length - 1;
|
||||
mockFnObj[version].push({
|
||||
spyOnFunc: jest
|
||||
.spyOn(moduleObj, functionName)
|
||||
.mockImplementation((dsl: any) => {
|
||||
/**
|
||||
* We need to delete the children property on the first migration(calculateDynamicHeight),
|
||||
* to avoid the recursion in the second migration(updateContainers)
|
||||
*/
|
||||
dsl && delete dsl.children;
|
||||
|
||||
describe(`Test ${testIdx}:`, () => {
|
||||
test(`Has ${functionName} function executed?`, () => {
|
||||
// Check if the migration function is called
|
||||
expect(results[resultsLastIdx].value.validationFuncName).toEqual(
|
||||
functionName,
|
||||
);
|
||||
});
|
||||
|
||||
// Check if the migration function is called with the current DSL version
|
||||
calls.forEach((args: any) => {
|
||||
test(`Does ${functionName} executes with DSL version: ${version}?`, () => {
|
||||
if (args[0]?.version === version) {
|
||||
expect(args[0]?.version).toEqual(version);
|
||||
}
|
||||
});
|
||||
test(`For ${functionName}, is the ${args[0]?.version} registerd in tests?`, () => {
|
||||
expect(
|
||||
Object.keys(mockFnObj).includes(
|
||||
args[0]?.version.toString() ?? "0",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
return {
|
||||
version: dsl?.version,
|
||||
validationFuncName: functionName,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Runs all the migrations
|
||||
await DSLMigrations.migrateDSL(
|
||||
originalDSLForDSLMigrations as unknown as DSLWidget,
|
||||
);
|
||||
|
||||
migrations.forEach((item: any) => {
|
||||
const { functionLookup, version } = item;
|
||||
const dslVersion = version ?? 0;
|
||||
|
||||
functionLookup.forEach(
|
||||
(lookup: { moduleObj: any; functionName: string }, index: number) => {
|
||||
const { functionName, moduleObj } = lookup;
|
||||
|
||||
if (moduleObj) {
|
||||
const mockObj = mockFnObj[dslVersion][index].spyOnFunc;
|
||||
const calls = mockObj.mock?.calls;
|
||||
const results = mockObj.mock?.results;
|
||||
const resultsLastIdx = mockObj.mock.results.length - 1;
|
||||
|
||||
// Check if the migration function is called
|
||||
expect(results[resultsLastIdx].value.validationFuncName).toEqual(
|
||||
functionName,
|
||||
);
|
||||
// Check if the migration function is called with the current DSL version
|
||||
calls.forEach((args: any) => {
|
||||
// Does functionName executes with the correct DSL version number
|
||||
if (args[0]?.version === version) {
|
||||
expect(args[0]?.version).toEqual(version);
|
||||
}
|
||||
|
||||
// For a functionName is the correct DSL version registed in test cases
|
||||
expect(
|
||||
Object.keys(mockFnObj).includes(
|
||||
args[0]?.version.toString() ?? "0",
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
test("Check the migration count matches the lates page version", () => {
|
||||
expect(migrations.length).toEqual(DSLMigrations.LATEST_DSL_VERSION);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import type { DSLWidget } from "../types";
|
|||
const ASSETS_CDN_URL = "https://assets.appsmith.com";
|
||||
|
||||
describe("correctly migrate dsl", () => {
|
||||
it("migrateDSL for private widget", () => {
|
||||
it("migrateDSL for private widget", async () => {
|
||||
const currentVersion = 49; // before adding privateWidgets to all List widgets
|
||||
const nextVersion = LATEST_DSL_VERSION; // It runs Two Migrations, Always Update as migration increases
|
||||
|
||||
|
|
@ -1229,12 +1229,12 @@ describe("correctly migrate dsl", () => {
|
|||
isLoading: false,
|
||||
};
|
||||
|
||||
const actualNextDsl = migrateDSL(currentDSL);
|
||||
const actualNextDsl = await migrateDSL(currentDSL);
|
||||
|
||||
expect(actualNextDsl).toEqual(expectedNextDSL);
|
||||
});
|
||||
|
||||
it("migrateDSL for theming v1", () => {
|
||||
it("migrateDSL for theming v1", async () => {
|
||||
const currentVersion = 53;
|
||||
const nextVersion = LATEST_DSL_VERSION;
|
||||
const currentDSL: DSLWidget = {
|
||||
|
|
@ -2452,7 +2452,7 @@ describe("correctly migrate dsl", () => {
|
|||
isLoading: false,
|
||||
};
|
||||
|
||||
const actualNextDsl = migrateDSL(currentDSL);
|
||||
const actualNextDsl = await migrateDSL(currentDSL);
|
||||
|
||||
expect(actualNextDsl).toEqual(expectedNextDSL);
|
||||
});
|
||||
|
|
@ -3096,7 +3096,7 @@ describe("correctly migrate dsl", () => {
|
|||
expect(actualNextDSL).toEqual(expectedNextDSL);
|
||||
});
|
||||
|
||||
it("correctly migrates currentIndex/currentRow properties for validations in table view", () => {
|
||||
it("correctly migrates currentIndex/currentRow properties for validations in table view", async () => {
|
||||
const currentVersion = 78;
|
||||
const currentDSL: DSLWidget = {
|
||||
bottomRow: 740,
|
||||
|
|
@ -3171,7 +3171,7 @@ describe("correctly migrate dsl", () => {
|
|||
},
|
||||
],
|
||||
};
|
||||
const nextDSL = migrateDSL(currentDSL);
|
||||
const nextDSL = await migrateDSL(currentDSL);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
const validations = (nextDSL.children || [])[0].primaryColumns.column1
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ export default class DSLController extends BaseController {
|
|||
super();
|
||||
}
|
||||
|
||||
migrateDSL(req: Request, res: Response) {
|
||||
async migrateDSL(req: Request, res: Response) {
|
||||
try {
|
||||
const latestDSL = migrateDSLToLatest(req.body);
|
||||
const latestDSL = await migrateDSLToLatest(req.body);
|
||||
|
||||
super.sendResponse(res, latestDSL);
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import DSLController from "@controllers/Dsl/DslController";
|
||||
import { Validator } from "@middlewares/Validator";
|
||||
import express from "express";
|
||||
import type { Response, Request } from "express";
|
||||
|
||||
const router = express.Router();
|
||||
const dslController = new DSLController();
|
||||
|
|
@ -12,6 +13,11 @@ router.get(
|
|||
dslController.getLatestDSLVersion,
|
||||
);
|
||||
|
||||
router.post("/migrate", validator.validateRequest, dslController.migrateDSL);
|
||||
router.post(
|
||||
"/migrate",
|
||||
validator.validateRequest,
|
||||
async (req: Request, res: Response) =>
|
||||
await dslController.migrateDSL(req, res),
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { LATEST_DSL_VERSION, migrateDSL } from "@shared/dsl";
|
||||
|
||||
export function migrateDSLToLatest(currentDsl) {
|
||||
const latestDSL = migrateDSL(currentDsl);
|
||||
export async function migrateDSLToLatest(currentDsl) {
|
||||
const latestDSL = await migrateDSL(currentDsl);
|
||||
|
||||
return latestDSL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ export interface PageLayoutsRequest {
|
|||
}
|
||||
|
||||
export interface FetchPageResponseData {
|
||||
isDefault?: boolean;
|
||||
isHidden?: boolean;
|
||||
id: string;
|
||||
baseId: string;
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -193,14 +193,15 @@ export function* refreshTheApp() {
|
|||
}
|
||||
}
|
||||
|
||||
export const getCanvasWidgetsPayload = (
|
||||
export const getCanvasWidgetsPayload = async (
|
||||
pageResponse: FetchPageResponse,
|
||||
dslTransformer?: (dsl: DSLWidget) => DSLWidget,
|
||||
): UpdateCanvasPayload => {
|
||||
const extractedDSL = extractCurrentDSL({
|
||||
): Promise<UpdateCanvasPayload> => {
|
||||
const currentDSL = await extractCurrentDSL({
|
||||
dslTransformer,
|
||||
response: pageResponse,
|
||||
}).dsl;
|
||||
});
|
||||
const extractedDSL = currentDSL.dsl;
|
||||
const flattenedDSL = flattenDSL(extractedDSL);
|
||||
const pageWidgetId = MAIN_CONTAINER_WIDGET_ID;
|
||||
|
||||
|
|
@ -249,10 +250,9 @@ export function* handleFetchedPage({
|
|||
// Wait for widget config to be loaded before we can generate the canvas payload
|
||||
yield call(waitForWidgetConfigBuild);
|
||||
// Get Canvas payload
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(
|
||||
fetchPageResponse,
|
||||
dslTransformer,
|
||||
);
|
||||
|
||||
const canvasWidgetsPayload: UpdateCanvasPayload =
|
||||
yield getCanvasWidgetsPayload(fetchPageResponse, dslTransformer);
|
||||
|
||||
// Update the canvas
|
||||
yield put(initCanvasLayout(canvasWidgetsPayload));
|
||||
|
|
@ -329,7 +329,8 @@ export function* updateCanvasLayout(response: FetchPageResponse) {
|
|||
// Wait for widget config to load before we can get the canvas payload
|
||||
yield call(waitForWidgetConfigBuild);
|
||||
// Get Canvas payload
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(response);
|
||||
const canvasWidgetsPayload: UpdateCanvasPayload =
|
||||
yield getCanvasWidgetsPayload(response);
|
||||
|
||||
// resize main canvas
|
||||
resizePublishedMainCanvasToLowestWidget(canvasWidgetsPayload.widgets);
|
||||
|
|
@ -688,9 +689,11 @@ export function* createNewPageFromEntity(
|
|||
// So, the client premptively uses the default page DSL
|
||||
// The default page DSL is used and modified using the layout system
|
||||
// specific dslTransformer
|
||||
const currentDSL: { dsl: DSLWidget; layoutId: string | undefined } =
|
||||
yield extractCurrentDSL({ dslTransformer });
|
||||
const defaultPageLayouts = [
|
||||
{
|
||||
dsl: extractCurrentDSL({ dslTransformer }).dsl,
|
||||
dsl: currentDSL.dsl,
|
||||
layoutOnLoadActions: [],
|
||||
},
|
||||
];
|
||||
|
|
@ -754,14 +757,17 @@ export function* createPageSaga(action: ReduxAction<CreatePageActionPayload>) {
|
|||
// Add this to the page DSLs for entity explorer
|
||||
// The dslTransformer may not be necessary for the entity explorer
|
||||
// However, we still transform for consistency.
|
||||
const currentDSL: { dsl: DSLWidget; layoutId: string | undefined } =
|
||||
yield extractCurrentDSL({
|
||||
dslTransformer,
|
||||
response,
|
||||
});
|
||||
|
||||
yield put({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSL_SUCCESS,
|
||||
payload: {
|
||||
pageId: response.data.id,
|
||||
dsl: extractCurrentDSL({
|
||||
dslTransformer,
|
||||
response,
|
||||
}).dsl,
|
||||
dsl: currentDSL.dsl,
|
||||
layoutId: response.data.layouts[0].id,
|
||||
},
|
||||
});
|
||||
|
|
@ -879,7 +885,7 @@ export function* clonePageSaga(
|
|||
// We're not sending the `dslTransformer` to the `extractCurrentDSL` function
|
||||
// as this is a clone operation, and any layout system specific
|
||||
// updates to the DSL would have already been performed in the original page
|
||||
const { dsl, layoutId } = extractCurrentDSL({
|
||||
const { dsl, layoutId } = yield extractCurrentDSL({
|
||||
response,
|
||||
});
|
||||
|
||||
|
|
@ -1169,7 +1175,7 @@ export function* fetchPageDSLSaga(
|
|||
// between Auto Layout and Fixed layout systems, this means that
|
||||
// particularly for these two layout systems the dslTransformer may be necessary
|
||||
// unless we're no longer running any conversions
|
||||
const { dsl, layoutId } = extractCurrentDSL({
|
||||
const { dsl, layoutId } = yield extractCurrentDSL({
|
||||
dslTransformer,
|
||||
response: fetchPageResponse,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "@blueprintjs/core/lib/esm/common/classes";
|
||||
import { importSvg } from "@appsmith/ads-old";
|
||||
import type { IconName } from "@blueprintjs/core";
|
||||
import log from "loglevel";
|
||||
|
||||
// This export must be named "IconSize" to match the exports of @blueprintjs/core/lib/esm/components/icon
|
||||
export enum IconSize {
|
||||
|
|
@ -29,17 +30,21 @@ type IconMapType = Record<
|
|||
Record<IconSize, () => Promise<typeof import("*.svg")>>
|
||||
>;
|
||||
|
||||
// Create a variable to cache the imported module
|
||||
// Cache for lazy-loaded svg imports
|
||||
let cachedSvgImportsMap: IconMapType | null = null;
|
||||
|
||||
// Function to lazily load the file once
|
||||
const loadSvgImportsMapOnce = async () => {
|
||||
if (!cachedSvgImportsMap) {
|
||||
const { default: svgImportsMap } = await import(
|
||||
"components/designSystems/blueprintjs/icon/svgImportsMap"
|
||||
);
|
||||
try {
|
||||
const { default: svgImportsMap } = await import(
|
||||
"components/designSystems/blueprintjs/icon/svgImportsMap"
|
||||
);
|
||||
|
||||
cachedSvgImportsMap = svgImportsMap; // Cache the module for future use
|
||||
cachedSvgImportsMap = svgImportsMap; // Cache the module for future use
|
||||
} catch (e) {
|
||||
log.error("Error loading SvgImportsMap", e);
|
||||
}
|
||||
}
|
||||
|
||||
return cachedSvgImportsMap;
|
||||
|
|
@ -70,7 +75,7 @@ function Icon(props: IconProps) {
|
|||
// Load the cached svgImportsMap once
|
||||
const svgImportsMap = await loadSvgImportsMapOnce();
|
||||
|
||||
if (typeof icon === "string" && icon in svgImportsMap) {
|
||||
if (svgImportsMap && typeof icon === "string" && icon in svgImportsMap) {
|
||||
const SvgIcon = await importSvg(svgImportsMap[icon][pixelGridSize]);
|
||||
|
||||
setSvgIcon(() => SvgIcon); // Set the component as lazy-loaded
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ describe("Canvas selection test cases", () => {
|
|||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it("Should select using canvas draw", () => {
|
||||
it("Should select using canvas draw", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -94,10 +94,13 @@ describe("Canvas selection test cases", () => {
|
|||
</MemoryRouter>,
|
||||
{ initialState: store.getState(), sagasToRun: sagasToRunForTests },
|
||||
);
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let selectionCanvas: any = component.queryByTestId(
|
||||
let selectionCanvas: any = await component.findByTestId(
|
||||
`canvas-selection-${MAIN_CONTAINER_WIDGET_ID}`,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -121,7 +124,7 @@ describe("Canvas selection test cases", () => {
|
|||
expect(selectionCanvas.style.zIndex).toBe("");
|
||||
});
|
||||
|
||||
it("Should select all elements using canvas from top to bottom", () => {
|
||||
it("Should select all elements using canvas from top to bottom", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -166,8 +169,10 @@ describe("Canvas selection test cases", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const selectionDiv: any = component.queryByTestId(
|
||||
const selectionDiv: any = await component.findByTestId(
|
||||
`div-selection-${MAIN_CONTAINER_WIDGET_ID}`,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
fireEvent(
|
||||
|
|
@ -221,7 +226,7 @@ describe("Canvas selection test cases", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Should allow draw to select using cmd + draw in Container component", () => {
|
||||
it("Should allow draw to select using cmd + draw in Container component", async () => {
|
||||
const containerId = generateReactKey();
|
||||
const canvasId = generateReactKey();
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -273,8 +278,10 @@ describe("Canvas selection test cases", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let selectionCanvas: any = component.queryByTestId(
|
||||
let selectionCanvas: any = await component.findByTestId(
|
||||
`canvas-selection-${canvasId}`,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -300,7 +307,7 @@ describe("Canvas selection test cases", () => {
|
|||
expect(selectionCanvas.style.zIndex).toBe("");
|
||||
});
|
||||
|
||||
it("Should not allow draw to select using cmd + draw in drop disabled Canvas component", () => {
|
||||
it("Should not allow draw to select using cmd + draw in drop disabled Canvas component", async () => {
|
||||
const containerId = generateReactKey();
|
||||
const canvasId = generateReactKey();
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -341,14 +348,27 @@ describe("Canvas selection test cases", () => {
|
|||
<Canvas canvasWidth={dsl.rightColumn} widgetsStructure={dsl} />
|
||||
</MockPageDSL>,
|
||||
);
|
||||
|
||||
let selectionCanvas;
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const selectionCanvas: any = component.queryByTestId(`canvas-${canvasId}`);
|
||||
try {
|
||||
// We actually want to assert the canvas component could not be found,
|
||||
// if the canvas component could not be found after timeout findByTestId will throw an error
|
||||
// In that case we set the component to be null
|
||||
selectionCanvas = await component.findByTestId(
|
||||
`canvas-${canvasId}`,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
} catch (e) {
|
||||
selectionCanvas = null;
|
||||
}
|
||||
|
||||
expect(selectionCanvas).toBeNull();
|
||||
});
|
||||
|
||||
it("Should select all elements inside a CONTAINER using draw on canvas from top to bottom", () => {
|
||||
it("Should select all elements inside a CONTAINER using draw on canvas from top to bottom", async () => {
|
||||
const containerId = "containerWidget";
|
||||
const canvasId = "canvasWidget";
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -426,8 +446,10 @@ describe("Canvas selection test cases", () => {
|
|||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const selectionDiv: any = component.queryByTestId(
|
||||
const selectionDiv: any = await component.findByTestId(
|
||||
`div-selection-${canvasId}`,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
fireEvent(
|
||||
|
|
@ -479,7 +501,7 @@ describe("Canvas selection test cases", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Draw to select from outside of canvas(editor) ", () => {
|
||||
it("Draw to select from outside of canvas(editor) ", async () => {
|
||||
const mockGetIsFetchingPage = jest.spyOn(utilities, "getIsFetchingPage");
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -525,7 +547,11 @@ describe("Canvas selection test cases", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const widgetEditor: any = component.queryByTestId("t--widgets-editor");
|
||||
const widgetEditor: any = await component.findByTestId(
|
||||
"t--widgets-editor",
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let selectionCanvas: any = component.queryByTestId(
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ describe("Entity Explorer tests", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Should render Widgets tree in entity explorer", () => {
|
||||
it("Should render Widgets tree in entity explorer", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([{ type: "TABS_WIDGET" }]);
|
||||
|
|
@ -88,9 +88,13 @@ describe("Entity Explorer tests", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const widgetsTree: any = component.queryByText("Widgets", {
|
||||
selector: "div.t--entity-name",
|
||||
});
|
||||
const widgetsTree: Element = await component.findByText(
|
||||
"Widgets",
|
||||
{
|
||||
selector: "div.t--entity-name",
|
||||
},
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(widgetsTree);
|
||||
|
|
@ -111,7 +115,7 @@ describe("Entity Explorer tests", () => {
|
|||
spyWidgetSelection.mockClear();
|
||||
});
|
||||
|
||||
it("Select widget on entity explorer", () => {
|
||||
it("Select widget on entity explorer", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -128,8 +132,11 @@ describe("Entity Explorer tests", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const tabsWidget: any = component.queryByText(children[0].widgetName);
|
||||
const tabsWidget: Element = await component.findByText(
|
||||
children[0].widgetName,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(tabsWidget);
|
||||
|
|
@ -144,7 +151,7 @@ describe("Entity Explorer tests", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("CMD + click Multi Select widget on entity explorer", () => {
|
||||
it("CMD + click Multi Select widget on entity explorer", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -166,8 +173,11 @@ describe("Entity Explorer tests", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const checkBox: any = component.queryByText(children[0].widgetName);
|
||||
const checkBox: Element = await component.findByText(
|
||||
children[0].widgetName,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(checkBox);
|
||||
|
|
@ -198,7 +208,7 @@ describe("Entity Explorer tests", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Shift + Click Multi Select widget on entity explorer", () => {
|
||||
it("Shift + Click Multi Select widget on entity explorer", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -211,8 +221,7 @@ describe("Entity Explorer tests", () => {
|
|||
{ type: "BUTTON_WIDGET", parentId: "0", widgetId: "buttonWidgetId" },
|
||||
]);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const dsl: any = widgetCanvasFactory.build({
|
||||
const dsl = widgetCanvasFactory.build({
|
||||
children,
|
||||
});
|
||||
const component = render(
|
||||
|
|
@ -223,7 +232,11 @@ describe("Entity Explorer tests", () => {
|
|||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const checkboxWidget: any = component.queryByText(children[0].widgetName);
|
||||
const checkboxWidget: any = await component.findByText(
|
||||
children[0].widgetName,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const buttonWidget: any = component.queryByText(children[2].widgetName);
|
||||
|
|
@ -255,7 +268,7 @@ describe("Entity Explorer tests", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Shift + Click Deselect Non Siblings", () => {
|
||||
it("Shift + Click Deselect Non Siblings", async () => {
|
||||
const containerId = "containerWidgetId";
|
||||
const canvasId = "canvasWidgetId";
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -311,9 +324,10 @@ describe("Entity Explorer tests", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const containerWidget: any = component.queryByText(
|
||||
const containerWidget: Element = await component.findByText(
|
||||
containerChildren[0].widgetName,
|
||||
undefined,
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ describe("Canvas Hot Keys", () => {
|
|||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function UpdatedEditor({ dsl }: any) {
|
||||
useMockDsl(dsl);
|
||||
const hasLoaded = useMockDsl(dsl);
|
||||
|
||||
return <IDE />;
|
||||
return hasLoaded ? <IDE /> : null;
|
||||
}
|
||||
|
||||
// These need to be at the top to avoid imports not being mocked. ideally should be in setup.ts but will override for all other tests
|
||||
|
|
@ -140,7 +140,7 @@ describe("Canvas Hot Keys", () => {
|
|||
</MemoryRouter>,
|
||||
{ initialState: store.getState(), sagasToRun: sagasToRunForTests },
|
||||
);
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(2);
|
||||
act(() => {
|
||||
|
|
@ -291,7 +291,7 @@ describe("Cut/Copy/Paste hotkey", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const artBoard: any = component.queryByTestId("t--canvas-artboard");
|
||||
const artBoard: any = await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
// deselect all other widgets
|
||||
fireEvent.click(artBoard);
|
||||
|
|
@ -389,7 +389,7 @@ describe("Cut/Copy/Paste hotkey", () => {
|
|||
);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const artBoard: any = component.queryByTestId("t--canvas-artboard");
|
||||
const artBoard: any = await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
// deselect all other widgets
|
||||
fireEvent.click(artBoard);
|
||||
|
|
@ -434,7 +434,7 @@ describe("Cut/Copy/Paste hotkey", () => {
|
|||
});
|
||||
|
||||
describe("Undo/Redo hotkey", () => {
|
||||
it("should dispatch undo Action on cmd + z", () => {
|
||||
it("should dispatch undo Action on cmd + z", async () => {
|
||||
const dispatchSpy = jest.spyOn(store, "dispatch");
|
||||
const component = render(
|
||||
<MockPageDSL>
|
||||
|
|
@ -449,6 +449,9 @@ describe("Undo/Redo hotkey", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
|
||||
// wait for the dom to settle down by waitng for the canvas to be loaded
|
||||
await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
dispatchSpy.mockClear();
|
||||
|
||||
act(() => {
|
||||
|
|
@ -462,10 +465,11 @@ describe("Undo/Redo hotkey", () => {
|
|||
);
|
||||
});
|
||||
|
||||
expect(dispatchSpy).toBeCalledTimes(1);
|
||||
expect(dispatchSpy).toBeCalledWith(undoAction());
|
||||
await waitFor(() => {
|
||||
expect(dispatchSpy).toBeCalledWith(undoAction());
|
||||
});
|
||||
});
|
||||
it("should dispatch redo Action on cmd + shift + z", () => {
|
||||
it("should dispatch redo Action on cmd + shift + z", async () => {
|
||||
const dispatchSpy = jest.spyOn(store, "dispatch");
|
||||
const component = render(
|
||||
<MockPageDSL>
|
||||
|
|
@ -480,6 +484,9 @@ describe("Undo/Redo hotkey", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
|
||||
// wait for the dom to settle down by waitng for the canvas to be loaded
|
||||
await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
dispatchSpy.mockClear();
|
||||
|
||||
act(() => {
|
||||
|
|
@ -496,7 +503,7 @@ describe("Undo/Redo hotkey", () => {
|
|||
expect(dispatchSpy).toBeCalledTimes(1);
|
||||
expect(dispatchSpy).toBeCalledWith(redoAction());
|
||||
});
|
||||
it("should dispatch redo Action on ctrl + y", () => {
|
||||
it("should dispatch redo Action on ctrl + y", async () => {
|
||||
const dispatchSpy = jest.spyOn(store, "dispatch");
|
||||
const component = render(
|
||||
<MockPageDSL>
|
||||
|
|
@ -511,6 +518,9 @@ describe("Undo/Redo hotkey", () => {
|
|||
</MockPageDSL>,
|
||||
);
|
||||
|
||||
// wait for the dom to settle down by waitng for the canvas to be loaded
|
||||
await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
dispatchSpy.mockClear();
|
||||
|
||||
act(() => {
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
}));
|
||||
});
|
||||
|
||||
it("Drag to move widgets", () => {
|
||||
it("Drag to move widgets", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -190,7 +190,8 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
const propPane = component.queryByTestId("t--propertypane");
|
||||
|
||||
expect(propPane).toBeNull();
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(1);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -275,7 +276,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
expect(finalPositions.top).not.toEqual(initPositions.top);
|
||||
});
|
||||
|
||||
it("When widgets are moved out of main container bounds move them back to previous position", () => {
|
||||
it("When widgets are moved out of main container bounds move them back to previous position", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -310,7 +311,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
const propPane = component.queryByTestId("t--propertypane");
|
||||
|
||||
expect(propPane).toBeNull();
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(1);
|
||||
// TODO: Fix this the next time the file is edited
|
||||
|
|
@ -499,7 +500,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
// expect(finalPositions.top).toEqual(initPositions.top);
|
||||
// });
|
||||
|
||||
it("When widgets are out of bottom most bounds of parent canvas, canvas has to expand", () => {
|
||||
it("When widgets are out of bottom most bounds of parent canvas, canvas has to expand", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([
|
||||
|
|
@ -544,7 +545,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
const propPane = component.queryByTestId("t--propertypane");
|
||||
|
||||
expect(propPane).toBeNull();
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(2);
|
||||
|
||||
|
|
@ -639,7 +640,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Drag and Drop widget into an empty canvas", () => {
|
||||
it("Drag and Drop widget into an empty canvas", async () => {
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren([]);
|
||||
|
|
@ -675,7 +676,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const containerButton: any = component.queryAllByText("Container");
|
||||
const containerButton: any = await component.findAllByText("Container");
|
||||
|
||||
act(() => {
|
||||
fireEvent.dragStart(containerButton[0]);
|
||||
|
|
@ -733,7 +734,7 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
expect(newlyAddedCanvas.length).toBe(1);
|
||||
});
|
||||
|
||||
it("Disallow drag if widget not focused", () => {
|
||||
it("Disallow drag if widget not focused", async () => {
|
||||
const initialState = store.getState() as unknown as Partial<AppState>;
|
||||
const containerId = generateReactKey();
|
||||
const canvasId = generateReactKey();
|
||||
|
|
@ -778,6 +779,8 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
</MemoryRouter>,
|
||||
{ initialState, sagasToRun: sagasToRunForTests },
|
||||
);
|
||||
// wait for the dom to settle down by waitng for the widget to be loaded
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -790,8 +793,6 @@ describe("Drag and Drop widgets into Main container", () => {
|
|||
".t--draggable-containerwidget",
|
||||
);
|
||||
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(1);
|
||||
|
||||
const initWidgetPosition = {
|
||||
|
|
@ -894,7 +895,7 @@ describe("Drag in a nested container", () => {
|
|||
}));
|
||||
});
|
||||
|
||||
it("container drags when focused on", () => {
|
||||
it("container drags when focused on", async () => {
|
||||
mockGetIsFetchingPage.mockImplementation(() => false);
|
||||
|
||||
const component = renderNestedComponent();
|
||||
|
|
@ -903,6 +904,8 @@ describe("Drag in a nested container", () => {
|
|||
.spyOn(uiSelectors, "getSelectedWidgets")
|
||||
.mockReturnValue(["container-id"]);
|
||||
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const containerWidget: any = component.container.querySelector(
|
||||
|
|
@ -914,8 +917,6 @@ describe("Drag in a nested container", () => {
|
|||
".t--draggable-containerwidget",
|
||||
);
|
||||
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(3);
|
||||
|
||||
const initContainerWidgetPosition = {
|
||||
|
|
@ -975,7 +976,7 @@ describe("Drag in a nested container", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("nested widget drags when focused on", () => {
|
||||
it("nested widget drags when focused on", async () => {
|
||||
mockGetIsFetchingPage.mockImplementation(() => false);
|
||||
|
||||
const component = renderNestedComponent();
|
||||
|
|
@ -984,6 +985,8 @@ describe("Drag in a nested container", () => {
|
|||
.spyOn(uiSelectors, "getSelectedWidgets")
|
||||
.mockReturnValue(["text-widget"]);
|
||||
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const textWidget: any = component.container.querySelector(
|
||||
|
|
@ -995,8 +998,6 @@ describe("Drag in a nested container", () => {
|
|||
".t--draggable-textwidget",
|
||||
);
|
||||
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(3);
|
||||
|
||||
const initTextWidgetPosition = {
|
||||
|
|
@ -1054,10 +1055,11 @@ describe("Drag in a nested container", () => {
|
|||
expect(finalTextWidgetPositions).not.toStrictEqual(initTextWidgetPosition);
|
||||
});
|
||||
|
||||
it("does not let disabledWidget drag and parent widget position stays same", () => {
|
||||
it("does not let disabledWidget drag and parent widget position stays same", async () => {
|
||||
mockGetIsFetchingPage.mockImplementation(() => false);
|
||||
|
||||
const component = renderNestedComponent();
|
||||
const canvasWidgets = await component.findAllByTestId("test-widget");
|
||||
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -1090,8 +1092,6 @@ describe("Drag in a nested container", () => {
|
|||
top: inputWidget.style.top,
|
||||
};
|
||||
|
||||
const canvasWidgets = component.queryAllByTestId("test-widget");
|
||||
|
||||
expect(canvasWidgets.length).toBe(3);
|
||||
|
||||
act(() => {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ describe("IDE URL rendering: UI", () => {
|
|||
const url =
|
||||
`/app/applicationSlug/pageSlug-${pageId}/edit/widgets/` + widgetID;
|
||||
|
||||
const { getByTestId } = render(
|
||||
const component = render(
|
||||
<Route path={BUILDER_PATH}>
|
||||
<UpdatedEditor dsl={dsl} />
|
||||
</Route>,
|
||||
|
|
@ -106,8 +106,11 @@ describe("IDE URL rendering: UI", () => {
|
|||
},
|
||||
);
|
||||
|
||||
// wait for the dom to settle down by waitng for the canvas to be loaded
|
||||
await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
// check for list UI
|
||||
getByTestId(`t--entity-item-${children[0].widgetName}`);
|
||||
component.getByTestId(`t--entity-item-${children[0].widgetName}`);
|
||||
});
|
||||
|
||||
it("Canvas: Check tabs rendering in side by side mode", () => {
|
||||
|
|
|
|||
|
|
@ -44,13 +44,13 @@ const defaultDSL = defaultTemplate;
|
|||
* @param fetchPageResponse The response from the fetchPage API Call
|
||||
* @returns The updated DSL and the layoutId
|
||||
*/
|
||||
export const extractCurrentDSL = ({
|
||||
export const extractCurrentDSL = async ({
|
||||
dslTransformer,
|
||||
response,
|
||||
}: {
|
||||
dslTransformer?: (dsl: DSLWidget) => DSLWidget;
|
||||
response?: FetchPageResponse;
|
||||
}): { dsl: DSLWidget; layoutId: string | undefined } => {
|
||||
}): Promise<{ dsl: DSLWidget; layoutId: string | undefined }> => {
|
||||
// If fetch page response doesn't exist
|
||||
// It means we are creating a new page
|
||||
const newPage = !response;
|
||||
|
|
@ -62,10 +62,10 @@ export const extractCurrentDSL = ({
|
|||
let dsl = currentDSL as DSLWidget;
|
||||
|
||||
// Run all the migrations on this DSL
|
||||
dsl = migrateDSL(
|
||||
dsl = (await migrateDSL(
|
||||
currentDSL as ContainerWidgetProps<WidgetProps>,
|
||||
newPage,
|
||||
) as DSLWidget;
|
||||
)) as DSLWidget;
|
||||
|
||||
// If this DSL is meant to be transformed
|
||||
// then the dslTransformer would have been passed by the caller
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
import { sagasToRunForTests } from "test/sagas";
|
||||
import { MockApplication } from "test/testCommon";
|
||||
import { UpdateAppViewer, UpdatedEditor } from "test/testMockedWidgets";
|
||||
import { render } from "test/testUtils";
|
||||
import { render, waitFor } from "test/testUtils";
|
||||
import { generateReactKey } from "widgets/WidgetUtils";
|
||||
|
||||
const pageId = "0123456789abcdef00000000";
|
||||
|
|
@ -110,6 +110,8 @@ describe("ContainerWidget tests", () => {
|
|||
</MemoryRouter>,
|
||||
{ initialState: appState, sagasToRun: sagasToRunForTests },
|
||||
);
|
||||
expect(spyUseCanvasDragging).toHaveBeenCalled();
|
||||
await waitFor(() => {
|
||||
expect(spyUseCanvasDragging).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ describe("Tabs widget functional cases", () => {
|
|||
expect(tab2).toBeDefined();
|
||||
});
|
||||
|
||||
it("Should render components inside tabs by default", () => {
|
||||
it("Should render components inside tabs by default", async () => {
|
||||
const tab1Children = buildChildren([
|
||||
{ type: "SWITCH_WIDGET", label: "Tab1 Switch" },
|
||||
{ type: "CHECKBOX_WIDGET", label: "Tab1 Checkbox" },
|
||||
|
|
@ -74,6 +74,10 @@ describe("Tabs widget functional cases", () => {
|
|||
<Canvas canvasWidth={dsl.rightColumn} widgetsStructure={dsl} />
|
||||
</MockPageDSL>,
|
||||
);
|
||||
|
||||
// wait for the dom to settle down by waitng for the canvas to be loaded
|
||||
await component.findByTestId("t--canvas-artboard");
|
||||
|
||||
const tab1 = component.queryByText("Tab 1");
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import type { DSLWidget } from "WidgetProvider/constants";
|
|||
import defaultTemplate from "templates/default";
|
||||
import { WidgetTypeFactories } from "./Widgets/WidgetTypeFactories";
|
||||
const defaultMainContainer: DSLWidget = {
|
||||
...(defaultTemplate as any),
|
||||
...(defaultTemplate as unknown as DSLWidget),
|
||||
canExtend: true,
|
||||
renderMode: "PAGE",
|
||||
version: 1,
|
||||
isLoading: false,
|
||||
};
|
||||
} as DSLWidget;
|
||||
|
||||
export const mainContainerFactory = makeFactory({ ...defaultMainContainer });
|
||||
export const widgetCanvasFactory = makeFactory(mainContainerFactory.build());
|
||||
|
|
@ -18,18 +18,22 @@ const buildChild = (child: Partial<WidgetProps>): WidgetProps => {
|
|||
...child,
|
||||
});
|
||||
};
|
||||
|
||||
export const buildChildren = (children: Partial<WidgetProps>[]) => {
|
||||
try {
|
||||
return children.map((child) => {
|
||||
return buildChild(child);
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Check if child widget data provided");
|
||||
}
|
||||
};
|
||||
|
||||
export const buildDslWithChildren = (childData: Partial<WidgetProps>[]) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const children: any = buildChildren(childData);
|
||||
|
||||
return widgetCanvasFactory.build({
|
||||
children,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,13 +12,16 @@ import type { WidgetEntity } from "ee/entities/DataTree/types";
|
|||
import urlBuilder from "ee/entities/URLRedirect/URLAssembly";
|
||||
import type { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsStructureReducer";
|
||||
import type { Page } from "entities/Page";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { FetchPageResponse } from "api/PageApi";
|
||||
|
||||
const pageId = "0123456789abcdef00000000";
|
||||
|
||||
export const useMockDsl = (dsl: any, mode?: APP_MODE) => {
|
||||
export const useMockDsl = (dsl: unknown, mode?: APP_MODE) => {
|
||||
const dispatch = useDispatch();
|
||||
dispatch(setAppMode(mode || APP_MODE.EDIT));
|
||||
const mockResp: any = {
|
||||
const [hasLoaded, setHasLoaded] = useState(false);
|
||||
|
||||
const mockResp = {
|
||||
data: {
|
||||
id: pageId,
|
||||
pageId: pageId,
|
||||
|
|
@ -42,52 +45,81 @@ export const useMockDsl = (dsl: any, mode?: APP_MODE) => {
|
|||
"delete:pages",
|
||||
],
|
||||
},
|
||||
};
|
||||
const canvasWidgetsPayload = getCanvasWidgetsPayload(mockResp);
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
||||
payload: [
|
||||
{
|
||||
pageId: mockResp.data.id,
|
||||
dsl: extractCurrentDSL({ response: mockResp }).dsl,
|
||||
},
|
||||
],
|
||||
});
|
||||
const pages: Page[] = [
|
||||
{
|
||||
pageName: mockResp.data.name,
|
||||
pageId: mockResp.data.id,
|
||||
basePageId: mockResp.data.id,
|
||||
isDefault: mockResp.data.isDefault,
|
||||
isHidden: !!mockResp.data.isHidden,
|
||||
slug: mockResp.data.slug,
|
||||
userPermissions: [
|
||||
"read:pages",
|
||||
"manage:pages",
|
||||
"create:pageActions",
|
||||
"delete:pages",
|
||||
],
|
||||
},
|
||||
];
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
|
||||
payload: {
|
||||
pages,
|
||||
applicationId: mockResp.data.applicationId,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: "UPDATE_LAYOUT",
|
||||
payload: { widgets: canvasWidgetsPayload.widgets },
|
||||
});
|
||||
} as unknown as FetchPageResponse;
|
||||
|
||||
dispatch(updateCurrentPage(mockResp.data.id));
|
||||
useEffect(function loadScripts() {
|
||||
const loadMigrationScripts = async () => {
|
||||
const canvasWidgetsPayloadD = await getCanvasWidgetsPayload(mockResp);
|
||||
const currentDSL = await extractCurrentDSL({
|
||||
response: mockResp,
|
||||
});
|
||||
|
||||
const currentDsl = currentDSL.dsl;
|
||||
const canvasWidgetsPayload = canvasWidgetsPayloadD.widgets;
|
||||
|
||||
dispatch(setAppMode(mode || APP_MODE.EDIT));
|
||||
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
||||
payload: [
|
||||
{
|
||||
pageId: mockResp.data.id,
|
||||
dsl: currentDsl,
|
||||
},
|
||||
],
|
||||
});
|
||||
const pages = [
|
||||
{
|
||||
pageName: mockResp.data.name,
|
||||
pageId: mockResp.data.id,
|
||||
basePageId: mockResp.data.id,
|
||||
isDefault: mockResp.data.isDefault,
|
||||
isHidden: !!mockResp.data.isHidden,
|
||||
slug: mockResp.data.slug,
|
||||
userPermissions: [
|
||||
"read:pages",
|
||||
"manage:pages",
|
||||
"create:pageActions",
|
||||
"delete:pages",
|
||||
],
|
||||
},
|
||||
] as unknown as Page[];
|
||||
|
||||
dispatch({
|
||||
type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
|
||||
payload: {
|
||||
pages,
|
||||
applicationId: mockResp.data.applicationId,
|
||||
},
|
||||
});
|
||||
dispatch({
|
||||
type: "UPDATE_LAYOUT",
|
||||
payload: { widgets: canvasWidgetsPayload },
|
||||
});
|
||||
|
||||
dispatch(updateCurrentPage(mockResp.data.id));
|
||||
setHasLoaded(true);
|
||||
};
|
||||
|
||||
loadMigrationScripts();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return hasLoaded;
|
||||
};
|
||||
|
||||
export function MockPageDSL({ children, dsl }: any) {
|
||||
export function MockPageDSL({
|
||||
children,
|
||||
dsl,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
dsl?: unknown;
|
||||
}) {
|
||||
editorInitializer();
|
||||
useMockDsl(dsl);
|
||||
return children;
|
||||
|
||||
const hasLoaded = useMockDsl(dsl);
|
||||
|
||||
return hasLoaded ? children : null;
|
||||
}
|
||||
|
||||
const getChildWidgets = (
|
||||
|
|
@ -123,7 +155,7 @@ export const mockCreateCanvasWidget = (
|
|||
canvasWidget: FlattenedWidgetProps,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
evaluatedWidget: WidgetEntity,
|
||||
): any => {
|
||||
) => {
|
||||
return { ...canvasWidget };
|
||||
};
|
||||
|
||||
|
|
@ -141,17 +173,21 @@ export const syntheticTestMouseEvent = (
|
|||
optionsToAdd = {},
|
||||
) => {
|
||||
const options = Object.entries(optionsToAdd);
|
||||
|
||||
options.forEach(([key, value]) => {
|
||||
Object.defineProperty(event, key, { get: () => value });
|
||||
});
|
||||
|
||||
return event;
|
||||
};
|
||||
|
||||
export function MockApplication({ children }: any) {
|
||||
export function MockApplication({ children }: { children: JSX.Element }) {
|
||||
editorInitializer();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
dispatch(initEditorAction({ basePageId: pageId, mode: APP_MODE.EDIT }));
|
||||
const mockResp: any = {
|
||||
|
||||
const mockResp = {
|
||||
workspaceId: "workspace_id",
|
||||
pages: [
|
||||
{
|
||||
|
|
@ -176,6 +212,7 @@ export function MockApplication({ children }: any) {
|
|||
slug: "app-name",
|
||||
applicationVersion: 2,
|
||||
};
|
||||
|
||||
urlBuilder.updateURLParams(
|
||||
{
|
||||
baseApplicationId: mockResp.baseId,
|
||||
|
|
@ -212,6 +249,7 @@ export function MockApplication({ children }: any) {
|
|||
],
|
||||
},
|
||||
});
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +263,8 @@ export function dispatchTestKeyboardEventWithCode(
|
|||
meta = false,
|
||||
) {
|
||||
const event = document.createEvent("KeyboardEvent");
|
||||
(event as any).initKeyboardEvent(
|
||||
|
||||
event.initKeyboardEvent(
|
||||
eventType,
|
||||
true,
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ import { useMockDsl } from "./testCommon";
|
|||
|
||||
export function MockCanvas() {
|
||||
const canvasWidgetsStructure = useSelector(getCanvasWidgetsStructure);
|
||||
|
||||
return <Canvas canvasWidth={0} widgetsStructure={canvasWidgetsStructure} />;
|
||||
}
|
||||
|
||||
export function UpdateAppViewer({ dsl }: any) {
|
||||
useMockDsl(dsl, APP_MODE.PUBLISHED);
|
||||
return <AppViewerPageContainer />;
|
||||
export function UpdateAppViewer({ dsl }: { dsl: unknown }) {
|
||||
const hasLoaded = useMockDsl(dsl, APP_MODE.PUBLISHED);
|
||||
|
||||
return hasLoaded ? <AppViewerPageContainer /> : null;
|
||||
}
|
||||
export function UpdatedEditor({ dsl }: any) {
|
||||
useMockDsl(dsl, APP_MODE.EDIT);
|
||||
return <IDE />;
|
||||
export function UpdatedEditor({ dsl }: { dsl: unknown }) {
|
||||
const hasLoaded = useMockDsl(dsl, APP_MODE.EDIT);
|
||||
|
||||
return hasLoaded ? <IDE /> : null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user