diff --git a/app/client/src/WidgetProvider/factory/index.tsx b/app/client/src/WidgetProvider/factory/index.tsx index 78958ddd8a4f..967a339db52b 100644 --- a/app/client/src/WidgetProvider/factory/index.tsx +++ b/app/client/src/WidgetProvider/factory/index.tsx @@ -55,6 +55,9 @@ class WidgetFactory { Partial & WidgetConfigProps & { type: string } > = new Map(); + static widgetDefaultPropertiesMap: Map> = + new Map(); + static widgetsMap: Map = new Map(); static widgetBuilderMap: Map = new Map(); @@ -132,6 +135,18 @@ class WidgetFactory { onCanvasUI, }; + // When adding widgets to canvas in Anvil, we don't need all of configured properties + // (See _config object) + // and that should ideally be the case for Fixed mode widgets as well + // So, creating this map to use in WidgetAdditionSagas for both Fixed + // and Anvil. + // Before this we were using "ALL" configured properties when creating + // the newly added widget. This lead to many extra properties being added + // to the DSL + WidgetFactory.widgetDefaultPropertiesMap.set( + widget.type, + Object.freeze({ ...defaultConfig }), + ); WidgetFactory.widgetConfigMap.set(widget.type, Object.freeze(_config)); } diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/anvilDraggingSagas.test.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/anvilDraggingSagas.test.ts index fb1ffd3ab6b4..ee8ec1ea6694 100644 --- a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/anvilDraggingSagas.test.ts +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/anvilDraggingSagas.test.ts @@ -1,5 +1,5 @@ import { select } from "redux-saga/effects"; -import { addWidgetsSaga, moveWidgetsSaga } from "."; +import { moveWidgetsSaga } from "."; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; import { generateReactKey } from "@shared/dsl/src/migrate/utils"; import { LayoutComponentTypes } from "layoutSystems/anvil/utils/anvilTypes"; @@ -33,6 +33,7 @@ import { ResponsiveBehavior, } from "layoutSystems/common/utils/constants"; import { mockAnvilHighlightInfo } from "mocks/mockHighlightInfo"; +import { addWidgetsSaga } from "../anvilWidgetAdditionSagas"; describe("", () => { beforeAll(() => { diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/helpers.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/helpers.ts new file mode 100644 index 000000000000..81c97cff964d --- /dev/null +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/helpers.ts @@ -0,0 +1,32 @@ +import type { WidgetProps } from "widgets/BaseWidget"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { FlexLayerAlignment } from "layoutSystems/common/utils/constants"; +import { select } from "redux-saga/effects"; +import { getWidget } from "sagas/selectors"; + +// Function to retrieve highlighting information for the last row in the main canvas layout +export function* getMainCanvasLastRowHighlight() { + // Retrieve the main canvas widget + const mainCanvas: WidgetProps = yield select( + getWidget, + MAIN_CONTAINER_WIDGET_ID, + ); + + const layoutId: string = mainCanvas.layout[0].layoutId; + const layoutOrder = [layoutId]; + const rowIndex = mainCanvas.layout[0].layout.length; + + // Return the highlighting information for the last row in the main canvas + return { + canvasId: MAIN_CONTAINER_WIDGET_ID, + layoutOrder, + rowIndex, + posX: 0, + posY: 0, + alignment: FlexLayerAlignment.Start, + dropZone: {}, + height: 0, + width: 0, + isVertical: false, + }; +} diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/index.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/index.ts index 2b5c1b5e2bf5..4b011417219e 100644 --- a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/index.ts +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilDraggingSagas/index.ts @@ -8,38 +8,13 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects"; import type { AnvilHighlightInfo, DraggedWidget, - WidgetLayoutProps, } from "layoutSystems/anvil/utils/anvilTypes"; -import { getWidget, getWidgets } from "sagas/selectors"; -import { addWidgetsToPreset } from "../../../utils/layouts/update/additionUtils"; -import type { - AnvilMoveWidgetsPayload, - AnvilNewWidgetsPayload, -} from "../../actions/actionTypes"; +import { getWidgets } from "sagas/selectors"; +import type { AnvilMoveWidgetsPayload } from "../../actions/actionTypes"; import { AnvilReduxActionTypes } from "../../actions/actionTypes"; -import { generateDefaultLayoutPreset } from "layoutSystems/anvil/layoutComponents/presets/DefaultLayoutPreset"; -import { selectWidgetInitAction } from "actions/widgetSelectionActions"; -import { SelectionRequestType } from "sagas/WidgetSelectUtils"; -import { - addDetachedWidgetToMainCanvas, - addWidgetsToMainCanvasLayout, - moveWidgetsToMainCanvas, -} from "layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils"; -import type { WidgetProps } from "widgets/BaseWidget"; -import { - GridDefaults, - MAIN_CONTAINER_WIDGET_ID, -} from "constants/WidgetConstants"; -import { FlexLayerAlignment } from "layoutSystems/common/utils/constants"; -import { - addWidgetsToSection, - moveWidgetsToSection, -} from "layoutSystems/anvil/utils/layouts/update/sectionUtils"; -import { WDS_V2_WIDGET_MAP } from "widgets/wds/constants"; -import { - addNewWidgetToDsl, - getCreateWidgetPayload, -} from "layoutSystems/anvil/utils/widgetAdditionUtils"; +import { moveWidgetsToMainCanvas } from "layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils"; + +import { moveWidgetsToSection } from "layoutSystems/anvil/utils/layouts/update/sectionUtils"; import { updateAndSaveAnvilLayout } from "../../../utils/anvilChecksUtils"; import { isRedundantZoneWidget, @@ -49,259 +24,6 @@ import { import { severTiesFromParents } from "../../../utils/layouts/update/moveUtils"; import { widgetChildren } from "../../../utils/layouts/widgetUtils"; -// Function to retrieve highlighting information for the last row in the main canvas layout -export function* getMainCanvasLastRowHighlight() { - // Retrieve the main canvas widget - const mainCanvas: WidgetProps = yield select( - getWidget, - MAIN_CONTAINER_WIDGET_ID, - ); - - // Extract the layout ID and row index for the last row in the main canvas - const layoutId: string = mainCanvas.layout[0].layoutId; - const layoutOrder = [layoutId]; - const rowIndex = mainCanvas.layout[0].layout.length; - - // Return the highlighting information for the last row in the main canvas - return { - canvasId: MAIN_CONTAINER_WIDGET_ID, - layoutOrder, - rowIndex, - posX: 0, - posY: 0, - alignment: FlexLayerAlignment.Start, - dropZone: {}, - height: 0, - width: 0, - isVertical: false, - }; -} - -// function to handle adding suggested widgets to the Anvil canvas -function* addSuggestedWidgetsAnvilSaga( - actionPayload: ReduxAction<{ - newWidget: { - newWidgetId: string; - type: string; - rows?: number; - columns?: number; - props: WidgetProps; - detachFromLayout: boolean; - }; - }>, -) { - const { newWidget } = actionPayload.payload; - - // Find the corresponding WDS entry for the given widget type - const wdsEntry = Object.entries(WDS_V2_WIDGET_MAP).find( - ([legacyType]) => legacyType === newWidget.type, - ); - - // If a matching WDS entry is found, proceed with adding the suggested widget - if (wdsEntry) { - // Extract the WDS type for the suggested widget - const [, wdsType] = wdsEntry; - - // Define parameters for the new widget based on the WDS type and provided dimensions - const newWidgetParams = { - width: (newWidget.rows || 0 / GridDefaults.DEFAULT_GRID_COLUMNS) * 100, - height: newWidget.columns || 0 * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - newWidgetId: newWidget.newWidgetId, - parentId: MAIN_CONTAINER_WIDGET_ID, - type: wdsType, - detachFromLayout: newWidget.detachFromLayout, - }; - - // Get highlighting information for the last row in the main canvas - const mainCanvasHighLight: AnvilHighlightInfo = yield call( - getMainCanvasLastRowHighlight, - ); - - // Add the new widget to the DSL - const updatedWidgets: CanvasWidgetsReduxState = yield call( - addNewChildToDSL, - mainCanvasHighLight, - newWidgetParams, - true, - false, - ); - - // Update the widget properties with the properties provided in the action payload - updatedWidgets[newWidgetParams.newWidgetId] = { - ...updatedWidgets[newWidgetParams.newWidgetId], - ...newWidget.props, - }; - - // Save the updated Anvil layout - yield call(updateAndSaveAnvilLayout, updatedWidgets); - - // Select the added widget - yield put( - selectWidgetInitAction(SelectionRequestType.One, [ - newWidgetParams.newWidgetId, - ]), - ); - } -} - -// function to add a new child widget to the DSL -export function* addNewChildToDSL( - highlight: AnvilHighlightInfo, // Highlight information for the drop zone - newWidget: { - width: number; - height: number; - newWidgetId: string; - type: string; - detachFromLayout: boolean; - }, - isMainCanvas: boolean, // Indicates if the drop zone is the main canvas - isSection: boolean, // Indicates if the drop zone is a section -) { - const { alignment, canvasId } = highlight; - const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); - - const parentWidgetWithLayout = allWidgets[canvasId]; - let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets }; - - const draggedWidgets: WidgetLayoutProps[] = [ - { - alignment, - widgetId: newWidget.newWidgetId, - widgetType: newWidget.type, - }, - ]; - - if (newWidget.detachFromLayout) { - updatedWidgets = yield call(addDetachedWidgetToMainCanvas, updatedWidgets, { - widgetId: newWidget.newWidgetId, - type: newWidget.type, - }); - } else { - // Handle different scenarios based on the drop zone type (main canvas, section, or generic layout) - if (!!isMainCanvas || parentWidgetWithLayout.detachFromLayout) { - updatedWidgets = yield call( - addWidgetsToMainCanvasLayout, - updatedWidgets, - draggedWidgets, - highlight, - ); - } else if (!!isSection) { - const res: { canvasWidgets: CanvasWidgetsReduxState } = yield call( - addWidgetsToSection, - updatedWidgets, - draggedWidgets, - highlight, - updatedWidgets[canvasId], - ); - updatedWidgets = res.canvasWidgets; - } else { - updatedWidgets = yield call( - addWidgetToGenericLayout, - updatedWidgets, - draggedWidgets, - highlight, - newWidget, - ); - } - } - return updatedWidgets; -} - -// function to handle the addition of new widgets to the Anvil layout -export function* addWidgetsSaga( - actionPayload: ReduxAction, -) { - try { - const start = performance.now(); - - const { - dragMeta: { draggedOn }, - highlight, - newWidget, - } = actionPayload.payload; - // Check if the drop zone is the main canvas - const isMainCanvas = draggedOn === "MAIN_CANVAS"; - // Check if the drop zone is a section - const isSection = draggedOn === "SECTION"; - - // Call the addNewChildToDSL saga to perform the actual addition of the new widget to the DSL - const updatedWidgets: CanvasWidgetsReduxState = yield call( - addNewChildToDSL, - highlight, - newWidget, - !!isMainCanvas, - !!isSection, - ); - - // Save the updated Anvil layout - yield call(updateAndSaveAnvilLayout, updatedWidgets); - - // Select the newly added widget - yield put( - selectWidgetInitAction(SelectionRequestType.Create, [ - newWidget.newWidgetId, - ]), - ); - - log.debug("Anvil: add new widget took", performance.now() - start, "ms"); - } catch (error) { - yield put({ - type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, - payload: { - action: AnvilReduxActionTypes.ANVIL_ADD_NEW_WIDGET, - error, - }, - }); - } -} - -function* addWidgetToGenericLayout( - allWidgets: CanvasWidgetsReduxState, - draggedWidgets: WidgetLayoutProps[], - highlight: AnvilHighlightInfo, - newWidget: { - width: number; - height: number; - newWidgetId: string; - type: string; - }, -) { - let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets }; - const canvasWidget = updatedWidgets[highlight.canvasId]; - const canvasLayout = canvasWidget.layout - ? canvasWidget.layout - : generateDefaultLayoutPreset(); - - /** - * Create widget and add to parent. - */ - updatedWidgets = yield call( - addNewWidgetToDsl, - updatedWidgets, - getCreateWidgetPayload( - newWidget.newWidgetId, - newWidget.type, - canvasWidget.widgetId, - ), - ); - /** - * Also add it to parent's layout. - */ - return { - ...updatedWidgets, - [canvasWidget.widgetId]: { - ...updatedWidgets[canvasWidget.widgetId], - layout: addWidgetsToPreset(canvasLayout, highlight, draggedWidgets), - }, - [newWidget.newWidgetId]: { - ...updatedWidgets[newWidget.newWidgetId], - // This is a temp fix, widget dimensions will be self computed by widgets - height: newWidget.height, - width: newWidget.width, - }, - }; -} - /** * Remove widgets from current parents and layouts. * Add to new parent and layout. @@ -421,11 +143,6 @@ export function handleDeleteRedundantZones( export default function* anvilDraggingSagas() { yield all([ - takeLatest(AnvilReduxActionTypes.ANVIL_ADD_NEW_WIDGET, addWidgetsSaga), takeLatest(AnvilReduxActionTypes.ANVIL_MOVE_WIDGET, moveWidgetsSaga), - takeLatest( - AnvilReduxActionTypes.ANVIL_ADD_SUGGESTED_WIDGET, - addSuggestedWidgetsAnvilSaga, - ), ]); } diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.test.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.test.ts new file mode 100644 index 000000000000..c702495c8e20 --- /dev/null +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.test.ts @@ -0,0 +1,196 @@ +import type { WidgetProps } from "widgets/BaseWidget"; +import { + addChildReferenceToParent, + getUniqueWidgetName, + runBlueprintOperationsOnWidgets, + updateWidgetListWithNewWidget, +} from "./helpers"; +import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import { BlueprintOperationTypes } from "WidgetProvider/constants"; +import { expectSaga } from "redux-saga-test-plan"; +import { select } from "redux-saga/effects"; +import { getDataTree } from "selectors/dataTreeSelectors"; +import { getLayoutSystemType } from "selectors/layoutSystemSelectors"; +import WidgetFactory from "WidgetProvider/factory"; + +describe("addChildReferenceToParent", () => { + it("should add a new child reference to the parent widget", () => { + const widgets: Record> = { + parentId: { + widgetId: "parentId", + children: [], + }, + childWidgetId: { + widgetId: "childWidgetId", + }, + }; + + const updatedWidgets = addChildReferenceToParent( + widgets as CanvasWidgetsReduxState, + "parentId", + "childWidgetId", + ); + + expect(updatedWidgets["parentId"].children).toContain("childWidgetId"); + }); + + it("should handle parent widget without children property", () => { + const widgets: Record> = { + parentId: { + widgetId: "parentId", + }, + childWidgetId: { + widgetId: "childWidgetId", + }, + }; + + const updatedWidgets = addChildReferenceToParent( + widgets as CanvasWidgetsReduxState, + "parentId", + "childWidgetId", + ); + + expect(updatedWidgets["parentId"].children).toContain("childWidgetId"); + }); +}); + +describe("getUniqueWidgetName", () => { + it("should generate a unique widget name within the dataTree", async () => { + const prefix = "widget"; + const result = await expectSaga(getUniqueWidgetName, prefix) + .provide([[select(getDataTree), { someWidget: {} }]]) + .run(); + + expect(result.returnValue).toBe("widget1"); + }); + + it("should handle multiple widgets with the same prefix", async () => { + const prefix = "widget"; + const result = await expectSaga(getUniqueWidgetName, prefix) + .provide([[select(getDataTree), { widget1: {}, widget2: {} }]]) + .run(); + + expect(result.returnValue).toBe("widget3"); + }); +}); + +describe("runBlueprintOperationsOnWidgets", () => { + it("should call executeWidgetBlueprintOperations with the blueprint operations and widgets", async () => { + const widgets: Record> = { + widgetId: { + widgetId: "widgetId", + }, + }; + const blueprint = { + operations: [ + { + type: BlueprintOperationTypes.MODIFY_PROPS, + fn: () => [ + { + widgetId: "widgetId", + propertyName: "isOpen", + propertyValue: true, + }, + ], + }, + ], + }; + + const expected = { + widgetId: { + widgetId: "widgetId", + isOpen: true, + }, + }; + + const result = await expectSaga( + runBlueprintOperationsOnWidgets, + widgets as CanvasWidgetsReduxState, + "widgetId", + blueprint, + ) + .provide([[select(getLayoutSystemType), "ANVIL"]]) + .run(); + + expect(result.returnValue).toStrictEqual(expected); + }); +}); + +describe("updateWidgetListWithNewWidget", () => { + it("should updated list of widgets correctly with the new widget", async () => { + const blueprint = { + operations: [ + { + type: BlueprintOperationTypes.MODIFY_PROPS, + fn: () => [ + { + widgetId: "newWidgetId", + propertyName: "isOpen", + propertyValue: true, + }, + ], + }, + ], + }; + WidgetFactory.widgetDefaultPropertiesMap.get = jest.fn(() => ({ + someRandomProperty: "random", + widgetName: "widgetName", + version: 1, + blueprint, + })); + + const params = { + parentId: "parentId", + widgetId: "newWidgetId", + type: "widgetType", + }; + + const widgets: Record> = { + parentId: { + widgetId: "parentId", + widgetName: "parentWidget", + }, + existingWidget: { + widgetName: "widgetName", + widgetId: "existingWidget", + }, + }; + + const expected = { + parentId: { + widgetId: "parentId", + children: ["newWidgetId"], + widgetName: "parentWidget", + }, + newWidgetId: { + someRandomProperty: "random", + widgetName: "widgetName1", + parentId: "parentId", + widgetId: "newWidgetId", + type: "widgetType", + isOpen: true, + version: 1, + blueprint: undefined, + rows: undefined, + columns: undefined, + }, + existingWidget: { + widgetName: "widgetName", + widgetId: "existingWidget", + }, + }; + + const result = await expectSaga( + updateWidgetListWithNewWidget as any, + params, + widgets as CanvasWidgetsReduxState, + ) + .provide([ + [select(getDataTree), { existingWidget: {}, parentWidget: {} }], + [select(getLayoutSystemType), "ANVIL"], + ]) + .run(); + + expect(result.returnValue).toStrictEqual(expected); + }); +}); diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.ts new file mode 100644 index 000000000000..0451da37713c --- /dev/null +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers.ts @@ -0,0 +1,249 @@ +import type { + FlattenedWidgetProps, + WidgetBlueprint, +} from "WidgetProvider/constants"; +import { getNextEntityName } from "utils/AppsmithUtils"; +import type { DataTree } from "entities/DataTree/dataTreeTypes"; +import { getDataTree } from "selectors/dataTreeSelectors"; +import WidgetFactory from "WidgetProvider/factory"; +import { executeWidgetBlueprintOperations } from "sagas/WidgetBlueprintSagas"; +import { call, put, select } from "redux-saga/effects"; +import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import AppsmithConsole from "utils/AppsmithConsole"; +import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils"; +import { WidgetReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; + +/** + * In Anvil, we maintain some properties set by users on widgets. + * When a similar or same widget is added to the Canvas, we retrieve these + * properties and apply them. + * This is to assist users by automatically apply settings based on context. + + * Retrieves the values from session storage for the widget properties + * for hydration of the widget when we create widget on drop + */ +export function getWidgetSessionValues( + type: string, + parent: FlattenedWidgetProps, +) { + // For WDS_INLINE_BUTTONS_WIDGET, we want to hydation only to work when we add more items to the inline button group. + // So we don't want to hydrate the values when we drop the widget on the canvas. + if (["WDS_INLINE_BUTTONS_WIDGET"].includes(type)) return; + + let widgetType = type; + const configMap = WidgetFactory.widgetConfigMap.get(type); + + const widgetSessionValues: any = {}; + + // in case we are dropping WDS_ICON_BUTTON_WIDGET, we want to reuse the values of BUTTON_WIDGET + if (type === "WDS_ICON_BUTTON_WIDGET") { + widgetType = "WDS_BUTTON_WIDGET"; + } + + for (const key in configMap) { + if (configMap[key] === undefined) continue; + let sessionStorageKey = `${widgetType}.${key}`; + + if (type === "ZONE_WIDGET") { + sessionStorageKey = `${widgetType}.${parent.widgetId}.${key}`; + } + + let valueFromSession: any = sessionStorage.getItem(sessionStorageKey); + + // parse "true" as true and "false" as false + if (valueFromSession === "true") { + valueFromSession = true; + } else if (valueFromSession === "false") { + valueFromSession = false; + } + + if (valueFromSession !== undefined && valueFromSession !== null) { + widgetSessionValues[key] = valueFromSession; + } + } + + return widgetSessionValues; +} + +export function* getUniqueWidgetName(prefix: string) { + // The dataTree contains all entities (widgets, actions, etc). + // We need to make sure that none of the entities have the same name + // as the evaluations use the names of the entities as the unique identifier + // for entities. + const evalTree: DataTree = yield select(getDataTree); + const entityNames = Object.keys(evalTree); + + // Get a new widget name that doesn't conflict with any existing entity names + const widgetName = getNextEntityName( + prefix, // The widget name prefix configured by the widget + entityNames, + ); + return widgetName; +} + +/** + * This generator function runs the blueprint operations configured in the new + * widget being added. + * @param widgets Canvas Widgets + * @param widgetId Widget Id of the new widget being added + * @param blueprint The configured operations to be run on the list of widgets + * @returns An updated list of widgets + */ +export function* runBlueprintOperationsOnWidgets( + widgets: CanvasWidgetsReduxState, + widgetId: string, + blueprint?: WidgetBlueprint, +) { + if (!blueprint?.operations || blueprint.operations.length === 0) { + return widgets; + } + // Some widgets need to run a few operations like modifying props or adding an action + // these operations can be performed on the parent of the widget we're adding + // therefore, we pass all widgets to executeWidgetBlueprintOperations + // blueprint.operations contain the set of operations to perform to update the canvasWidgets + // The blueprint operations configuration can be found in the default configurations of the widgets + // Finalize the canvasWidgets with everything that needs to be updated + const updatedWidgets: CanvasWidgetsReduxState = yield call( + executeWidgetBlueprintOperations, + blueprint.operations, + widgets, + widgetId, + ); + return updatedWidgets; +} + +/** + * This function helps reference the new widget in the list of children + * of the existing parent widget + * @param widgets Canvas Widgets + * @param parentId Parent into which the new widget is added + * @param newChildWidgetId WidgetId of the new child widget + * @returns An updated list of widgets + */ +export function addChildReferenceToParent( + widgets: CanvasWidgetsReduxState, + parentId: string, + newChildWidgetId: string, +): CanvasWidgetsReduxState { + const stateParent = widgets[parentId]; + // Update widgets to put back in the canvasWidgetsReducer + const parent = { + ...stateParent, + children: [...(stateParent.children || []), newChildWidgetId], + }; + + widgets[parent.widgetId] = parent; + return widgets; +} + +/** + * This helper generator function adds all properties to the new widget so that + * it can be used in the application. The return value will eventually be stored + * in the CanvasWidgetsReducer (normalised) and the servers (DSL - denormalised) + * @param params The new widget's id, the parent whose child it is, and the type of the widget + * @param widgets The list of widgets in which we will add the new widget + * @returns An Object with the new list of widgets that also contains the new widget + */ +export function* updateWidgetListWithNewWidget( + params: { + parentId: string; + type: string; + widgetId: string; + }, + widgets: CanvasWidgetsReduxState, +) { + const { parentId, type, widgetId } = params; + + // Get default properties of the widget configured by the widget + const widgetDefaultProperties = WidgetFactory.widgetDefaultPropertiesMap.get( + type, + ) as Record; + + // Hydrate widget with properties based previously configured values of similar widgets + const widgetSessionValues = getWidgetSessionValues(type, widgets[parentId]); + + // Get a unique name for the new widget + const widgetName: string = yield getUniqueWidgetName( + widgetDefaultProperties.widgetName as string, + ); + + const widget = { + ...widgetDefaultProperties, + parentId, + widgetId, + type, + widgetName, + version: widgetDefaultProperties.version, + ...widgetSessionValues, // This is at the end of the spread to override + blueprint: undefined, // This is usually non-serializable and needs to be removed + rows: undefined, + columns: undefined, + }; + + // Add the widget to the canvasWidgets (type: CanvasWidgetsReduxState) + widgets[widget.widgetId] = widget; + + // Run blueprint operations an update the list of widgets + let updatedWidgets: CanvasWidgetsReduxState = + yield runBlueprintOperationsOnWidgets( + widgets, + widgetId, + widgetDefaultProperties.blueprint as WidgetBlueprint | undefined, + ); + + // Reference new widget in the parent widget + updatedWidgets = addChildReferenceToParent( + updatedWidgets, + parentId, + widgetId, + ); + + return updatedWidgets; +} + +/** + * This is a general orchestrator generator function that calls the appropriate + * operations to complete the addition of new widget into the list of widgets in + * the system + * @param stateWidgets The list of widgets from redux state + * @param payload Payload of the redux action that will be used to create the new child widget + * @returns An updated list of widgets + */ +export function* addNewAnvilWidgetToDSL( + widgets: CanvasWidgetsReduxState, + payload: { + parentId: string; + type: string; + widgetId: string; + }, +) { + const { type, widgetId } = payload; + + const updatedWidgets: CanvasWidgetsReduxState = + yield updateWidgetListWithNewWidget(payload, widgets); + + // We need to have a separate helper which listens to specific redux actions + // and updates the console accordingly. Doing it here doesn't seem right. + // In this case we may listen to `WIDGET_CHILD_ADDED` action. + // https://github.com/appsmithorg/appsmith/issues/35161 + AppsmithConsole.info({ + text: "Widget was created", + source: { + type: ENTITY_TYPE.WIDGET, + id: widgetId, + name: updatedWidgets[widgetId].widgetName, + }, + }); + + // This redux action seems useless, but it is possible that evaluations + // uses this to trigger evaluations. + yield put({ + type: WidgetReduxActionTypes.WIDGET_CHILD_ADDED, + payload: { + widgetId: widgetId, + type: type, + }, + }); + + return updatedWidgets; +} diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/index.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/index.ts new file mode 100644 index 000000000000..ee43487e60fd --- /dev/null +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/index.ts @@ -0,0 +1,265 @@ +import { all, call, put, select, takeLatest } from "redux-saga/effects"; +import type { AnvilNewWidgetsPayload } from "../../actions/actionTypes"; +import { AnvilReduxActionTypes } from "../../actions/actionTypes"; +import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; +import type { + AnvilHighlightInfo, + WidgetLayoutProps, +} from "layoutSystems/anvil/utils/anvilTypes"; +import { + ReduxActionErrorTypes, + type ReduxAction, +} from "@appsmith/constants/ReduxActionConstants"; +import type { WidgetProps } from "widgets/BaseWidget"; +import { WDS_V2_WIDGET_MAP } from "widgets/wds/constants"; +import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; +import { getMainCanvasLastRowHighlight } from "../anvilDraggingSagas/helpers"; +import { updateAndSaveAnvilLayout } from "layoutSystems/anvil/utils/anvilChecksUtils"; +import { selectWidgetInitAction } from "actions/widgetSelectionActions"; +import { SelectionRequestType } from "sagas/WidgetSelectUtils"; +import { getWidgets } from "sagas/selectors"; +import { + addDetachedWidgetToMainCanvas, + addWidgetsToMainCanvasLayout, +} from "layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils"; +import { addWidgetsToSection } from "layoutSystems/anvil/utils/layouts/update/sectionUtils"; +import log from "loglevel"; +import { generateDefaultLayoutPreset } from "layoutSystems/anvil/layoutComponents/presets/DefaultLayoutPreset"; +import { addWidgetsToPreset } from "layoutSystems/anvil/utils/layouts/update/additionUtils"; +import { addNewAnvilWidgetToDSL } from "./helpers"; + +// The suggested widget functionality allows users to bind data from the Query pane +// to a new or existing widget on the Canvas. +// This saga handles the necessary logic to add an the suggested widget to an Anvil canvas +function* addSuggestedWidgetToDSL( + actionPayload: ReduxAction<{ + newWidget: { + newWidgetId: string; + type: string; + props: WidgetProps; + detachFromLayout: boolean; + }; + }>, +) { + try { + const { newWidget } = actionPayload.payload; + + // Find the corresponding WDS entry for the given widget type + const wdsEntry = Object.entries(WDS_V2_WIDGET_MAP).find( + ([legacyType]) => legacyType === newWidget.type, + ); + + // If a matching WDS entry is found, proceed with adding the suggested widget + if (wdsEntry) { + // Extract the WDS type for the suggested widget + const [, wdsType] = wdsEntry; + + // Define parameters for the new widget based on the WDS type and provided dimensions + const newWidgetParams = { + newWidgetId: newWidget.newWidgetId, + parentId: MAIN_CONTAINER_WIDGET_ID, + type: wdsType, + detachFromLayout: newWidget.detachFromLayout, + }; + + // Get highlighting information for the last row in the main canvas + const mainCanvasHighLight: AnvilHighlightInfo = yield call( + getMainCanvasLastRowHighlight, + ); + + // Add the new widget to the DSL + const updatedWidgets: CanvasWidgetsReduxState = + yield getUpdatedListOfWidgetsAfterAddingNewWidget( + mainCanvasHighLight, + newWidgetParams, + true, + false, + ); + + // Update the widget properties with the properties provided in the action payload + updatedWidgets[newWidgetParams.newWidgetId] = { + ...updatedWidgets[newWidgetParams.newWidgetId], + ...newWidget.props, + }; + + // Save the updated Anvil layout + yield call(updateAndSaveAnvilLayout, updatedWidgets); + + // Select the added widget + yield put( + selectWidgetInitAction(SelectionRequestType.One, [ + newWidgetParams.newWidgetId, + ]), + ); + } + } catch (e) { + log.debug("Error adding suggested widget to Anvil Canvas: ", e); + } +} + +// function to add a new child widget to the DSL +export function* getUpdatedListOfWidgetsAfterAddingNewWidget( + highlight: AnvilHighlightInfo, // Highlight information for the drop zone + newWidget: { + newWidgetId: string; + type: string; + detachFromLayout: boolean; + }, + isMainCanvas: boolean, // Indicates if the drop zone is the main canvas + isSection: boolean, // Indicates if the drop zone is a section +) { + const { alignment, canvasId } = highlight; + const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets); + + const parentWidgetWithLayout = allWidgets[canvasId]; + + // Creating a shallow copy of the widgets from redux state + // as these will be mutated in the course of the operation + let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets }; + + const draggedWidgets: WidgetLayoutProps[] = [ + { + alignment, + widgetId: newWidget.newWidgetId, + widgetType: newWidget.type, + }, + ]; + + // Follow the operations necessary for detached widgets like Modal Widget + if (newWidget.detachFromLayout) { + updatedWidgets = yield call(addDetachedWidgetToMainCanvas, updatedWidgets, { + widgetId: newWidget.newWidgetId, + type: newWidget.type, + }); + } else { + // Handle different scenarios based on the drop zone type (main canvas, section, or generic layout) + // If the widget is dropped in the main canvas or into a detached widget like the Modal Widget + if (isMainCanvas || parentWidgetWithLayout.detachFromLayout) { + updatedWidgets = yield call( + addWidgetsToMainCanvasLayout, + updatedWidgets, + draggedWidgets, + highlight, + ); + } else if (isSection) { + const res: { canvasWidgets: CanvasWidgetsReduxState } = yield call( + addWidgetsToSection, + updatedWidgets, + draggedWidgets, + highlight, + updatedWidgets[canvasId], + ); + updatedWidgets = res.canvasWidgets; + } else { + // The typical operation when adding widgets to a zone + updatedWidgets = yield addWidgetToGenericLayout( + updatedWidgets, + draggedWidgets, + highlight, + newWidget, + ); + } + } + return updatedWidgets; +} + +// function to handle the addition of new widgets to the Anvil layout +export function* addWidgetsSaga( + actionPayload: ReduxAction, +) { + try { + const start = performance.now(); + + const { + dragMeta: { draggedOn }, + highlight, + newWidget, + } = actionPayload.payload; + // Check if the drop zone is the main canvas + const isMainCanvas = draggedOn === "MAIN_CANVAS"; + // Check if the drop zone is a section + const isSection = draggedOn === "SECTION"; + + // Call the addNewChildToDSL saga to perform the actual addition of the new widget to the DSL + const updatedWidgets: CanvasWidgetsReduxState = yield call( + getUpdatedListOfWidgetsAfterAddingNewWidget, + highlight, + newWidget, + !!isMainCanvas, + !!isSection, + ); + + // Save the updated Anvil layout + yield call(updateAndSaveAnvilLayout, updatedWidgets); + + // Select the newly added widget + yield put( + selectWidgetInitAction(SelectionRequestType.Create, [ + newWidget.newWidgetId, + ]), + ); + + log.debug("Anvil: add new widget took", performance.now() - start, "ms"); + } catch (error) { + yield put({ + type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR, + payload: { + action: AnvilReduxActionTypes.ANVIL_ADD_NEW_WIDGET, + error, + }, + }); + } +} + +function* addWidgetToGenericLayout( + allWidgets: CanvasWidgetsReduxState, + draggedWidgets: WidgetLayoutProps[], + highlight: AnvilHighlightInfo, + newWidget: { + newWidgetId: string; + type: string; + }, +) { + let updatedWidgets: CanvasWidgetsReduxState = { ...allWidgets }; + const canvasWidget = updatedWidgets[highlight.canvasId]; + const canvasLayout = canvasWidget.layout + ? canvasWidget.layout + : generateDefaultLayoutPreset(); + + const newWidgetContext = { + widgetId: newWidget.newWidgetId, + type: newWidget.type, + parentId: canvasWidget.widgetId, + }; + + /** + * Create widget and add to parent. + */ + updatedWidgets = yield addNewAnvilWidgetToDSL( + updatedWidgets, + newWidgetContext, + ); + /** + * Also add it to parent's layout. + */ + return { + ...updatedWidgets, + [canvasWidget.widgetId]: { + ...updatedWidgets[canvasWidget.widgetId], + layout: addWidgetsToPreset(canvasLayout, highlight, draggedWidgets), + }, + [newWidget.newWidgetId]: { + ...updatedWidgets[newWidget.newWidgetId], + }, + }; +} + +export default function* anvilWidgetAdditionSagas() { + yield all([ + takeLatest(AnvilReduxActionTypes.ANVIL_ADD_NEW_WIDGET, addWidgetsSaga), + takeLatest( + AnvilReduxActionTypes.ANVIL_ADD_SUGGESTED_WIDGET, + addSuggestedWidgetToDSL, + ), + ]); +} diff --git a/app/client/src/layoutSystems/anvil/integrations/sagas/index.ts b/app/client/src/layoutSystems/anvil/integrations/sagas/index.ts index 69337fd560eb..d4851b070027 100644 --- a/app/client/src/layoutSystems/anvil/integrations/sagas/index.ts +++ b/app/client/src/layoutSystems/anvil/integrations/sagas/index.ts @@ -5,6 +5,7 @@ import anvilSectionSagas from "./sectionSagas"; import anvilSpaceDistributionSagas from "./anvilSpaceDistributionSagas"; import anvilWidgetSelectionSaga from "./anvilWidgetSelectionSaga"; import pasteSagas from "./pasteSagas"; +import anvilWidgetAdditionSagas from "./anvilWidgetAdditionSagas"; export default function* anvilSagas() { yield fork(LayoutElementPositionsSaga); @@ -13,4 +14,5 @@ export default function* anvilSagas() { yield fork(anvilSpaceDistributionSagas); yield fork(anvilWidgetSelectionSaga); yield fork(pasteSagas); + yield fork(anvilWidgetAdditionSagas); } diff --git a/app/client/src/layoutSystems/anvil/layoutComponents/presets/sectionPreset.tsx b/app/client/src/layoutSystems/anvil/layoutComponents/presets/sectionPreset.tsx index be5c56b2d451..5b792fb6366b 100644 --- a/app/client/src/layoutSystems/anvil/layoutComponents/presets/sectionPreset.tsx +++ b/app/client/src/layoutSystems/anvil/layoutComponents/presets/sectionPreset.tsx @@ -12,9 +12,6 @@ export const sectionPreset = (): LayoutProps[] => { isPermanent: true, layout: [], layoutId: generateReactKey(), - layoutStyle: { - border: "none", - }, layoutType: LayoutComponentTypes.SECTION, maxChildLimit: 4, }, diff --git a/app/client/src/layoutSystems/anvil/layoutComponents/presets/zonePreset.tsx b/app/client/src/layoutSystems/anvil/layoutComponents/presets/zonePreset.tsx index 05a84d31293a..52d1e740a580 100644 --- a/app/client/src/layoutSystems/anvil/layoutComponents/presets/zonePreset.tsx +++ b/app/client/src/layoutSystems/anvil/layoutComponents/presets/zonePreset.tsx @@ -12,10 +12,6 @@ export const zonePreset = (): LayoutProps[] => { isPermanent: true, layout: [], layoutId: generateReactKey(), - layoutStyle: { - border: "none", - height: "100%", - }, layoutType: LayoutComponentTypes.ZONE, }, ]; diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils.ts b/app/client/src/layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils.ts index 5465a6cfaf03..647e0fb323ba 100644 --- a/app/client/src/layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils.ts +++ b/app/client/src/layoutSystems/anvil/utils/layouts/update/mainCanvasLayoutUtils.ts @@ -13,10 +13,7 @@ import { call } from "redux-saga/effects"; import { severTiesFromParents, transformMovedWidgets } from "./moveUtils"; import { anvilWidgets } from "widgets/anvil/constants"; import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants"; -import { - addNewWidgetToDsl, - getCreateWidgetPayload, -} from "../../widgetAdditionUtils"; +import { addNewAnvilWidgetToDSL } from "layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers"; /** * This function adds a detached widget to the main canvas. @@ -30,14 +27,13 @@ export function* addDetachedWidgetToMainCanvas( allWidgets: CanvasWidgetsReduxState, draggedWidget: { widgetId: string; type: string }, ) { - const updatedWidgets: CanvasWidgetsReduxState = yield call( - addNewWidgetToDsl, + const updatedWidgets: CanvasWidgetsReduxState = yield addNewAnvilWidgetToDSL( allWidgets, - getCreateWidgetPayload( - draggedWidget.widgetId, - draggedWidget.type, - MAIN_CONTAINER_WIDGET_ID, - ), + { + widgetId: draggedWidget.widgetId, + type: draggedWidget.type, + parentId: MAIN_CONTAINER_WIDGET_ID, + }, ); return updatedWidgets; } diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/update/sectionUtils.ts b/app/client/src/layoutSystems/anvil/utils/layouts/update/sectionUtils.ts index 427a57c27522..54fd4b7fe702 100644 --- a/app/client/src/layoutSystems/anvil/utils/layouts/update/sectionUtils.ts +++ b/app/client/src/layoutSystems/anvil/utils/layouts/update/sectionUtils.ts @@ -13,11 +13,8 @@ import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidg import { call } from "redux-saga/effects"; import { severTiesFromParents, transformMovedWidgets } from "./moveUtils"; import type { FlattenedWidgetProps } from "WidgetProvider/constants"; -import { - addNewWidgetToDsl, - getCreateWidgetPayload, -} from "../../widgetAdditionUtils"; import { anvilWidgets } from "widgets/anvil/constants"; +import { addNewAnvilWidgetToDSL } from "layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers"; export function* createSectionAndAddWidget( allWidgets: CanvasWidgetsReduxState, @@ -29,10 +26,13 @@ export function* createSectionAndAddWidget( * Step 1: Create Section widget. */ const widgetId: string = generateReactKey(); - const updatedWidgets: CanvasWidgetsReduxState = yield call( - addNewWidgetToDsl, + const updatedWidgets: CanvasWidgetsReduxState = yield addNewAnvilWidgetToDSL( allWidgets, - getCreateWidgetPayload(widgetId, anvilWidgets.SECTION_WIDGET, parentId), + { + widgetId, + type: anvilWidgets.SECTION_WIDGET, + parentId, + }, ); /** @@ -93,15 +93,11 @@ function* addZoneToSection( * => New widget. * => Create it and add to section. */ - canvasWidgets = yield call( - addNewWidgetToDsl, - canvasWidgets, - getCreateWidgetPayload( - zoneWidgetId, - anvilWidgets.ZONE_WIDGET, - sectionWidgetId, - ), - ); + canvasWidgets = yield addNewAnvilWidgetToDSL(canvasWidgets, { + widgetId: zoneWidgetId, + type: anvilWidgets.ZONE_WIDGET, + parentId: sectionWidgetId, + }); } else { /** * Add zone widgetIds to canvas.children. diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/update/zoneUtils.ts b/app/client/src/layoutSystems/anvil/utils/layouts/update/zoneUtils.ts index e920e5d2cbc4..96ece0e14b2c 100644 --- a/app/client/src/layoutSystems/anvil/utils/layouts/update/zoneUtils.ts +++ b/app/client/src/layoutSystems/anvil/utils/layouts/update/zoneUtils.ts @@ -10,10 +10,6 @@ import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidg import { call } from "redux-saga/effects"; import { addWidgetsToChildTemplate } from "./additionUtils"; import type { FlattenedWidgetProps } from "WidgetProvider/constants"; -import { - addNewWidgetToDsl, - getCreateWidgetPayload, -} from "../../widgetAdditionUtils"; import { isLargeWidget } from "../../widgetUtils"; import { anvilWidgets } from "widgets/anvil/constants"; import { @@ -27,6 +23,7 @@ import { isEmptyWidget, widgetChildren, } from "../widgetUtils"; +import { addNewAnvilWidgetToDSL } from "layoutSystems/anvil/integrations/sagas/anvilWidgetAdditionSagas/helpers"; export function* createZoneAndAddWidgets( allWidgets: CanvasWidgetsReduxState, @@ -38,10 +35,13 @@ export function* createZoneAndAddWidgets( * Create Zone widget. */ const widgetId: string = generateReactKey(); - const updatedWidgets: CanvasWidgetsReduxState = yield call( - addNewWidgetToDsl, + const updatedWidgets: CanvasWidgetsReduxState = yield addNewAnvilWidgetToDSL( allWidgets, - getCreateWidgetPayload(widgetId, anvilWidgets.ZONE_WIDGET, parentId), + { + widgetId, + type: anvilWidgets.ZONE_WIDGET, + parentId, + }, ); /** @@ -176,11 +176,11 @@ function* updateDraggedWidgets( /** * Create new widget with zone as the parent. */ - updatedWidgets = yield call( - addNewWidgetToDsl, - allWidgets, - getCreateWidgetPayload(widgetId, widgetType, zoneWidgetId), - ); + updatedWidgets = yield addNewAnvilWidgetToDSL(allWidgets, { + widgetId, + type: widgetType, + parentId: zoneWidgetId, + }); } return updatedWidgets; } diff --git a/app/client/src/layoutSystems/anvil/utils/sectionOperationUtils.ts b/app/client/src/layoutSystems/anvil/utils/sectionOperationUtils.ts index e7ff6de8721c..a6b2551bf673 100644 --- a/app/client/src/layoutSystems/anvil/utils/sectionOperationUtils.ts +++ b/app/client/src/layoutSystems/anvil/utils/sectionOperationUtils.ts @@ -10,11 +10,11 @@ import type { import { call, select } from "redux-saga/effects"; import { getWidgets } from "sagas/selectors"; import { generateReactKey } from "utils/generators"; -import { addNewChildToDSL } from "../integrations/sagas/anvilDraggingSagas"; import type BaseLayoutComponent from "../layoutComponents/BaseLayoutComponent"; import LayoutFactory from "../layoutComponents/LayoutFactory"; import { defaultHighlightRenderInfo } from "../utils/constants"; import { anvilWidgets } from "widgets/anvil/constants"; +import { getUpdatedListOfWidgetsAfterAddingNewWidget } from "../integrations/sagas/anvilWidgetAdditionSagas"; /** * Function to get the highlight information for the last column of a Section Widget @@ -173,7 +173,7 @@ export function* addNewZonesToSection( }; const highlight = getSectionLastColumnHighlight(sectionWidget); updatedWidgets = yield call( - addNewChildToDSL, + getUpdatedListOfWidgetsAfterAddingNewWidget, highlight, newWidget, false, diff --git a/app/client/src/layoutSystems/anvil/utils/widgetAdditionUtils.ts b/app/client/src/layoutSystems/anvil/utils/widgetAdditionUtils.ts index 3577fc8cd201..347bccddeb32 100644 --- a/app/client/src/layoutSystems/anvil/utils/widgetAdditionUtils.ts +++ b/app/client/src/layoutSystems/anvil/utils/widgetAdditionUtils.ts @@ -1,7 +1,6 @@ import type { FlattenedWidgetProps } from "WidgetProvider/constants"; import type { WidgetAddChild } from "actions/pageActions"; import { WidgetReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; -import { GridDefaults } from "constants/WidgetConstants"; import { ENTITY_TYPE } from "@appsmith/entities/AppsmithConsole/utils"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { call, put } from "redux-saga/effects"; @@ -65,29 +64,3 @@ export function* addNewWidgetToDsl( return updatedWidgets; } - -/** - * - * Create default props for a new widget. - * Default values can be used here as some of these props are vestigial and are not required by Anvil. - */ -export function getCreateWidgetPayload( - widgetId: string, - type: string, - parentId: string, - data: Partial = {}, -): WidgetAddChild { - return { - columns: GridDefaults.DEFAULT_GRID_COLUMNS, - leftColumn: 0, - newWidgetId: widgetId, - parentColumnSpace: 1, - parentRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, - rows: 10, - tabId: "", - topRow: 0, - type: type, - widgetId: parentId, - ...data, - }; -} diff --git a/app/client/src/sagas/WidgetAdditionSagas.ts b/app/client/src/sagas/WidgetAdditionSagas.ts index 0c5cc07a5d22..ab3d91baf1a4 100644 --- a/app/client/src/sagas/WidgetAdditionSagas.ts +++ b/app/client/src/sagas/WidgetAdditionSagas.ts @@ -53,7 +53,6 @@ import { import { getPropertiesToUpdate } from "./WidgetOperationSagas"; import { getWidget, getWidgets } from "./selectors"; import { addBuildingBlockToCanvasSaga } from "./BuildingBlockSagas/BuildingBlockAdditionSagas"; -import { getCurrentlyOpenAnvilDetachedWidgets } from "layoutSystems/anvil/integrations/modalSelectors"; const WidgetTypes = WidgetFactory.widgetTypes; @@ -87,13 +86,8 @@ function* getChildWidgetProps( ]); const themeDefaultConfig = WidgetFactory.getWidgetStylesheetConfigMap(type) || {}; - const widgetSessionValues = getWidgetSessionValues(type, parent); const mainCanvasWidth: number = yield select(getCanvasWidth); const isMobile: boolean = yield select(getIsAutoLayoutMobileBreakPoint); - const detachedWidgets: string[] = yield select( - getCurrentlyOpenAnvilDetachedWidgets, - ); - const isModalOpen = detachedWidgets && detachedWidgets.length > 0; if (!widgetName) { const widgetNames = Object.keys(widgets).map((w) => widgets[w].widgetName); @@ -122,12 +116,6 @@ function* getChildWidgetProps( } } - // in case we are creating zone inside zone, we want to use the parent's column space, we want - // to make sure the elevateBackground is set to false - if (type === "ZONE_WIDGET" && isModalOpen) { - props = { ...props, elevatedBackground: false }; - } - const isAutoLayout = isStack(widgets, parent); const isFillWidget = restDefaultConfig?.responsiveBehavior === ResponsiveBehavior.Fill; @@ -142,7 +130,6 @@ function* getChildWidgetProps( widgetId: newWidgetId, renderMode: RenderModes.CANVAS, ...themeDefaultConfig, - ...widgetSessionValues, }; const { minWidth } = getWidgetMinMaxDimensionsInPixel( @@ -549,51 +536,3 @@ export default function* widgetAdditionSagas() { takeEvery(ReduxActionTypes.WIDGET_ADD_NEW_TAB_CHILD, addNewTabChildSaga), ]); } - -/** - * retrieves the values from session storage for the widget properties - * for hydration of the widget when we create widget on drop - */ -export function getWidgetSessionValues( - type: string, - parent: FlattenedWidgetProps, -) { - // For WDS_INLINE_BUTTONS_WIDGET, we want to hydation only to work when we add more items to the inline button group. - // So we don't want to hydrate the values when we drop the widget on the canvas. - if (["WDS_INLINE_BUTTONS_WIDGET"].includes(type)) return; - - let widgetType = type; - const configMap = WidgetFactory.widgetConfigMap.get(type); - - const widgetSessionValues: any = {}; - - // in case we are dropping WDS_ICON_BUTTON_WIDGET, we want to reuse the values of BUTTON_WIDGET - if (type === "WDS_ICON_BUTTON_WIDGET") { - widgetType = "WDS_BUTTON_WIDGET"; - } - - for (const key in configMap) { - if (configMap[key] != undefined) { - let sessionStorageKey = `${widgetType}.${key}`; - - if (type === "ZONE_WIDGET") { - sessionStorageKey = `${widgetType}.${parent.widgetId}.${key}`; - } - - let valueFromSession: any = sessionStorage.getItem(sessionStorageKey); - - // parse "true" as true and "false" as false - if (valueFromSession === "true") { - valueFromSession = true; - } else if (valueFromSession === "false") { - valueFromSession = false; - } - - if (valueFromSession !== undefined && valueFromSession !== null) { - widgetSessionValues[key] = valueFromSession; - } - } - } - - return widgetSessionValues; -} diff --git a/app/client/src/widgets/anvil/SectionWidget/widget/config/defaultConfig.ts b/app/client/src/widgets/anvil/SectionWidget/widget/config/defaultConfig.ts index 9456dfd23948..410e775e4008 100644 --- a/app/client/src/widgets/anvil/SectionWidget/widget/config/defaultConfig.ts +++ b/app/client/src/widgets/anvil/SectionWidget/widget/config/defaultConfig.ts @@ -9,14 +9,11 @@ import { LayoutSystemTypes } from "layoutSystems/types"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { getWidgetBluePrintUpdates } from "utils/WidgetBlueprintUtils"; import { sectionPreset } from "layoutSystems/anvil/layoutComponents/presets/sectionPreset"; -import { ButtonBoxShadowTypes } from "components/constants"; export const defaultConfig: WidgetDefaultProps = { elevatedBackground: false, - boxShadow: ButtonBoxShadowTypes.NONE, children: [], columns: 0, - detachFromLayout: false, responsiveBehavior: ResponsiveBehavior.Fill, rows: 0, version: 1, diff --git a/app/client/src/widgets/anvil/ZoneWidget/widget/config/defaultConfig.ts b/app/client/src/widgets/anvil/ZoneWidget/widget/config/defaultConfig.ts index d8aa8cf490c7..ed496a1128bd 100644 --- a/app/client/src/widgets/anvil/ZoneWidget/widget/config/defaultConfig.ts +++ b/app/client/src/widgets/anvil/ZoneWidget/widget/config/defaultConfig.ts @@ -5,10 +5,7 @@ import { } from "WidgetProvider/constants"; import { zonePreset } from "layoutSystems/anvil/layoutComponents/presets/zonePreset"; import type { LayoutProps } from "layoutSystems/anvil/utils/anvilTypes"; -import { - FlexVerticalAlignment, - ResponsiveBehavior, -} from "layoutSystems/common/utils/constants"; +import { ResponsiveBehavior } from "layoutSystems/common/utils/constants"; import { LayoutSystemTypes } from "layoutSystems/types"; import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer"; import { getWidgetBluePrintUpdates } from "utils/WidgetBlueprintUtils"; @@ -17,8 +14,6 @@ export const defaultConfig: WidgetDefaultProps = { elevatedBackground: true, children: [], columns: 0, - detachFromLayout: false, - flexVerticalAlignment: FlexVerticalAlignment.Stretch, responsiveBehavior: ResponsiveBehavior.Fill, rows: 0, version: 1, @@ -30,18 +25,36 @@ export const defaultConfig: WidgetDefaultProps = { fn: ( widget: FlattenedWidgetProps, widgets: CanvasWidgetsReduxState, - parent: FlattenedWidgetProps, - layoutSystemType: LayoutSystemTypes, + parent: FlattenedWidgetProps, // Why does this exist, when we have all the widgets? + layoutSystemType: LayoutSystemTypes, // All widgets are new in Anvil, however, it may be needed for Auto Layout ) => { if (layoutSystemType !== LayoutSystemTypes.ANVIL) return []; const layout: LayoutProps[] = zonePreset(); - return getWidgetBluePrintUpdates({ + const updates = getWidgetBluePrintUpdates({ [widget.widgetId]: { layout, }, }); + + // In a modal widget, the zones don't have borders and elevation + // by default. We go up the hierarchy to find any Modal Widget + // If it exists, we remove the elevated background for the zone + let parentId = widget.parentId; + while (parentId) { + if (widgets[parentId].type === "WDS_MODAL_WIDGET") { + updates.push({ + widgetId: widget.widgetId, + propertyName: "elevatedBackground", + propertyValue: false, + }); + break; + } + parentId = widgets[parentId].parentId; + } + + return updates; }, }, ], diff --git a/app/client/src/widgets/wds/WDSInputWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSInputWidget/config/defaultsConfig.ts index 14622efb1309..42436cf380b1 100644 --- a/app/client/src/widgets/wds/WDSInputWidget/config/defaultsConfig.ts +++ b/app/client/src/widgets/wds/WDSInputWidget/config/defaultsConfig.ts @@ -6,7 +6,7 @@ export const defaultsConfig = { labelPosition: "top", inputType: "TEXT", widgetName: "Input", - version: 2, + version: 1, label: "Label", showStepArrows: false, responsiveBehavior: ResponsiveBehavior.Fill, diff --git a/app/client/src/widgets/wds/WDSModalWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSModalWidget/config/defaultsConfig.ts index 77bf3cac8023..02175cbc413f 100644 --- a/app/client/src/widgets/wds/WDSModalWidget/config/defaultsConfig.ts +++ b/app/client/src/widgets/wds/WDSModalWidget/config/defaultsConfig.ts @@ -34,7 +34,6 @@ export const defaultsConfig = { layoutSystemType: LayoutSystemTypes, ) => { if (layoutSystemType !== LayoutSystemTypes.ANVIL) return []; - const layout: LayoutProps[] = modalPreset(); return getWidgetBluePrintUpdates({ [widget.widgetId]: { diff --git a/app/client/src/widgets/wds/WDSTableWidget/config/defaultsConfig.ts b/app/client/src/widgets/wds/WDSTableWidget/config/defaultsConfig.ts index 1467514b073b..44915803301a 100644 --- a/app/client/src/widgets/wds/WDSTableWidget/config/defaultsConfig.ts +++ b/app/client/src/widgets/wds/WDSTableWidget/config/defaultsConfig.ts @@ -36,7 +36,7 @@ export const defaultsConfig = { isVisiblePagination: true, isSortable: true, delimiter: ",", - version: 2, + version: 1, inlineEditingSaveOption: InlineEditingSaveOptions.ROW_LEVEL, pageSize: 8, buttonLabel: "Action",