feat: Non auto height invisible widgets (#20118)
## Description This PR adds another feature update we had planned for Auto Height - [ ] For new applications, in View and Preview mode, any widget which is invisible will let go of its space and collapse if it's either on the main Canvas or a container-like widget which has Auto-height enabled. - [ ] Widgets within a container-like Widget, say Tabs, that doesn't have Auto-height enabled, will now let go of their space if they're invisible. - [ ] The experience in Edit mode has not changed. TL;DR: In new applications, in the Preview and Published _AKA_ View modes, if a widget is invisible and within an Auto-height-enabled container like a Tab, a Modal, a Form, or the main Canvas, it will fully collapse, allowing widgets below it to move up and take its space. This changes the behavior today prior to the release of this PR for Auto-height-enabled widgets. Fixes #19983 Fixes #18681
This commit is contained in:
parent
2dec7eb7f4
commit
55cf16ae9d
282
app/client/cypress/fixtures/autoHeightInvisibleWidgetsDSL.json
Normal file
282
app/client/cypress/fixtures/autoHeightInvisibleWidgetsDSL.json
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
{"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 4896,
|
||||
"snapColumns": 64,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 590,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 125,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"version": 76,
|
||||
"minHeight": 1292,
|
||||
"dynamicTriggerPathList": [],
|
||||
"parentColumnSpace": 1,
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"resetFormOnClick": false,
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Button1",
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"displayName": "Button",
|
||||
"iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg",
|
||||
"searchTags": [
|
||||
"click",
|
||||
"submit"
|
||||
],
|
||||
"topRow": 1,
|
||||
"bottomRow": 24,
|
||||
"parentRowSpace": 10,
|
||||
"type": "BUTTON_WIDGET",
|
||||
"hideCard": false,
|
||||
"topRowBeforeCollapse": 1,
|
||||
"animateLoading": true,
|
||||
"parentColumnSpace": 16.234375,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 18,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"text": "Submit",
|
||||
"isDisabled": false,
|
||||
"key": "wgi0jkm894",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 34,
|
||||
"isDefaultClickDisabled": true,
|
||||
"widgetId": "h2kgzv66ca",
|
||||
"bottomRowBeforeCollapse": 24,
|
||||
"isVisible": false,
|
||||
"recaptchaType": "V3",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 1,
|
||||
"disabledWhenInvalid": false,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"originalBottomRow": 24,
|
||||
"buttonVariant": "PRIMARY",
|
||||
"placement": "CENTER"
|
||||
},
|
||||
{
|
||||
"widgetName": "Divider1",
|
||||
"thickness": 2,
|
||||
"displayName": "Divider",
|
||||
"iconSVG": "/static/media/icon.cbe8f608ca868e1eb44607e5fbd4a9e5.svg",
|
||||
"searchTags": [
|
||||
"line"
|
||||
],
|
||||
"topRow": 24,
|
||||
"bottomRow": 28,
|
||||
"parentRowSpace": 10,
|
||||
"type": "DIVIDER_WIDGET",
|
||||
"capType": "nc",
|
||||
"hideCard": false,
|
||||
"animateLoading": true,
|
||||
"parentColumnSpace": 16.234375,
|
||||
"leftColumn": 16,
|
||||
"dynamicBindingPathList": [],
|
||||
"key": "tzfnvdc0dc",
|
||||
"dividerColor": "#858282",
|
||||
"orientation": "horizontal",
|
||||
"strokeStyle": "solid",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 36,
|
||||
"widgetId": "cq1cekp1z2",
|
||||
"capSide": 0,
|
||||
"isVisible": true,
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 24,
|
||||
"originalBottomRow": 28
|
||||
},
|
||||
{
|
||||
"boxShadow": "{{appsmith.theme.boxShadow.appBoxShadow}}",
|
||||
"widgetName": "Container1",
|
||||
"borderColor": "#E0DEDE",
|
||||
"isCanvas": true,
|
||||
"displayName": "Container",
|
||||
"iconSVG": "/static/media/icon.1977dca3370505e2db3a8e44cfd54907.svg",
|
||||
"searchTags": [
|
||||
"div",
|
||||
"parent",
|
||||
"group"
|
||||
],
|
||||
"topRow": 36,
|
||||
"bottomRow": 51,
|
||||
"parentRowSpace": 10,
|
||||
"type": "CONTAINER_WIDGET",
|
||||
"hideCard": false,
|
||||
"shouldScrollContents": true,
|
||||
"animateLoading": true,
|
||||
"parentColumnSpace": 16.234375,
|
||||
"leftColumn": 15,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
},
|
||||
{
|
||||
"key": "boxShadow"
|
||||
}
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas1",
|
||||
"displayName": "Canvas",
|
||||
"topRow": 0,
|
||||
"bottomRow": 150,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": false,
|
||||
"hideCard": true,
|
||||
"minHeight": 150,
|
||||
"parentColumnSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"dynamicBindingPathList": [],
|
||||
"children": [
|
||||
{
|
||||
"boxShadow": "none",
|
||||
"widgetName": "FilePicker1",
|
||||
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"displayName": "FilePicker",
|
||||
"iconSVG": "/static/media/icon.7c5ad9c357928c6ff5701bf51a56c2e5.svg",
|
||||
"searchTags": [
|
||||
"upload"
|
||||
],
|
||||
"topRow": 0,
|
||||
"bottomRow": 9,
|
||||
"parentRowSpace": 10,
|
||||
"allowedFileTypes": [],
|
||||
"type": "FILE_PICKER_WIDGET_V2",
|
||||
"hideCard": false,
|
||||
"topRowBeforeCollapse": 0,
|
||||
"animateLoading": true,
|
||||
"parentColumnSpace": 5.775390625,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 12,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "buttonColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"isDisabled": false,
|
||||
"key": "5w3ckl5gj6",
|
||||
"isRequired": false,
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 47,
|
||||
"isDefaultClickDisabled": true,
|
||||
"widgetId": "dsp6wm4sk3",
|
||||
"bottomRowBeforeCollapse": 9,
|
||||
"isVisible": false,
|
||||
"label": "Select Files",
|
||||
"maxFileSize": 5,
|
||||
"dynamicTyping": true,
|
||||
"version": 1,
|
||||
"fileDataType": "Base64",
|
||||
"parentId": "ay573tnz48",
|
||||
"selectedFiles": [],
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 0,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"originalBottomRow": 0,
|
||||
"files": [],
|
||||
"maxNumFiles": 1
|
||||
},
|
||||
{
|
||||
"widgetName": "Checkbox1",
|
||||
"displayName": "Checkbox",
|
||||
"iconSVG": "/static/media/icon.aaab032b43383e4fa53ffc0ef40c90ef.svg",
|
||||
"searchTags": [
|
||||
"boolean"
|
||||
],
|
||||
"topRow": 9,
|
||||
"bottomRow": 13,
|
||||
"parentRowSpace": 10,
|
||||
"type": "CHECKBOX_WIDGET",
|
||||
"alignWidget": "LEFT",
|
||||
"hideCard": false,
|
||||
"animateLoading": true,
|
||||
"parentColumnSpace": 5.775390625,
|
||||
"leftColumn": 12,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "accentColor"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"labelPosition": "Left",
|
||||
"isDisabled": false,
|
||||
"key": "5jfy6xde7m",
|
||||
"isRequired": false,
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 47,
|
||||
"dynamicHeight": "AUTO_HEIGHT",
|
||||
"widgetId": "3271vh5f32",
|
||||
"accentColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"isVisible": true,
|
||||
"label": "Label",
|
||||
"version": 1,
|
||||
"parentId": "ay573tnz48",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 4,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"defaultCheckedState": true,
|
||||
"maxDynamicHeight": 9000,
|
||||
"originalBottomRow": 8,
|
||||
"minDynamicHeight": 4
|
||||
}
|
||||
],
|
||||
"key": "dkvxavs6qg",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 389.625,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "ay573tnz48",
|
||||
"containerStyle": "none",
|
||||
"isVisible": true,
|
||||
"version": 1,
|
||||
"parentId": "ps28fk6mg0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false
|
||||
}
|
||||
],
|
||||
"borderWidth": "1",
|
||||
"key": "d72uz4r1l6",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 39,
|
||||
"dynamicHeight": "AUTO_HEIGHT",
|
||||
"widgetId": "ps28fk6mg0",
|
||||
"containerStyle": "card",
|
||||
"isVisible": true,
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 36,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"maxDynamicHeight": 9000,
|
||||
"originalBottomRow": 55,
|
||||
"minDynamicHeight": 10
|
||||
}
|
||||
]
|
||||
}}
|
||||
189
app/client/cypress/fixtures/autoHeightOverlapDSL.json
Normal file
189
app/client/cypress/fixtures/autoHeightOverlapDSL.json
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
{
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 4896,
|
||||
"snapColumns": 64,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 380,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 125,
|
||||
"parentRowSpace": 1,
|
||||
"propertyValue": 1292,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"propertyPath": "bottomRow",
|
||||
"version": 76,
|
||||
"minHeight": 1292,
|
||||
"dynamicTriggerPathList": [],
|
||||
"parentColumnSpace": 1,
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Text1",
|
||||
"displayName": "Text",
|
||||
"iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg",
|
||||
"searchTags": [
|
||||
"typography",
|
||||
"paragraph",
|
||||
"label"
|
||||
],
|
||||
"topRow": 5,
|
||||
"bottomRow": 9,
|
||||
"parentRowSpace": 10,
|
||||
"type": "TEXT_WIDGET",
|
||||
"hideCard": false,
|
||||
"animateLoading": true,
|
||||
"overflow": "NONE",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"parentColumnSpace": 27.5625,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 11,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "truncateButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"shouldTruncate": false,
|
||||
"truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"text": "something",
|
||||
"key": "ryg1uo07f7",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 27,
|
||||
"textAlign": "LEFT",
|
||||
"dynamicHeight": "AUTO_HEIGHT",
|
||||
"widgetId": "m4doxmviiu",
|
||||
"isVisible": false,
|
||||
"fontStyle": "BOLD",
|
||||
"textColor": "#231F20",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 5,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"maxDynamicHeight": 9000,
|
||||
"originalBottomRow": 9,
|
||||
"fontSize": "1rem",
|
||||
"minDynamicHeight": 4
|
||||
},
|
||||
{
|
||||
"widgetName": "Text2",
|
||||
"displayName": "Text",
|
||||
"iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg",
|
||||
"searchTags": [
|
||||
"typography",
|
||||
"paragraph",
|
||||
"label"
|
||||
],
|
||||
"topRow": 13,
|
||||
"bottomRow": 17,
|
||||
"parentRowSpace": 10,
|
||||
"type": "TEXT_WIDGET",
|
||||
"hideCard": false,
|
||||
"animateLoading": true,
|
||||
"overflow": "NONE",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"parentColumnSpace": 27.5625,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 11,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "truncateButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"shouldTruncate": false,
|
||||
"truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"text": "anything",
|
||||
"key": "ryg1uo07f7",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 27,
|
||||
"textAlign": "LEFT",
|
||||
"dynamicHeight": "AUTO_HEIGHT",
|
||||
"widgetId": "ryq5qy60cg",
|
||||
"isVisible": false,
|
||||
"fontStyle": "BOLD",
|
||||
"textColor": "#231F20",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 9,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"maxDynamicHeight": 9000,
|
||||
"originalBottomRow": 13,
|
||||
"fontSize": "1rem",
|
||||
"minDynamicHeight": 4
|
||||
},
|
||||
{
|
||||
"widgetName": "Text3",
|
||||
"displayName": "Text",
|
||||
"iconSVG": "/static/media/icon.97c59b523e6f70ba6f40a10fc2c7c5b5.svg",
|
||||
"searchTags": [
|
||||
"typography",
|
||||
"paragraph",
|
||||
"label"
|
||||
],
|
||||
"topRow": 9,
|
||||
"bottomRow": 13,
|
||||
"parentRowSpace": 10,
|
||||
"type": "TEXT_WIDGET",
|
||||
"hideCard": false,
|
||||
"animateLoading": true,
|
||||
"overflow": "NONE",
|
||||
"fontFamily": "{{appsmith.theme.fontFamily.appFont}}",
|
||||
"parentColumnSpace": 27.5625,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 11,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "truncateButtonColor"
|
||||
},
|
||||
{
|
||||
"key": "fontFamily"
|
||||
},
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"shouldTruncate": false,
|
||||
"truncateButtonColor": "{{appsmith.theme.colors.primaryColor}}",
|
||||
"text": "another",
|
||||
"key": "ryg1uo07f7",
|
||||
"isDeprecated": false,
|
||||
"rightColumn": 27,
|
||||
"textAlign": "LEFT",
|
||||
"dynamicHeight": "AUTO_HEIGHT",
|
||||
"widgetId": "kx7mvoopqu",
|
||||
"isVisible": false,
|
||||
"fontStyle": "BOLD",
|
||||
"textColor": "#231F20",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"originalTopRow": 13,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
|
||||
"maxDynamicHeight": 9000,
|
||||
"originalBottomRow": 17,
|
||||
"fontSize": "1rem",
|
||||
"minDynamicHeight": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
|
||||
|
||||
const { AggregateHelper, CommonLocators, DeployMode } = ObjectsRegistry;
|
||||
|
||||
describe("Fixed Invisible widgets and auto height containers", () => {
|
||||
before(() => {
|
||||
// Create a page with a divider below a button widget and a checkbox widget below a filepicker widget
|
||||
// Button widget and filepicker widgets are fixed height widgets
|
||||
cy.fixture("autoHeightInvisibleWidgetsDSL").then((val: any) => {
|
||||
AggregateHelper.AddDsl(val);
|
||||
});
|
||||
});
|
||||
|
||||
it("1. Divider should be below Button Widget in edit mode", () => {
|
||||
// This test checks for the height of the button widget and the filepicker widget
|
||||
// As well as the top value for the widgets below button and filepicker (divider and checkbox respectively)
|
||||
cy.get(CommonLocators._widgetInDeployed("buttonwidget")).should(
|
||||
"have.css",
|
||||
"height",
|
||||
"230px",
|
||||
);
|
||||
cy.get(CommonLocators._widgetInDeployed("filepickerwidgetv2")).should(
|
||||
"have.css",
|
||||
"height",
|
||||
"90px",
|
||||
);
|
||||
|
||||
cy.get(CommonLocators._widgetInDeployed("dividerwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"246px",
|
||||
);
|
||||
cy.get(CommonLocators._widgetInDeployed("checkboxwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"96px",
|
||||
);
|
||||
});
|
||||
|
||||
it("2. Divider should move up by the height of the button widget in preview mode", () => {
|
||||
// This tests if the divider and checkbox widget move up by an appropriate amount in preview mode.
|
||||
AggregateHelper.AssertElementVisible(
|
||||
CommonLocators._previewModeToggle("edit"),
|
||||
);
|
||||
AggregateHelper.GetNClick(CommonLocators._previewModeToggle("edit"));
|
||||
|
||||
cy.get(CommonLocators._widgetInDeployed("dividerwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"16px",
|
||||
);
|
||||
cy.get(CommonLocators._widgetInDeployed("checkboxwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"6px",
|
||||
);
|
||||
});
|
||||
|
||||
it("3. Divider should move up by the height of the button widget in view mode", () => {
|
||||
// This tests if the divider and checkbox widget move up by an appropriate amount in view mode.
|
||||
DeployMode.DeployApp();
|
||||
cy.get(CommonLocators._widgetInDeployed("dividerwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"16px",
|
||||
);
|
||||
cy.get(CommonLocators._widgetInDeployed("checkboxwidget")).should(
|
||||
"have.css",
|
||||
"top",
|
||||
"6px",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
|
||||
|
||||
const { AggregateHelper, CommonLocators, DeployMode } = ObjectsRegistry;
|
||||
|
||||
describe("Fixed Invisible widgets and auto height containers", () => {
|
||||
before(() => {
|
||||
// Create a page with a divider below a button widget and a checkbox widget below a filepicker widget
|
||||
// Button widget and filepicker widgets are fixed height widgets
|
||||
cy.fixture("autoHeightOverlapDSL").then((val: any) => {
|
||||
AggregateHelper.AddDsl(val);
|
||||
});
|
||||
});
|
||||
|
||||
it("1. Invisible widgets should not overlap when returning from preview mode to edit mode", () => {
|
||||
cy.get(CommonLocators._widgetInDeployed("textwidget"));
|
||||
AggregateHelper.AssertContains("anything", "exist", "#ryq5qy60cg");
|
||||
|
||||
AggregateHelper.AssertElementVisible(
|
||||
CommonLocators._previewModeToggle("edit"),
|
||||
);
|
||||
AggregateHelper.GetNClick(CommonLocators._previewModeToggle("edit"));
|
||||
|
||||
AggregateHelper.AssertElementVisible(
|
||||
CommonLocators._previewModeToggle("preview"),
|
||||
);
|
||||
AggregateHelper.GetNClick(CommonLocators._previewModeToggle("preview"));
|
||||
|
||||
cy.get("#ryq5qy60cg").should("have.css", "top", "136px");
|
||||
cy.get("#kx7mvoopqu").should("have.css", "top", "96px");
|
||||
cy.get("#m4doxmviiu").should("have.css", "top", "56px");
|
||||
});
|
||||
});
|
||||
|
|
@ -79,10 +79,11 @@ export const setWidgetDynamicProperty = (
|
|||
|
||||
export const updateMultipleWidgetPropertiesAction = (
|
||||
widgetsToUpdate: UpdateWidgetsPayload,
|
||||
shouldEval = false,
|
||||
) => {
|
||||
return {
|
||||
type: ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES,
|
||||
payload: widgetsToUpdate,
|
||||
payload: { widgetsToUpdate, shouldEval },
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1062,6 +1062,7 @@ export interface ApplicationPayload {
|
|||
isAutoUpdate?: boolean;
|
||||
isManualUpdate?: boolean;
|
||||
embedSetting?: AppEmbedSetting;
|
||||
collapseInvisibleWidgets?: boolean;
|
||||
}
|
||||
|
||||
export type WorkspaceDetails = {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,9 @@ export const handlers = {
|
|||
action: ReduxAction<{ applicationList: ApplicationPayload[] }>,
|
||||
) => ({
|
||||
...state,
|
||||
currentApplication: action.payload,
|
||||
currentApplication: {
|
||||
...action.payload,
|
||||
},
|
||||
isFetchingApplication: false,
|
||||
}),
|
||||
[ReduxActionTypes.CURRENT_APPLICATION_NAME_UPDATE]: (
|
||||
|
|
|
|||
|
|
@ -142,6 +142,8 @@ export const WIDGET_STATIC_PROPS = {
|
|||
detachFromLayout: true,
|
||||
noContainerOffset: false,
|
||||
height: false,
|
||||
topRowBeforeCollapse: false,
|
||||
bottomRowBeforeCollapse: false,
|
||||
};
|
||||
|
||||
export const WIDGET_DSL_STRUCTURE_PROPS = {
|
||||
|
|
@ -181,4 +183,6 @@ export const WIDGET_PROPS_TO_SKIP_FROM_EVAL = {
|
|||
iconSVG: true,
|
||||
version: true,
|
||||
displayName: true,
|
||||
topRowBeforeCollapse: false,
|
||||
bottomRowBeforeCollapse: false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ const autoHeightLayoutTreeReducer = createImmerReducer(initialState, {
|
|||
action: ReduxAction<AutoHeightLayoutTreePayload>,
|
||||
) => {
|
||||
const { tree } = action.payload;
|
||||
const diff = xor(Object.keys(state), [...Object.keys(tree)]);
|
||||
for (const widgetId in diff) {
|
||||
const diff: string[] = xor(Object.keys(state), [...Object.keys(tree)]);
|
||||
for (const widgetId of diff) {
|
||||
delete state[widgetId];
|
||||
}
|
||||
for (const widgetId in tree) {
|
||||
|
|
|
|||
|
|
@ -19,12 +19,15 @@ describe("Canvas Widgets Reducer", () => {
|
|||
};
|
||||
const type = ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES;
|
||||
const payload = {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "apple",
|
||||
},
|
||||
],
|
||||
widgetsToUpdate: {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "apple",
|
||||
},
|
||||
],
|
||||
},
|
||||
shouldEval: false,
|
||||
};
|
||||
const expected = {
|
||||
"0": { children: ["xyz123"] },
|
||||
|
|
@ -53,12 +56,15 @@ describe("Canvas Widgets Reducer", () => {
|
|||
};
|
||||
const type = ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES;
|
||||
const payload = {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.games.ball",
|
||||
propertyValue: ["football"],
|
||||
},
|
||||
],
|
||||
widgetsToUpdate: {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.games.ball",
|
||||
propertyValue: ["football"],
|
||||
},
|
||||
],
|
||||
},
|
||||
shouldEval: false,
|
||||
};
|
||||
const expected = {
|
||||
"0": { children: ["xyz123"] },
|
||||
|
|
@ -90,12 +96,15 @@ describe("Canvas Widgets Reducer", () => {
|
|||
};
|
||||
const type = ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES;
|
||||
const payload = {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "orange",
|
||||
},
|
||||
],
|
||||
widgetsToUpdate: {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "orange",
|
||||
},
|
||||
],
|
||||
},
|
||||
shouldEval: false,
|
||||
};
|
||||
|
||||
// Reference equality check using toBe
|
||||
|
|
@ -118,12 +127,15 @@ describe("Canvas Widgets Reducer", () => {
|
|||
};
|
||||
const type = ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES;
|
||||
const payload = {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "orange",
|
||||
},
|
||||
],
|
||||
widgetsToUpdate: {
|
||||
xyz123: [
|
||||
{
|
||||
propertyPath: "someValue.apple",
|
||||
propertyValue: "orange",
|
||||
},
|
||||
],
|
||||
},
|
||||
shouldEval: true,
|
||||
};
|
||||
|
||||
const result = reducer(initialState, { type, payload }).xyz123.someValue
|
||||
|
|
|
|||
|
|
@ -104,11 +104,14 @@ const canvasWidgetsReducer = createImmerReducer(initialState, {
|
|||
},
|
||||
[ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES]: (
|
||||
state: CanvasWidgetsReduxState,
|
||||
action: ReduxAction<UpdateWidgetsPayload>,
|
||||
action: ReduxAction<{
|
||||
widgetsToUpdate: UpdateWidgetsPayload;
|
||||
shouldEval: boolean;
|
||||
}>,
|
||||
) => {
|
||||
// For each widget whose properties we would like to update
|
||||
for (const [widgetId, propertyPathsToUpdate] of Object.entries(
|
||||
action.payload,
|
||||
action.payload.widgetsToUpdate,
|
||||
)) {
|
||||
// Iterate through each property to update in `widgetId`
|
||||
propertyPathsToUpdate.forEach(({ propertyPath, propertyValue }) => {
|
||||
|
|
@ -125,7 +128,10 @@ const canvasWidgetsReducer = createImmerReducer(initialState, {
|
|||
const canvasWidgetHeightsToUpdate: Record<
|
||||
string,
|
||||
number
|
||||
> = getCanvasWidgetHeightsToUpdate(Object.keys(action.payload), state);
|
||||
> = getCanvasWidgetHeightsToUpdate(
|
||||
Object.keys(action.payload.widgetsToUpdate),
|
||||
state,
|
||||
);
|
||||
for (const widgetId in canvasWidgetHeightsToUpdate) {
|
||||
state[widgetId].bottomRow = canvasWidgetHeightsToUpdate[widgetId];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ import {
|
|||
getCurrentPageId,
|
||||
getCurrentPageName,
|
||||
getPageById,
|
||||
previewModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import {
|
||||
executePageLoadActions,
|
||||
|
|
@ -571,6 +572,7 @@ export function* saveLayoutSaga(action: ReduxAction<{ isRetry?: boolean }>) {
|
|||
try {
|
||||
const currentPageId: string = yield select(getCurrentPageId);
|
||||
const currentPage: Page = yield select(getPageById(currentPageId));
|
||||
const isPreviewMode: boolean = yield select(previewModeSelector);
|
||||
|
||||
const appMode: APP_MODE | undefined = yield select(getAppMode);
|
||||
|
||||
|
|
@ -585,7 +587,7 @@ export function* saveLayoutSaga(action: ReduxAction<{ isRetry?: boolean }>) {
|
|||
});
|
||||
}
|
||||
|
||||
if (appMode === APP_MODE.EDIT) {
|
||||
if (appMode === APP_MODE.EDIT && !isPreviewMode) {
|
||||
yield put(saveLayout(action.payload.isRetry));
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
batchUpdateWidgetProperty,
|
||||
DeleteWidgetPropertyPayload,
|
||||
SetWidgetDynamicPropertyPayload,
|
||||
updateMultipleWidgetPropertiesAction,
|
||||
UpdateWidgetPropertyPayload,
|
||||
UpdateWidgetPropertyRequestPayload,
|
||||
} from "actions/controlActions";
|
||||
|
|
@ -140,7 +141,6 @@ import { flashElementsById } from "utils/helpers";
|
|||
import { getSlidingArenaName } from "constants/componentClassNameConstants";
|
||||
import { builderURL } from "RouteBuilder";
|
||||
import history from "utils/history";
|
||||
import { updateMultipleWidgetProperties } from "actions/widgetActions";
|
||||
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
|
||||
import { traverseTreeAndExecuteBlueprintChildOperations } from "./WidgetBlueprintSagas";
|
||||
import { MetaState } from "reducers/entityReducers/metaReducer";
|
||||
|
|
@ -798,7 +798,7 @@ function* updateCanvasSize(
|
|||
// Check this out when non canvas widgets are updating snapRows
|
||||
// erstwhile: Math.round((rows * props.snapRowSpace) / props.parentRowSpace),
|
||||
yield put(
|
||||
updateMultipleWidgetProperties({
|
||||
updateMultipleWidgetPropertiesAction({
|
||||
[canvasWidgetId]: [
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ import {
|
|||
ReduxActionTypes,
|
||||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
import { UpdateWidgetAutoHeightPayload } from "actions/autoHeightActions";
|
||||
import { updateAndSaveLayout } from "actions/pageActions";
|
||||
import log from "loglevel";
|
||||
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { put, select } from "redux-saga/effects";
|
||||
import { getWidgets } from "sagas/selectors";
|
||||
import { getIsDraggingOrResizing } from "selectors/widgetSelectors";
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
|
|
@ -39,3 +42,20 @@ export function* batchCallsToUpdateWidgetAutoHeightSaga(
|
|||
type: ReduxActionTypes.PROCESS_AUTO_HEIGHT_UPDATES,
|
||||
});
|
||||
}
|
||||
|
||||
// In this saga, we simply call the UPDATE_LAYOUT, with shouldReplay: false
|
||||
// This makes sure that we call eval, but we don't add the updates to the replay stack
|
||||
export function* callEvalWithoutReplay(
|
||||
action: ReduxAction<{ widgetsToUpdate: any; shouldEval: boolean }>,
|
||||
) {
|
||||
if (action.payload.shouldEval) {
|
||||
const widgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
||||
yield put(
|
||||
updateAndSaveLayout(widgets, {
|
||||
shouldReplay: false,
|
||||
isRetry: false,
|
||||
updatedWidgetIds: [],
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ import {
|
|||
} from "@appsmith/constants/ReduxActionConstants";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import log from "loglevel";
|
||||
import { AutoHeightLayoutTreeReduxState } from "reducers/entityReducers/autoHeightReducers/autoHeightLayoutTreeReducer";
|
||||
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { call, put, select } from "redux-saga/effects";
|
||||
import { getMinHeightBasedOnChildren, shouldWidgetsCollapse } from "./helpers";
|
||||
import { getWidgets } from "sagas/selectors";
|
||||
import { getCanvasHeightOffset } from "utils/WidgetSizeUtils";
|
||||
import { getAutoHeightLayoutTree } from "selectors/autoHeightSelectors";
|
||||
import { FlattenedWidgetProps } from "widgets/constants";
|
||||
import {
|
||||
getWidgetMaxAutoHeight,
|
||||
|
|
@ -20,6 +18,7 @@ import {
|
|||
import { getChildOfContainerLikeWidget } from "./helpers";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import { getLayoutTree } from "./layoutTree";
|
||||
|
||||
export function* dynamicallyUpdateContainersSaga(
|
||||
action?: ReduxAction<{ resettingTabs: boolean }>,
|
||||
|
|
@ -37,9 +36,7 @@ export function* dynamicallyUpdateContainersSaga(
|
|||
return isCanvasWidget;
|
||||
});
|
||||
|
||||
const dynamicHeightLayoutTree: AutoHeightLayoutTreeReduxState = yield select(
|
||||
getAutoHeightLayoutTree,
|
||||
);
|
||||
const { tree: dynamicHeightLayoutTree } = yield getLayoutTree(false);
|
||||
|
||||
const updates: Record<string, number> = {};
|
||||
const shouldCollapse: boolean = yield call(shouldWidgetsCollapse);
|
||||
|
|
@ -120,6 +117,17 @@ export function* dynamicallyUpdateContainersSaga(
|
|||
|
||||
// Get the larger value between the minDynamicHeightInRows and bottomMostRowForChild
|
||||
maxBottomRow = Math.max(maxBottomRowBasedOnChildren, maxBottomRow);
|
||||
} else {
|
||||
// If the parent is not supposed to be collapsed
|
||||
// Use the canvasHeight offset, as that would be the
|
||||
// minimum
|
||||
if (
|
||||
parentContainerWidget.bottomRow - parentContainerWidget.topRow >
|
||||
0 ||
|
||||
!shouldCollapse
|
||||
) {
|
||||
maxBottomRow += canvasHeightOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// The following makes sure we stay within bounds
|
||||
|
|
@ -156,7 +164,7 @@ export function* dynamicallyUpdateContainersSaga(
|
|||
}
|
||||
}
|
||||
|
||||
log.debug("Dynamic Height: Container Updates", { updates });
|
||||
log.debug("Auto Height: Container Updates", { updates });
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
// TODO(abhinav): Make sure there are no race conditions or scenarios where these updates are not considered.
|
||||
|
|
@ -171,7 +179,7 @@ export function* dynamicallyUpdateContainersSaga(
|
|||
}
|
||||
}
|
||||
log.debug(
|
||||
"Dynamic height: Container computations time taken:",
|
||||
"Auto height: Container computations time taken:",
|
||||
performance.now() - start,
|
||||
"ms",
|
||||
);
|
||||
|
|
|
|||
78
app/client/src/sagas/autoHeightSagas/helpers.test.ts
Normal file
78
app/client/src/sagas/autoHeightSagas/helpers.test.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { mutation_setPropertiesToUpdate } from "./helpers";
|
||||
|
||||
describe("auto height saga helpers", () => {
|
||||
it("When property exists, it should update correctly", () => {
|
||||
const propertiesToUpdate = {
|
||||
x: 50,
|
||||
y: "newValue",
|
||||
};
|
||||
const originalObject = {
|
||||
key1: [
|
||||
{
|
||||
propertyPath: "z",
|
||||
propertyValue: 20,
|
||||
},
|
||||
],
|
||||
};
|
||||
const expectedResult = {
|
||||
key1: [
|
||||
{
|
||||
propertyPath: "z",
|
||||
propertyValue: 20,
|
||||
},
|
||||
{
|
||||
propertyPath: "x",
|
||||
propertyValue: 50,
|
||||
},
|
||||
{
|
||||
propertyPath: "y",
|
||||
propertyValue: "newValue",
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = mutation_setPropertiesToUpdate(
|
||||
originalObject,
|
||||
"key1",
|
||||
propertiesToUpdate,
|
||||
);
|
||||
expect(result).toStrictEqual(expectedResult);
|
||||
});
|
||||
it("When property does not exist, it should update correctly", () => {
|
||||
const propertiesToUpdate = {
|
||||
x: 50,
|
||||
y: "newValue",
|
||||
};
|
||||
const originalObject = {
|
||||
key1: [
|
||||
{
|
||||
propertyPath: "z",
|
||||
propertyValue: 20,
|
||||
},
|
||||
],
|
||||
};
|
||||
const expectedResult = {
|
||||
key1: [
|
||||
{
|
||||
propertyPath: "z",
|
||||
propertyValue: 20,
|
||||
},
|
||||
],
|
||||
key2: [
|
||||
{
|
||||
propertyPath: "x",
|
||||
propertyValue: 50,
|
||||
},
|
||||
{
|
||||
propertyPath: "y",
|
||||
propertyValue: "newValue",
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = mutation_setPropertiesToUpdate(
|
||||
originalObject,
|
||||
"key2",
|
||||
propertiesToUpdate,
|
||||
);
|
||||
expect(result).toStrictEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { AppState } from "@appsmith/reducers";
|
||||
import {
|
||||
GridDefaults,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
|
|
@ -12,7 +13,10 @@ import { select } from "redux-saga/effects";
|
|||
import { getWidgetMetaProps, getWidgets } from "sagas/selectors";
|
||||
import { previewModeSelector } from "selectors/editorSelectors";
|
||||
import { getAppMode } from "selectors/entitiesSelector";
|
||||
import { isAutoHeightEnabledForWidget } from "widgets/WidgetUtils";
|
||||
import { getCanvasHeightOffset } from "utils/WidgetSizeUtils";
|
||||
import { DataTree, DataTreeWidget } from "entities/DataTree/dataTreeFactory";
|
||||
import { getDataTree } from "selectors/dataTreeSelectors";
|
||||
|
||||
export function* shouldWidgetsCollapse() {
|
||||
const isPreviewMode: boolean = yield select(previewModeSelector);
|
||||
|
|
@ -21,6 +25,14 @@ export function* shouldWidgetsCollapse() {
|
|||
return isPreviewMode || appMode === APP_MODE.PUBLISHED;
|
||||
}
|
||||
|
||||
export function* shouldAllInvisibleWidgetsInAutoHeightContainersCollapse() {
|
||||
const flag: boolean = yield select((state: AppState) => {
|
||||
return !!state.ui.applications.currentApplication?.collapseInvisibleWidgets;
|
||||
});
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
export function* getChildOfContainerLikeWidget(
|
||||
containerLikeWidget: FlattenedWidgetProps,
|
||||
) {
|
||||
|
|
@ -84,6 +96,9 @@ export function* getMinHeightBasedOnChildren(
|
|||
const shouldCollapse: boolean = yield shouldWidgetsCollapse();
|
||||
// Get all widgets in the DSL
|
||||
const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
||||
// Skip this whole process if the parent is collapsed: Process:
|
||||
// Get the DataTree
|
||||
const dataTree: DataTree = yield select(getDataTree);
|
||||
|
||||
const { children = [], parentId } = stateWidgets[widgetId];
|
||||
// If we need to consider the parent height
|
||||
|
|
@ -140,6 +155,23 @@ export function* getMinHeightBasedOnChildren(
|
|||
// detachFromLayout helps us identify such widgets
|
||||
if (detachFromLayout) continue;
|
||||
|
||||
// Seems like sometimes, the children comes in as a string instead of string array.
|
||||
// I'm not completely sure why that is, or which widgets use "children" properties as strings
|
||||
// So, we're skipping computations for the children if such a thing happens.
|
||||
if (tree[childWidgetId] === undefined) continue;
|
||||
|
||||
// Get this parentContainerWidget from the DataTree
|
||||
const dataTreeWidget = dataTree[stateWidgets[childWidgetId].widgetName];
|
||||
// If the widget exists, is not visible and we can collapse widgets
|
||||
|
||||
if (
|
||||
dataTreeWidget &&
|
||||
(dataTreeWidget as DataTreeWidget).isVisible !== true &&
|
||||
shouldCollapse
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the child widget's dimenstions from the tree
|
||||
const { bottomRow, topRow } = tree[childWidgetId];
|
||||
|
||||
|
|
@ -169,3 +201,91 @@ export function* getMinHeightBasedOnChildren(
|
|||
|
||||
return minHeightInRows;
|
||||
}
|
||||
/**
|
||||
* This function takes a widgetId and computes whether it can have zero height
|
||||
* Widget can have zero height if it has auto height enabled
|
||||
*
|
||||
*
|
||||
* Or if it is a child of a widget which has auto height enabled
|
||||
* (This is verified using shouldAllInvisibleWidgetsInAutoHeightContainersCollapse)
|
||||
*
|
||||
* @param stateWidgets The canvas widgets redux state needed for computations
|
||||
* @param widgetId The widget which is trying to collapse
|
||||
* @returns true if this widget can be collapsed to zero height
|
||||
*/
|
||||
export function* shouldCollapseThisWidget(
|
||||
stateWidgets: CanvasWidgetsReduxState,
|
||||
widgetId: string,
|
||||
) {
|
||||
const shouldCollapse: boolean = yield shouldWidgetsCollapse();
|
||||
const canCollapseAllWidgets: boolean = yield shouldAllInvisibleWidgetsInAutoHeightContainersCollapse();
|
||||
const widget = stateWidgets[widgetId];
|
||||
|
||||
// If we're in preview or view mode
|
||||
if (shouldCollapse) {
|
||||
// If this widget has auto height enabled
|
||||
if (isAutoHeightEnabledForWidget(widget)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the parent Canvas widgetId
|
||||
const parentId = widget.parentId;
|
||||
|
||||
if (parentId === MAIN_CONTAINER_WIDGET_ID && canCollapseAllWidgets) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the grandparent or the parent container like widget
|
||||
const parentContainerLikeWidgetId = parentId
|
||||
? stateWidgets[parentId].parentId
|
||||
: false;
|
||||
|
||||
// If the parent container like widget exists
|
||||
if (parentContainerLikeWidgetId) {
|
||||
const parentContainerLikeWidget =
|
||||
stateWidgets[parentContainerLikeWidgetId];
|
||||
// If we can collapse widgets within all auto height container like widgets
|
||||
// and if the parent container like widget exists
|
||||
// and if auto height is enabled for the parent container
|
||||
// or if the parent is the main container
|
||||
if (
|
||||
parentContainerLikeWidget &&
|
||||
canCollapseAllWidgets &&
|
||||
isAutoHeightEnabledForWidget(parentContainerLikeWidget)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function converts a standard object containing the properties to update
|
||||
* into the expected structure of { propertyPath: string, propertyValue: unknown }
|
||||
* @param originalObject The original object to mutate
|
||||
* @param widgetId The widgetId which will be the key in the object to mutate
|
||||
* @param propertiesToUpdate The properties which need to be added in the original object's widgetId key
|
||||
* @returns mutated object
|
||||
*/
|
||||
export function mutation_setPropertiesToUpdate(
|
||||
originalObject: Record<
|
||||
string,
|
||||
Array<{ propertyPath: string; propertyValue: unknown }>
|
||||
>,
|
||||
widgetId: string,
|
||||
propertiesToUpdate: Record<string, unknown>,
|
||||
) {
|
||||
if (!originalObject.hasOwnProperty(widgetId)) {
|
||||
originalObject[widgetId] = [];
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(propertiesToUpdate)) {
|
||||
originalObject[widgetId].push({
|
||||
propertyPath: key,
|
||||
propertyValue: value,
|
||||
});
|
||||
}
|
||||
|
||||
return originalObject;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { all, debounce, takeEvery, takeLatest } from "redux-saga/effects";
|
||||
import { batchCallsToUpdateWidgetAutoHeightSaga } from "./batcher";
|
||||
import {
|
||||
batchCallsToUpdateWidgetAutoHeightSaga,
|
||||
callEvalWithoutReplay,
|
||||
} from "./batcher";
|
||||
import { dynamicallyUpdateContainersSaga } from "./containers";
|
||||
import { generateTreeForAutoHeightComputations } from "./layoutTree";
|
||||
import { updateWidgetAutoHeightSaga } from "./widgets";
|
||||
|
|
@ -31,5 +34,9 @@ export default function* autoHeightSagas() {
|
|||
ReduxActionTypes.GENERATE_AUTO_HEIGHT_LAYOUT_TREE, // add, move, paste, cut, delete, undo/redo
|
||||
generateTreeForAutoHeightComputations,
|
||||
),
|
||||
takeLatest(
|
||||
ReduxActionTypes.UPDATE_MULTIPLE_WIDGET_PROPERTIES,
|
||||
callEvalWithoutReplay,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function* getLayoutTree(layoutUpdated: boolean) {
|
|||
getAutoHeightLayoutTree,
|
||||
);
|
||||
for (const canvasWidgetId in occupiedSpaces) {
|
||||
if (occupiedSpaces[canvasWidgetId].length > 0) {
|
||||
if (Object.keys(occupiedSpaces[canvasWidgetId]).length > 0) {
|
||||
const treeForThisCanvas = generateTree(
|
||||
occupiedSpaces[canvasWidgetId],
|
||||
!shouldCollapse && layoutUpdated,
|
||||
|
|
@ -39,7 +39,7 @@ export function* getLayoutTree(layoutUpdated: boolean) {
|
|||
}
|
||||
}
|
||||
log.debug(
|
||||
"Dynamic Height: Tree generation time taken:",
|
||||
"Auto Height: Tree generation time taken:",
|
||||
performance.now() - start,
|
||||
"ms",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ import {
|
|||
getAutoHeightUpdateQueue,
|
||||
resetAutoHeightUpdateQueue,
|
||||
} from "./batcher";
|
||||
import { getMinHeightBasedOnChildren, shouldWidgetsCollapse } from "./helpers";
|
||||
import {
|
||||
getChildOfContainerLikeWidget,
|
||||
getMinHeightBasedOnChildren,
|
||||
mutation_setPropertiesToUpdate,
|
||||
shouldCollapseThisWidget,
|
||||
} from "./helpers";
|
||||
import { updateMultipleWidgetPropertiesAction } from "actions/controlActions";
|
||||
import {
|
||||
generateAutoHeightLayoutTreeAction,
|
||||
|
|
@ -35,6 +40,7 @@ import {
|
|||
getCanvasLevelMap,
|
||||
} from "selectors/autoHeightSelectors";
|
||||
import { getLayoutTree } from "./layoutTree";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { TreeNode } from "utils/autoHeight/constants";
|
||||
import { directlyMutateDOMNodes } from "utils/autoHeight/mutateDOM";
|
||||
|
|
@ -71,8 +77,8 @@ export function* updateWidgetAutoHeightSaga(
|
|||
) {
|
||||
const start = performance.now();
|
||||
let shouldRecomputeContainers = false;
|
||||
let shouldEval = false;
|
||||
|
||||
const shouldCollapse: boolean = yield shouldWidgetsCollapse();
|
||||
const appMode: APP_MODE = yield select(getAppMode);
|
||||
|
||||
let updates = getAutoHeightUpdateQueue();
|
||||
|
|
@ -108,7 +114,7 @@ export function* updateWidgetAutoHeightSaga(
|
|||
log.debug("Auto Height: updates to process", { updates });
|
||||
|
||||
// Initialise all the widgets we will be updating
|
||||
const widgetsToUpdate: UpdateWidgetsPayload = {};
|
||||
let widgetsToUpdate: UpdateWidgetsPayload = {};
|
||||
|
||||
// Initialise all expected updates
|
||||
const expectedUpdates: Array<{
|
||||
|
|
@ -133,12 +139,44 @@ export function* updateWidgetAutoHeightSaga(
|
|||
getWidgetMinAutoHeight(widget) * GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
if (widget.type === "TABS_WIDGET") shouldRecomputeContainers = true;
|
||||
const config = WidgetFactory.widgetConfigMap.get(widget.type);
|
||||
if (config && config.needsHeightForContent) {
|
||||
shouldEval = true;
|
||||
}
|
||||
|
||||
// In case of a widget going invisible in view mode
|
||||
if (updates[widgetId] === 0) {
|
||||
if (shouldCollapse && isAutoHeightEnabledForWidget(widget)) {
|
||||
// Should we allow zero height for this widget?
|
||||
const shouldCollapse: boolean = yield shouldCollapseThisWidget(
|
||||
stateWidgets,
|
||||
widgetId,
|
||||
);
|
||||
|
||||
// If zero height is allowed
|
||||
if (shouldCollapse) {
|
||||
// setting the min to be 0, will take care of things with the same algorithm
|
||||
minDynamicHeightInPixels = 0;
|
||||
} else continue;
|
||||
// We also need a way to reset this widget if it is fixed, this is because,
|
||||
// for fixed widgets, auto height doesn't trigger, and there is a chance
|
||||
// that the widget will remain the same zero height even after they become
|
||||
// visible.
|
||||
// To do this, we're going to add some extra properties which we can later use to reset
|
||||
if (
|
||||
!isAutoHeightEnabledForWidget(widget) &&
|
||||
widget.topRow !== widget.bottomRow
|
||||
) {
|
||||
widgetsToUpdate = mutation_setPropertiesToUpdate(
|
||||
widgetsToUpdate,
|
||||
widgetId,
|
||||
{
|
||||
topRowBeforeCollapse: widget.topRow + 0,
|
||||
bottomRowBeforeCollapse: widget.bottomRow + 0,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const maxDynamicHeightInPixels =
|
||||
|
|
@ -184,20 +222,15 @@ export function* updateWidgetAutoHeightSaga(
|
|||
widgetsMeasuredInPixels.push(widgetId);
|
||||
|
||||
// Setting the height and dimensions of the Modal Widget
|
||||
widgetsToUpdate[widgetId] = [
|
||||
widgetsToUpdate = mutation_setPropertiesToUpdate(
|
||||
widgetsToUpdate,
|
||||
widgetId,
|
||||
{
|
||||
propertyPath: "height",
|
||||
propertyValue: newHeight,
|
||||
height: newHeight,
|
||||
bottomRow: widget.topRow + newHeight,
|
||||
topRow: widget.topRow,
|
||||
},
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: widget.topRow + newHeight,
|
||||
},
|
||||
{
|
||||
propertyPath: "topRow",
|
||||
propertyValue: widget.topRow,
|
||||
},
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,17 +305,14 @@ export function* updateWidgetAutoHeightSaga(
|
|||
// For each widget to update, add to the delta, the expected change.
|
||||
expectedUpdatesGroupedByParentCanvasWidget[
|
||||
parentCanvasWidgetId
|
||||
].forEach((update) => {
|
||||
delta[
|
||||
(update as {
|
||||
widgetId: string;
|
||||
expectedChangeInHeightInRows: number;
|
||||
}).widgetId
|
||||
] = (update as {
|
||||
].forEach(
|
||||
(update: {
|
||||
widgetId: string;
|
||||
expectedChangeInHeightInRows: number;
|
||||
}).expectedChangeInHeightInRows;
|
||||
});
|
||||
}) => {
|
||||
delta[update.widgetId] = update.expectedChangeInHeightInRows;
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -310,6 +340,18 @@ export function* updateWidgetAutoHeightSaga(
|
|||
const parentContainerLikeWidget: FlattenedWidgetProps =
|
||||
stateWidgets[parentCanvasWidget.parentId];
|
||||
|
||||
// Get the child we need to consider
|
||||
// For a container widget, it will be the child canvas
|
||||
// For a tabs widget, it will be the currently open tab's canvas
|
||||
const childWidgetId:
|
||||
| string
|
||||
| undefined = yield getChildOfContainerLikeWidget(
|
||||
parentContainerLikeWidget,
|
||||
);
|
||||
// Skip computations for the parent container like widget
|
||||
// if this child canvas is not the one currently visible
|
||||
if (childWidgetId !== parentCanvasWidget.widgetId) continue;
|
||||
|
||||
let minCanvasHeightInRows: number = yield getMinHeightBasedOnChildren(
|
||||
parentCanvasWidget.widgetId,
|
||||
changesSoFar,
|
||||
|
|
@ -364,19 +406,6 @@ export function* updateWidgetAutoHeightSaga(
|
|||
};
|
||||
}
|
||||
|
||||
// Convert this change into the standard expected update format.
|
||||
const expectedUpdate = {
|
||||
widgetId: parentContainerLikeWidget.widgetId,
|
||||
expectedHeightinPx:
|
||||
minHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
expectedChangeInHeightInRows:
|
||||
minHeightInRows - (layoutData.bottomRow - layoutData.topRow),
|
||||
currentTopRow: layoutData.topRow,
|
||||
currentBottomRow: layoutData.bottomRow,
|
||||
expectedBottomRow: layoutData.topRow + minHeightInRows,
|
||||
parentId: parentContainerLikeWidget.parentId,
|
||||
};
|
||||
|
||||
// If this widget is actually removed from the layout
|
||||
// For example, if this is a ModalWidget
|
||||
// We need to make sure that we change properties other than bottomRow and topRow
|
||||
|
|
@ -387,66 +416,105 @@ export function* updateWidgetAutoHeightSaga(
|
|||
);
|
||||
|
||||
// DRY this
|
||||
widgetsToUpdate[parentContainerLikeWidget.widgetId] = [
|
||||
widgetsToUpdate = mutation_setPropertiesToUpdate(
|
||||
widgetsToUpdate,
|
||||
parentContainerLikeWidget.widgetId,
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: minHeightInRows,
|
||||
bottomRow: minHeightInRows,
|
||||
height:
|
||||
minHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
minHeight:
|
||||
minHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
},
|
||||
{
|
||||
propertyPath: "height",
|
||||
propertyValue:
|
||||
(minHeightInRows + GridDefaults.CANVAS_EXTENSION_OFFSET) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
},
|
||||
{
|
||||
propertyPath: "minHeight",
|
||||
propertyValue:
|
||||
(minHeightInRows + GridDefaults.CANVAS_EXTENSION_OFFSET) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
},
|
||||
];
|
||||
);
|
||||
}
|
||||
|
||||
// If this is not a widget which is outside of the layout,
|
||||
// We must check if it has a parent
|
||||
// It most likely will, as this widget cannot be the MainContainer
|
||||
// The maincontainer is a Canvas Widget, not a container like widget.
|
||||
if (
|
||||
!parentContainerLikeWidget.detachFromLayout &&
|
||||
parentContainerLikeWidget.parentId
|
||||
) {
|
||||
// If this widget's parent canvas already has some updates
|
||||
// We push this update to the existing array.
|
||||
// DRY THIS
|
||||
if (
|
||||
expectedUpdatesGroupedByParentCanvasWidget.hasOwnProperty(
|
||||
parentContainerLikeWidget.parentId,
|
||||
)
|
||||
) {
|
||||
expectedUpdatesGroupedByParentCanvasWidget[
|
||||
parentContainerLikeWidget.parentId
|
||||
].push(expectedUpdate);
|
||||
} else {
|
||||
// Otherwise, we add a new entry.
|
||||
expectedUpdatesGroupedByParentCanvasWidget[
|
||||
parentContainerLikeWidget.parentId
|
||||
] = [expectedUpdate];
|
||||
}
|
||||
// If the parent container is trying to collapse already
|
||||
// Then the changes in the child should not effect the parent
|
||||
// For this we need to check for two different scenarios
|
||||
// 1. The parent is collapsing in this computation cycle
|
||||
// 2. The parent is already collapsed and should stay collapsed
|
||||
|
||||
// The parent might not have been added to the previously created group
|
||||
// parentCanvasWidgetGroupedByLevel
|
||||
const _level =
|
||||
canvasLevelMap[parentContainerLikeWidget.parentId];
|
||||
// So, we add it, if it is not the MainContainer.
|
||||
// This way it will be used in parentCanvasWidgetsToConsider
|
||||
// MainContainer was added when we initialised this variable,
|
||||
// so we're skipping it. level === 0 is true only for the MainContainer.
|
||||
if (_level !== 0) {
|
||||
// Get the parent from existing updates in this computation
|
||||
// cycle.
|
||||
const existingUpdate = expectedUpdates.find(
|
||||
(update) =>
|
||||
update.widgetId === parentContainerLikeWidget.widgetId,
|
||||
);
|
||||
|
||||
// Check if the parent has collapsed previously
|
||||
// And it needs to stay collapsed
|
||||
const shouldCollapseParent =
|
||||
shouldCollapseThisWidget(
|
||||
stateWidgets,
|
||||
parentContainerLikeWidget.widgetId,
|
||||
) &&
|
||||
parentContainerLikeWidget.topRow ===
|
||||
parentContainerLikeWidget.bottomRow &&
|
||||
!existingUpdate;
|
||||
|
||||
// If both the above conditions are false
|
||||
// Then update the expected updates for further
|
||||
// computations
|
||||
if (
|
||||
(existingUpdate === undefined ||
|
||||
existingUpdate.expectedHeightinPx !== 0) &&
|
||||
!shouldCollapseParent
|
||||
) {
|
||||
// Convert this change into the standard expected update format.
|
||||
const expectedUpdate = {
|
||||
widgetId: parentContainerLikeWidget.widgetId,
|
||||
expectedHeightinPx:
|
||||
minHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
expectedChangeInHeightInRows:
|
||||
minHeightInRows -
|
||||
(layoutData.bottomRow - layoutData.topRow),
|
||||
currentTopRow: layoutData.topRow,
|
||||
currentBottomRow: layoutData.bottomRow,
|
||||
expectedBottomRow: layoutData.topRow + minHeightInRows,
|
||||
parentId: parentContainerLikeWidget.parentId,
|
||||
};
|
||||
// If this is not a widget which is outside of the layout,
|
||||
// We must check if it has a parent
|
||||
// It most likely will, as this widget cannot be the MainContainer
|
||||
// The maincontainer is a Canvas Widget, not a container like widget.
|
||||
if (
|
||||
!parentContainerLikeWidget.detachFromLayout &&
|
||||
parentContainerLikeWidget.parentId
|
||||
) {
|
||||
// If this widget's parent canvas already has some updates
|
||||
// We push this update to the existing array.
|
||||
// DRY THIS
|
||||
parentCanvasWidgetsGroupedByLevel[_level] = uniq([
|
||||
...(parentCanvasWidgetsGroupedByLevel[_level] || []),
|
||||
parentContainerLikeWidget.parentId,
|
||||
]);
|
||||
if (
|
||||
expectedUpdatesGroupedByParentCanvasWidget.hasOwnProperty(
|
||||
parentContainerLikeWidget.parentId,
|
||||
)
|
||||
) {
|
||||
expectedUpdatesGroupedByParentCanvasWidget[
|
||||
parentContainerLikeWidget.parentId
|
||||
].push(expectedUpdate);
|
||||
} else {
|
||||
// Otherwise, we add a new entry.
|
||||
expectedUpdatesGroupedByParentCanvasWidget[
|
||||
parentContainerLikeWidget.parentId
|
||||
] = [expectedUpdate];
|
||||
}
|
||||
|
||||
// The parent might not have been added to the previously created group
|
||||
// parentCanvasWidgetGroupedByLevel
|
||||
const _level =
|
||||
canvasLevelMap[parentContainerLikeWidget.parentId];
|
||||
// So, we add it, if it is not the MainContainer.
|
||||
// This way it will be used in parentCanvasWidgetsToConsider
|
||||
// MainContainer was added when we initialised this variable,
|
||||
// so we're skipping it. level === 0 is true only for the MainContainer.
|
||||
if (_level !== 0) {
|
||||
// DRY THIS
|
||||
parentCanvasWidgetsGroupedByLevel[_level] = uniq([
|
||||
...(parentCanvasWidgetsGroupedByLevel[_level] || []),
|
||||
parentContainerLikeWidget.parentId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -482,13 +550,13 @@ export function* updateWidgetAutoHeightSaga(
|
|||
widgetsMeasuredInPixels.push(MAIN_CONTAINER_WIDGET_ID);
|
||||
|
||||
// Add the MainContainer's update.
|
||||
widgetsToUpdate[MAIN_CONTAINER_WIDGET_ID] = [
|
||||
widgetsToUpdate = mutation_setPropertiesToUpdate(
|
||||
widgetsToUpdate,
|
||||
MAIN_CONTAINER_WIDGET_ID,
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue:
|
||||
maxCanvasHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
bottomRow: maxCanvasHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
// Convert the changesSoFar (this are the computed changes)
|
||||
// To the widgetsToUpdate data structure for final reducer update.
|
||||
|
|
@ -498,33 +566,23 @@ export function* updateWidgetAutoHeightSaga(
|
|||
changedWidgetId
|
||||
];
|
||||
|
||||
if (!action?.payload) {
|
||||
const canvasOffset = getCanvasHeightOffset(
|
||||
stateWidgets[changedWidgetId].type,
|
||||
stateWidgets[changedWidgetId],
|
||||
);
|
||||
const canvasOffset = getCanvasHeightOffset(
|
||||
stateWidgets[changedWidgetId].type,
|
||||
stateWidgets[changedWidgetId],
|
||||
);
|
||||
|
||||
widgetCanvasOffsets[changedWidgetId] = canvasOffset;
|
||||
}
|
||||
widgetCanvasOffsets[changedWidgetId] = canvasOffset;
|
||||
|
||||
widgetsToUpdate[changedWidgetId] = [
|
||||
widgetsToUpdate = mutation_setPropertiesToUpdate(
|
||||
widgetsToUpdate,
|
||||
changedWidgetId,
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: changesSoFar[changedWidgetId].bottomRow,
|
||||
bottomRow: changesSoFar[changedWidgetId].bottomRow,
|
||||
topRow: changesSoFar[changedWidgetId].topRow,
|
||||
originalTopRow: originalTopRow,
|
||||
originalBottomRow: originalBottomRow,
|
||||
},
|
||||
{
|
||||
propertyPath: "topRow",
|
||||
propertyValue: changesSoFar[changedWidgetId].topRow,
|
||||
},
|
||||
{
|
||||
propertyPath: "originalTopRow",
|
||||
propertyValue: originalTopRow,
|
||||
},
|
||||
{
|
||||
propertyPath: "originalBottomRow",
|
||||
propertyValue: originalBottomRow,
|
||||
},
|
||||
];
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -535,7 +593,9 @@ export function* updateWidgetAutoHeightSaga(
|
|||
// Push all updates to the CanvasWidgetsReducer.
|
||||
// Note that we're not calling `UPDATE_LAYOUT`
|
||||
// as we don't need to trigger an eval
|
||||
yield put(updateMultipleWidgetPropertiesAction(widgetsToUpdate));
|
||||
yield put(
|
||||
updateMultipleWidgetPropertiesAction(widgetsToUpdate, shouldEval),
|
||||
);
|
||||
resetAutoHeightUpdateQueue();
|
||||
yield put(
|
||||
generateAutoHeightLayoutTreeAction(
|
||||
|
|
|
|||
|
|
@ -482,14 +482,16 @@ export const getOccupiedSpacesGroupedByParentCanvas = createSelector(
|
|||
widgets: CanvasWidgetsReduxState,
|
||||
): {
|
||||
occupiedSpaces: {
|
||||
[parentCanvasWidgetId: string]: Array<
|
||||
[parentCanvasWidgetId: string]: Record<
|
||||
string,
|
||||
OccupiedSpace & { originalTop: number; originalBottom: number }
|
||||
>;
|
||||
};
|
||||
canvasLevelMap: Record<string, number>;
|
||||
} => {
|
||||
const occupiedSpaces: {
|
||||
[parentCanvasWidgetId: string]: Array<
|
||||
[parentCanvasWidgetId: string]: Record<
|
||||
string,
|
||||
OccupiedSpace & { originalTop: number; originalBottom: number }
|
||||
>;
|
||||
} = {};
|
||||
|
|
@ -521,7 +523,7 @@ export const getOccupiedSpacesGroupedByParentCanvas = createSelector(
|
|||
}
|
||||
canvasLevelMap[canvasWidget.widgetId] = level;
|
||||
// Initialise the occupied spaces with an empty array
|
||||
occupiedSpaces[canvasWidgetId] = [];
|
||||
occupiedSpaces[canvasWidgetId] = {};
|
||||
// If this canvas widget has children
|
||||
if (canvasWidget.children && canvasWidget.children.length > 0) {
|
||||
// Iterate through all children
|
||||
|
|
@ -533,7 +535,7 @@ export const getOccupiedSpacesGroupedByParentCanvas = createSelector(
|
|||
// (unlike a modal widget or another canvas widget)
|
||||
if (!widget.detachFromLayout) {
|
||||
// Add the occupied space co-ordinates to the initialised array
|
||||
occupiedSpaces[canvasWidgetId].push({
|
||||
occupiedSpaces[canvasWidgetId][widget.widgetId] = {
|
||||
id: widget.widgetId,
|
||||
parentId: canvasWidgetId,
|
||||
left: widget.leftColumn,
|
||||
|
|
@ -542,7 +544,7 @@ export const getOccupiedSpacesGroupedByParentCanvas = createSelector(
|
|||
right: widget.rightColumn,
|
||||
originalTop: widget.originalTopRow,
|
||||
originalBottom: widget.originalBottomRow,
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ export const configureWidget = (config: WidgetConfiguration) => {
|
|||
iconSVG: config.iconSVG,
|
||||
isCanvas: config.isCanvas,
|
||||
canvasHeightOffset: config.canvasHeightOffset,
|
||||
needsHeightForContent: config.needsHeightForContent,
|
||||
};
|
||||
|
||||
const nonSerialisableWidgetConfigs: Record<string, unknown> = {};
|
||||
|
|
|
|||
|
|
@ -163,7 +163,12 @@ export function getCanvasBottomRow(
|
|||
|
||||
if (Array.isArray(children) && children.length > 0) {
|
||||
const bottomRow = children.reduce((prev, next) => {
|
||||
if (canvasWidgets[next].detachFromLayout) return prev;
|
||||
if (canvasWidgets[next].detachFromLayout) {
|
||||
return prev;
|
||||
}
|
||||
if (canvasWidgets[next].bottomRow === canvasWidgets[next].topRow) {
|
||||
return prev;
|
||||
}
|
||||
return canvasWidgets[next].bottomRow > prev
|
||||
? canvasWidgets[next].bottomRow
|
||||
: prev;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { generateTree } from "./generateTree";
|
|||
|
||||
describe("Generate Auto Height Layout tree", () => {
|
||||
it("Does not conflict when only one horizontal edge is the same", () => {
|
||||
const input: NodeSpace[] = [
|
||||
{ left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
{ left: 100, top: 0, bottom: 30, right: 120, id: "2" },
|
||||
];
|
||||
const input: Record<string, NodeSpace> = {
|
||||
"1": { left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
"2": { left: 100, top: 0, bottom: 30, right: 120, id: "2" },
|
||||
};
|
||||
const previousTree: Record<string, TreeNode> = {};
|
||||
const layoutUpdated = false;
|
||||
const expected = {
|
||||
|
|
@ -36,10 +36,10 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
});
|
||||
|
||||
it("Does conflict when part of the boxes overlap horizontally", () => {
|
||||
const input: NodeSpace[] = [
|
||||
{ left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
{ left: 80, top: 40, bottom: 80, right: 120, id: "2" },
|
||||
];
|
||||
const input: Record<string, NodeSpace> = {
|
||||
"1": { left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
"2": { left: 80, top: 40, bottom: 80, right: 120, id: "2" },
|
||||
};
|
||||
const previousTree: Record<string, TreeNode> = {};
|
||||
const layoutUpdated = false;
|
||||
const expected = {
|
||||
|
|
@ -69,10 +69,10 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
});
|
||||
|
||||
it("Uses existing originals if available in prevTree when layout hasn't updated", () => {
|
||||
const input: NodeSpace[] = [
|
||||
{ left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
{ left: 80, top: 30, bottom: 40, right: 120, id: "2" },
|
||||
];
|
||||
const input: Record<string, NodeSpace> = {
|
||||
"1": { left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
"2": { left: 80, top: 30, bottom: 40, right: 120, id: "2" },
|
||||
};
|
||||
const previousTree: Record<string, TreeNode> = {
|
||||
"1": {
|
||||
aboves: [],
|
||||
|
|
@ -121,10 +121,10 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
});
|
||||
|
||||
it("Ignores existing originals if available in prevTree when layout has updated", () => {
|
||||
const input: NodeSpace[] = [
|
||||
{ left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
{ left: 80, top: 30, bottom: 40, right: 120, id: "2" },
|
||||
];
|
||||
const input: Record<string, NodeSpace> = {
|
||||
"1": { left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
"2": { left: 80, top: 30, bottom: 40, right: 120, id: "2" },
|
||||
};
|
||||
const previousTree: Record<string, TreeNode> = {
|
||||
"1": {
|
||||
aboves: [],
|
||||
|
|
|
|||
|
|
@ -2,17 +2,21 @@ import { areIntersecting } from "utils/boxHelpers";
|
|||
import { pushToArray } from "utils/helpers";
|
||||
import { MAX_BOX_SIZE, NodeSpace, TreeNode } from "./constants";
|
||||
import { getNearestAbove } from "./helpers";
|
||||
|
||||
// This function uses the spaces occupied by sibling boxes and provides us with
|
||||
// a data structure which defines the relative vertical positioning of the boxes
|
||||
// in the form of "aboves" and "belows" for each box, which are array of box ids
|
||||
export function generateTree(
|
||||
spaces: NodeSpace[],
|
||||
spaces: Record<string, NodeSpace>,
|
||||
layoutUpdated: boolean,
|
||||
previousTree: Record<string, TreeNode>,
|
||||
): Record<string, TreeNode> {
|
||||
const spaceMap: Record<string, NodeSpace> = spaces;
|
||||
|
||||
const _spaces: string[] = Object.keys(spaceMap);
|
||||
// If widget doesn't exist in this DS, this means that its height changes does not effect any other sibling
|
||||
spaces.sort((a, b) => {
|
||||
_spaces.sort((A, B) => {
|
||||
const a: NodeSpace = spaceMap[A];
|
||||
const b: NodeSpace = spaceMap[B];
|
||||
//if both are of the same level and previous tree exists, check originalTops
|
||||
if (a.top === b.top && previousTree[a.id] && previousTree[b.id]) {
|
||||
return (
|
||||
|
|
@ -21,7 +25,6 @@ export function generateTree(
|
|||
}
|
||||
return a.top - b.top;
|
||||
}); // Sort based on position, top to bottom, so that we know which is above the other
|
||||
const _spaces = [...spaces];
|
||||
|
||||
const aboveMap: Record<string, string[]> = {};
|
||||
const belowMap: Record<string, string[]> = {};
|
||||
|
|
@ -29,18 +32,18 @@ export function generateTree(
|
|||
const tree: Record<string, TreeNode> = {};
|
||||
|
||||
// For each of the sibling boxes
|
||||
for (let i = 0; i < spaces.length; i++) {
|
||||
for (let i = 0; i < Object.keys(spaces).length; i++) {
|
||||
// Get the left most box in the array (Remember: we sorted from top to bottom, so the leftmost will be the top most)
|
||||
const _curr = _spaces.shift();
|
||||
if (_curr) {
|
||||
const _curr: string | undefined = _spaces.shift();
|
||||
if (_curr !== undefined) {
|
||||
// Create a reference copy as we need to override the bottom value
|
||||
const currentSpace = { ..._curr };
|
||||
const currentSpace = { ...spaceMap[_curr] };
|
||||
// Add a randomly large value to the bottom; this will help us know if any box is below this box
|
||||
currentSpace.bottom += MAX_BOX_SIZE;
|
||||
// For each of the remaining sibling widgets
|
||||
for (let j = 0; j < _spaces.length; j++) {
|
||||
// Create a reference copy as we need to override the bottom value
|
||||
const comparisionSpace = { ..._spaces[j] };
|
||||
const comparisionSpace = { ...spaceMap[_spaces[j]] };
|
||||
// Add a randomly large value to the bottom; this will help us know if any box is below this box
|
||||
// TODO(abhinav): This addition may not be necessary, as we're only looking to see if these boxes
|
||||
// are below the currentSpace
|
||||
|
|
@ -94,10 +97,12 @@ export function generateTree(
|
|||
// For each box, get the nearest above node
|
||||
// Then get the distance between this node and the nearest above
|
||||
// We'll try to maintain this distance when reflowing due to auto height
|
||||
// We also need to make sure that the nearest above doesn't go below 0, otherwise,
|
||||
// they can overlap.
|
||||
const nearestAbove = getNearestAbove(tree, boxId, {});
|
||||
if (nearestAbove.length > 0) {
|
||||
tree[boxId].distanceToNearestAbove =
|
||||
tree[boxId].topRow - tree[nearestAbove[0]].bottomRow;
|
||||
const distance = tree[boxId].topRow - tree[nearestAbove[0]].bottomRow;
|
||||
tree[boxId].distanceToNearestAbove = Math.max(distance, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,9 +53,14 @@ export function computeChangeInPositionBasedOnDelta(
|
|||
}
|
||||
|
||||
// Sort the effected box ids, this is to make sure we compute from top to bottom.
|
||||
const sortedEffectedBoxIds = effectedBoxes.sort(
|
||||
(a, b) => tree[a].topRow - tree[b].topRow,
|
||||
);
|
||||
const sortedEffectedBoxIds = effectedBoxes.sort((a, b) => {
|
||||
const A = tree[a].topRow;
|
||||
const B = tree[b].topRow;
|
||||
if (A === B) {
|
||||
return tree[a].originalTopRow - tree[b].originalTopRow;
|
||||
}
|
||||
return tree[a].topRow - tree[b].topRow;
|
||||
});
|
||||
|
||||
// For each of the boxes which have been effected
|
||||
for (const effectedBoxId of sortedEffectedBoxIds) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ const StyledContainerComponent = styled.div<
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
${(props) => (!!props.dropDisabled ? `position: relative;` : ``)}
|
||||
|
||||
${(props) => (props.shouldScrollContents ? scrollCSS : ``)}
|
||||
opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")};
|
||||
|
||||
|
|
@ -45,6 +47,7 @@ interface ContainerWrapperProps {
|
|||
backgroundColor?: string;
|
||||
widgetId: string;
|
||||
type: WidgetType;
|
||||
dropDisabled?: boolean;
|
||||
}
|
||||
function ContainerComponentWrapper(
|
||||
props: PropsWithChildren<ContainerWrapperProps>,
|
||||
|
|
@ -71,6 +74,7 @@ function ContainerComponentWrapper(
|
|||
className={`${
|
||||
props.shouldScrollContents ? getCanvasClassName() : ""
|
||||
} ${generateClassName(props.widgetId)} container-with-scrollbar`}
|
||||
dropDisabled={props.dropDisabled}
|
||||
onClickCapture={props.onClickCapture}
|
||||
ref={containerRef}
|
||||
resizeDisabled={props.resizeDisabled}
|
||||
|
|
@ -87,6 +91,7 @@ function ContainerComponent(props: ContainerComponentProps) {
|
|||
if (props.detachFromLayout) {
|
||||
return (
|
||||
<ContainerComponentWrapper
|
||||
dropDisabled={props.dropDisabled}
|
||||
onClickCapture={props.onClickCapture}
|
||||
resizeDisabled={props.resizeDisabled}
|
||||
shouldScrollContents={props.shouldScrollContents}
|
||||
|
|
@ -110,6 +115,7 @@ function ContainerComponent(props: ContainerComponentProps) {
|
|||
>
|
||||
<ContainerComponentWrapper
|
||||
backgroundColor={props.backgroundColor}
|
||||
dropDisabled={props.dropDisabled}
|
||||
onClickCapture={props.onClickCapture}
|
||||
resizeDisabled={props.resizeDisabled}
|
||||
shouldScrollContents={props.shouldScrollContents}
|
||||
|
|
@ -133,6 +139,7 @@ export interface ContainerComponentProps extends WidgetStyleContainerProps {
|
|||
backgroundColor?: string;
|
||||
type: WidgetType;
|
||||
noScroll?: boolean;
|
||||
dropDisabled?: boolean;
|
||||
}
|
||||
|
||||
export default ContainerComponent;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ButtonBoxShadowTypes } from "components/constants";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { WidgetHeightLimits } from "constants/WidgetConstants";
|
||||
import { GridDefaults, WidgetHeightLimits } from "constants/WidgetConstants";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import IconSVG from "./icon.svg";
|
||||
import Widget from "./widget";
|
||||
|
||||
|
|
@ -15,6 +16,17 @@ export const CONFIG = {
|
|||
active: true,
|
||||
},
|
||||
},
|
||||
canvasHeightOffset: (props: WidgetProps): number => {
|
||||
const offset =
|
||||
props.borderWidth && props.borderWidth > 1
|
||||
? Math.ceil(
|
||||
(2 * parseInt(props.borderWidth, 10) || 0) /
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
)
|
||||
: 0;
|
||||
|
||||
return offset;
|
||||
},
|
||||
searchTags: ["div", "parent", "group"],
|
||||
defaults: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox";
|
|||
import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena";
|
||||
import { getCanvasSnapRows } from "utils/WidgetPropsUtils";
|
||||
import { Stylesheet } from "entities/AppTheming";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
|
||||
class ContainerWidget extends BaseWidget<
|
||||
ContainerWidgetProps<WidgetProps>,
|
||||
|
|
@ -110,6 +111,7 @@ class ContainerWidget extends BaseWidget<
|
|||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.NUMBER },
|
||||
postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
},
|
||||
{
|
||||
propertyName: "borderRadius",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { ButtonVariantTypes, RecaptchaTypes } from "components/constants";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import IconSVG from "./icon.svg";
|
||||
import Widget from "./widget";
|
||||
|
||||
|
|
@ -15,6 +17,17 @@ export const CONFIG = {
|
|||
active: true,
|
||||
},
|
||||
},
|
||||
canvasHeightOffset: (props: WidgetProps): number => {
|
||||
const offset =
|
||||
props.borderWidth && props.borderWidth > 1
|
||||
? Math.round(
|
||||
(2 * parseInt(props.borderWidth, 10) || 0) /
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
)
|
||||
: 0;
|
||||
|
||||
return offset;
|
||||
},
|
||||
searchTags: ["group"],
|
||||
defaults: {
|
||||
rows: 40,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const CONFIG = {
|
|||
iconSVG: IconSVG,
|
||||
needsMeta: true,
|
||||
isCanvas: true,
|
||||
needsHeightForContent: true,
|
||||
defaults: {
|
||||
backgroundColor: "transparent",
|
||||
itemBackgroundColor: "#FFFFFF",
|
||||
|
|
|
|||
|
|
@ -274,8 +274,6 @@ export class ModalWidget extends BaseWidget<ModalWidgetProps, WidgetState> {
|
|||
getCanvasView() {
|
||||
let children = this.getChildren();
|
||||
children = this.makeModalSelectable(children);
|
||||
// children = this.showWidgetName(children, true);
|
||||
|
||||
return this.makeModalComponent(children, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { ButtonVariantTypes } from "components/constants";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
|
||||
import IconSVG from "./icon.svg";
|
||||
import Widget from "./widget";
|
||||
|
|
@ -16,6 +18,17 @@ export const CONFIG = {
|
|||
iconSVG: IconSVG,
|
||||
needsMeta: true,
|
||||
isCanvas: true,
|
||||
canvasHeightOffset: (props: WidgetProps): number => {
|
||||
const offset =
|
||||
props.borderWidth && props.borderWidth > 1
|
||||
? Math.ceil(
|
||||
(2 * parseInt(props.borderWidth, 10) || 0) /
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
)
|
||||
: 0;
|
||||
|
||||
return offset;
|
||||
},
|
||||
defaults: {
|
||||
rows: 14,
|
||||
columns: 22,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import ContainerWidget from "widgets/ContainerWidget";
|
|||
|
||||
import { ValidationTypes } from "constants/WidgetValidation";
|
||||
import { Stylesheet } from "entities/AppTheming";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
|
||||
class StatboxWidget extends ContainerWidget {
|
||||
static getPropertyPaneContentConfig() {
|
||||
|
|
@ -85,6 +86,7 @@ class StatboxWidget extends ContainerWidget {
|
|||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.NUMBER },
|
||||
postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
},
|
||||
{
|
||||
propertyName: "borderRadius",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const CONFIG = {
|
|||
needsMeta: true,
|
||||
searchTags: ["datagrid"],
|
||||
hideCard: true,
|
||||
needsHeightForContent: true,
|
||||
defaults: {
|
||||
rows: 28,
|
||||
columns: 34,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const CONFIG = {
|
|||
name: "Table",
|
||||
iconSVG: IconSVG,
|
||||
needsMeta: true,
|
||||
needsHeightForContent: true,
|
||||
defaults: {
|
||||
rows: 28,
|
||||
columns: 34,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Colors } from "constants/Colors";
|
||||
import { WidgetHeightLimits } from "constants/WidgetConstants";
|
||||
import { GridDefaults, WidgetHeightLimits } from "constants/WidgetConstants";
|
||||
import { WidgetProps } from "widgets/BaseWidget";
|
||||
import { BlueprintOperationTypes } from "widgets/constants";
|
||||
import IconSVG from "./icon.svg";
|
||||
|
|
@ -16,8 +16,20 @@ export const CONFIG = {
|
|||
// evaluations. One way to handle these types of properties is to
|
||||
// define them in a Map which the platform understands to have
|
||||
// them stored only in the WidgetFactory.
|
||||
canvasHeightOffset: (props: WidgetProps): number =>
|
||||
props.shouldShowTabs === true ? 4 : 0,
|
||||
canvasHeightOffset: (props: WidgetProps): number => {
|
||||
let offset =
|
||||
props.borderWidth && props.borderWidth > 1
|
||||
? Math.ceil(
|
||||
(2 * parseInt(props.borderWidth, 10) || 0) /
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
)
|
||||
: 0;
|
||||
|
||||
if (props.shouldShowTabs === true) {
|
||||
offset += 4;
|
||||
}
|
||||
return offset;
|
||||
},
|
||||
features: {
|
||||
dynamicHeight: {
|
||||
sectionIndex: 1,
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ class TabsWidget extends BaseWidget<
|
|||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: { type: ValidationTypes.NUMBER },
|
||||
postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
},
|
||||
{
|
||||
propertyName: "borderRadius",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export interface WidgetConfiguration {
|
|||
features?: WidgetFeatures;
|
||||
canvasHeightOffset?: (props: WidgetProps) => number;
|
||||
searchTags?: string[];
|
||||
needsHeightForContent?: boolean;
|
||||
properties: {
|
||||
config?: PropertyPaneConfig[];
|
||||
contentConfig?: PropertyPaneConfig[];
|
||||
|
|
@ -52,7 +53,12 @@ export interface DSLWidget extends WidgetProps {
|
|||
children?: DSLWidget[];
|
||||
}
|
||||
|
||||
const staticProps = omit(WIDGET_STATIC_PROPS, "children");
|
||||
const staticProps = omit(
|
||||
WIDGET_STATIC_PROPS,
|
||||
"children",
|
||||
"topRowBeforeCollapse",
|
||||
"bottomRowBeforeCollapse",
|
||||
);
|
||||
export type CanvasWidgetStructure = Pick<
|
||||
WidgetProps,
|
||||
keyof typeof staticProps
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
} from "utils/widgetRenderUtils";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { checkContainersForAutoHeightAction } from "actions/autoHeightActions";
|
||||
import { isAutoHeightEnabledForWidget } from "./WidgetUtils";
|
||||
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
|
||||
import { getGoogleMapsApiKey } from "ce/selectors/tenantSelectors";
|
||||
|
||||
|
|
@ -180,7 +181,32 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
|||
shouldResetCollapsedContainerHeightInViewOrPreviewMode ||
|
||||
shouldResetCollapsedContainerHeightInCanvasMode
|
||||
) {
|
||||
dispatch(checkContainersForAutoHeightAction());
|
||||
// We also need to check if a non-auto height widget has collapsed earlier
|
||||
// We can figure this out if the widget height is zero and the beforeCollapse
|
||||
// topRow and bottomRow are available.
|
||||
|
||||
// If the above is true, we call an auto height update call
|
||||
// so that the widget can be reset correctly.
|
||||
if (
|
||||
widgetProps.topRow === widgetProps.bottomRow &&
|
||||
widgetProps.topRowBeforeCollapse !== undefined &&
|
||||
widgetProps.bottomRowBeforeCollapse !== undefined &&
|
||||
!isAutoHeightEnabledForWidget(widgetProps)
|
||||
) {
|
||||
const heightBeforeCollapse =
|
||||
(widgetProps.bottomRowBeforeCollapse -
|
||||
widgetProps.topRowBeforeCollapse) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
dispatch({
|
||||
type: ReduxActionTypes.UPDATE_WIDGET_AUTO_HEIGHT,
|
||||
payload: {
|
||||
widgetId: props.widgetId,
|
||||
height: heightBeforeCollapse,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
dispatch(checkContainersForAutoHeightAction());
|
||||
}
|
||||
}
|
||||
|
||||
return <WrappedWidget {...widgetProps} />;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user